roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         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         isTouch =  (function() {
67             try {  
68                 document.createEvent("TouchEvent");  
69                 return true;  
70             } catch (e) {  
71                 return false;  
72             } 
73             
74         })();
75     // remove css image flicker
76         if(isIE && !isIE7){
77         try{
78             document.execCommand("BackgroundImageCache", false, true);
79         }catch(e){}
80     }
81     
82     Roo.apply(Roo, {
83         /**
84          * True if the browser is in strict mode
85          * @type Boolean
86          */
87         isStrict : isStrict,
88         /**
89          * True if the page is running over SSL
90          * @type Boolean
91          */
92         isSecure : isSecure,
93         /**
94          * True when the document is fully initialized and ready for action
95          * @type Boolean
96          */
97         isReady : false,
98         /**
99          * Turn on debugging output (currently only the factory uses this)
100          * @type Boolean
101          */
102         
103         debug: false,
104
105         /**
106          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
107          * @type Boolean
108          */
109         enableGarbageCollector : true,
110
111         /**
112          * True to automatically purge event listeners after uncaching an element (defaults to false).
113          * Note: this only happens if enableGarbageCollector is true.
114          * @type Boolean
115          */
116         enableListenerCollection:false,
117
118         /**
119          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
120          * the IE insecure content warning (defaults to javascript:false).
121          * @type String
122          */
123         SSL_SECURE_URL : "javascript:false",
124
125         /**
126          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
127          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
128          * @type String
129          */
130         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
131
132         emptyFn : function(){},
133         
134         /**
135          * Copies all the properties of config to obj if they don't already exist.
136          * @param {Object} obj The receiver of the properties
137          * @param {Object} config The source of the properties
138          * @return {Object} returns obj
139          */
140         applyIf : function(o, c){
141             if(o && c){
142                 for(var p in c){
143                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
144                 }
145             }
146             return o;
147         },
148
149         /**
150          * Applies event listeners to elements by selectors when the document is ready.
151          * The event name is specified with an @ suffix.
152 <pre><code>
153 Roo.addBehaviors({
154    // add a listener for click on all anchors in element with id foo
155    '#foo a@click' : function(e, t){
156        // do something
157    },
158
159    // add the same listener to multiple selectors (separated by comma BEFORE the @)
160    '#foo a, #bar span.some-class@mouseover' : function(){
161        // do something
162    }
163 });
164 </code></pre>
165          * @param {Object} obj The list of behaviors to apply
166          */
167         addBehaviors : function(o){
168             if(!Roo.isReady){
169                 Roo.onReady(function(){
170                     Roo.addBehaviors(o);
171                 });
172                 return;
173             }
174             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
175             for(var b in o){
176                 var parts = b.split('@');
177                 if(parts[1]){ // for Object prototype breakers
178                     var s = parts[0];
179                     if(!cache[s]){
180                         cache[s] = Roo.select(s);
181                     }
182                     cache[s].on(parts[1], o[b]);
183                 }
184             }
185             cache = null;
186         },
187
188         /**
189          * Generates unique ids. If the element already has an id, it is unchanged
190          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
191          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
192          * @return {String} The generated Id.
193          */
194         id : function(el, prefix){
195             prefix = prefix || "roo-gen";
196             el = Roo.getDom(el);
197             var id = prefix + (++idSeed);
198             return el ? (el.id ? el.id : (el.id = id)) : id;
199         },
200          
201        
202         /**
203          * Extends one class with another class and optionally overrides members with the passed literal. This class
204          * also adds the function "override()" to the class that can be used to override
205          * members on an instance.
206          * @param {Object} subclass The class inheriting the functionality
207          * @param {Object} superclass The class being extended
208          * @param {Object} overrides (optional) A literal with members
209          * @method extend
210          */
211         extend : function(){
212             // inline overrides
213             var io = function(o){
214                 for(var m in o){
215                     this[m] = o[m];
216                 }
217             };
218             return function(sb, sp, overrides){
219                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
220                     overrides = sp;
221                     sp = sb;
222                     sb = function(){sp.apply(this, arguments);};
223                 }
224                 var F = function(){}, sbp, spp = sp.prototype;
225                 F.prototype = spp;
226                 sbp = sb.prototype = new F();
227                 sbp.constructor=sb;
228                 sb.superclass=spp;
229                 
230                 if(spp.constructor == Object.prototype.constructor){
231                     spp.constructor=sp;
232                    
233                 }
234                 
235                 sb.override = function(o){
236                     Roo.override(sb, o);
237                 };
238                 sbp.override = io;
239                 Roo.override(sb, overrides);
240                 return sb;
241             };
242         }(),
243
244         /**
245          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
246          * Usage:<pre><code>
247 Roo.override(MyClass, {
248     newMethod1: function(){
249         // etc.
250     },
251     newMethod2: function(foo){
252         // etc.
253     }
254 });
255  </code></pre>
256          * @param {Object} origclass The class to override
257          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
258          * containing one or more methods.
259          * @method override
260          */
261         override : function(origclass, overrides){
262             if(overrides){
263                 var p = origclass.prototype;
264                 for(var method in overrides){
265                     p[method] = overrides[method];
266                 }
267             }
268         },
269         /**
270          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
271          * <pre><code>
272 Roo.namespace('Company', 'Company.data');
273 Company.Widget = function() { ... }
274 Company.data.CustomStore = function(config) { ... }
275 </code></pre>
276          * @param {String} namespace1
277          * @param {String} namespace2
278          * @param {String} etc
279          * @method namespace
280          */
281         namespace : function(){
282             var a=arguments, o=null, i, j, d, rt;
283             for (i=0; i<a.length; ++i) {
284                 d=a[i].split(".");
285                 rt = d[0];
286                 /** eval:var:o */
287                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
288                 for (j=1; j<d.length; ++j) {
289                     o[d[j]]=o[d[j]] || {};
290                     o=o[d[j]];
291                 }
292             }
293         },
294         /**
295          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
296          * <pre><code>
297 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
298 Roo.factory(conf, Roo.data);
299 </code></pre>
300          * @param {String} classname
301          * @param {String} namespace (optional)
302          * @method factory
303          */
304          
305         factory : function(c, ns)
306         {
307             // no xtype, no ns or c.xns - or forced off by c.xns
308             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
309                 return c;
310             }
311             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
312             if (c.constructor == ns[c.xtype]) {// already created...
313                 return c;
314             }
315             if (ns[c.xtype]) {
316                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
317                 var ret = new ns[c.xtype](c);
318                 ret.xns = false;
319                 return ret;
320             }
321             c.xns = false; // prevent recursion..
322             return c;
323         },
324          /**
325          * Logs to console if it can.
326          *
327          * @param {String|Object} string
328          * @method log
329          */
330         log : function(s)
331         {
332             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
333                 return; // alerT?
334             }
335             console.log(s);
336             
337         },
338         /**
339          * 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.
340          * @param {Object} o
341          * @return {String}
342          */
343         urlEncode : function(o){
344             if(!o){
345                 return "";
346             }
347             var buf = [];
348             for(var key in o){
349                 var ov = o[key], k = Roo.encodeURIComponent(key);
350                 var type = typeof ov;
351                 if(type == 'undefined'){
352                     buf.push(k, "=&");
353                 }else if(type != "function" && type != "object"){
354                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
355                 }else if(ov instanceof Array){
356                     if (ov.length) {
357                             for(var i = 0, len = ov.length; i < len; i++) {
358                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
359                             }
360                         } else {
361                             buf.push(k, "=&");
362                         }
363                 }
364             }
365             buf.pop();
366             return buf.join("");
367         },
368          /**
369          * Safe version of encodeURIComponent
370          * @param {String} data 
371          * @return {String} 
372          */
373         
374         encodeURIComponent : function (data)
375         {
376             try {
377                 return encodeURIComponent(data);
378             } catch(e) {} // should be an uri encode error.
379             
380             if (data == '' || data == null){
381                return '';
382             }
383             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
384             function nibble_to_hex(nibble){
385                 var chars = '0123456789ABCDEF';
386                 return chars.charAt(nibble);
387             }
388             data = data.toString();
389             var buffer = '';
390             for(var i=0; i<data.length; i++){
391                 var c = data.charCodeAt(i);
392                 var bs = new Array();
393                 if (c > 0x10000){
394                         // 4 bytes
395                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
396                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
397                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
398                     bs[3] = 0x80 | (c & 0x3F);
399                 }else if (c > 0x800){
400                          // 3 bytes
401                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
402                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
403                     bs[2] = 0x80 | (c & 0x3F);
404                 }else if (c > 0x80){
405                        // 2 bytes
406                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
407                     bs[1] = 0x80 | (c & 0x3F);
408                 }else{
409                         // 1 byte
410                     bs[0] = c;
411                 }
412                 for(var j=0; j<bs.length; j++){
413                     var b = bs[j];
414                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
415                             + nibble_to_hex(b &0x0F);
416                     buffer += '%'+hex;
417                }
418             }
419             return buffer;    
420              
421         },
422
423         /**
424          * 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]}.
425          * @param {String} string
426          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
427          * @return {Object} A literal with members
428          */
429         urlDecode : function(string, overwrite){
430             if(!string || !string.length){
431                 return {};
432             }
433             var obj = {};
434             var pairs = string.split('&');
435             var pair, name, value;
436             for(var i = 0, len = pairs.length; i < len; i++){
437                 pair = pairs[i].split('=');
438                 name = decodeURIComponent(pair[0]);
439                 value = decodeURIComponent(pair[1]);
440                 if(overwrite !== true){
441                     if(typeof obj[name] == "undefined"){
442                         obj[name] = value;
443                     }else if(typeof obj[name] == "string"){
444                         obj[name] = [obj[name]];
445                         obj[name].push(value);
446                     }else{
447                         obj[name].push(value);
448                     }
449                 }else{
450                     obj[name] = value;
451                 }
452             }
453             return obj;
454         },
455
456         /**
457          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
458          * passed array is not really an array, your function is called once with it.
459          * The supplied function is called with (Object item, Number index, Array allItems).
460          * @param {Array/NodeList/Mixed} array
461          * @param {Function} fn
462          * @param {Object} scope
463          */
464         each : function(array, fn, scope){
465             if(typeof array.length == "undefined" || typeof array == "string"){
466                 array = [array];
467             }
468             for(var i = 0, len = array.length; i < len; i++){
469                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
470             }
471         },
472
473         // deprecated
474         combine : function(){
475             var as = arguments, l = as.length, r = [];
476             for(var i = 0; i < l; i++){
477                 var a = as[i];
478                 if(a instanceof Array){
479                     r = r.concat(a);
480                 }else if(a.length !== undefined && !a.substr){
481                     r = r.concat(Array.prototype.slice.call(a, 0));
482                 }else{
483                     r.push(a);
484                 }
485             }
486             return r;
487         },
488
489         /**
490          * Escapes the passed string for use in a regular expression
491          * @param {String} str
492          * @return {String}
493          */
494         escapeRe : function(s) {
495             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
496         },
497
498         // internal
499         callback : function(cb, scope, args, delay){
500             if(typeof cb == "function"){
501                 if(delay){
502                     cb.defer(delay, scope, args || []);
503                 }else{
504                     cb.apply(scope, args || []);
505                 }
506             }
507         },
508
509         /**
510          * Return the dom node for the passed string (id), dom node, or Roo.Element
511          * @param {String/HTMLElement/Roo.Element} el
512          * @return HTMLElement
513          */
514         getDom : function(el){
515             if(!el){
516                 return null;
517             }
518             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
519         },
520
521         /**
522         * Shorthand for {@link Roo.ComponentMgr#get}
523         * @param {String} id
524         * @return Roo.Component
525         */
526         getCmp : function(id){
527             return Roo.ComponentMgr.get(id);
528         },
529          
530         num : function(v, defaultValue){
531             if(typeof v != 'number'){
532                 return defaultValue;
533             }
534             return v;
535         },
536
537         destroy : function(){
538             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
539                 var as = a[i];
540                 if(as){
541                     if(as.dom){
542                         as.removeAllListeners();
543                         as.remove();
544                         continue;
545                     }
546                     if(typeof as.purgeListeners == 'function'){
547                         as.purgeListeners();
548                     }
549                     if(typeof as.destroy == 'function'){
550                         as.destroy();
551                     }
552                 }
553             }
554         },
555
556         // inpired by a similar function in mootools library
557         /**
558          * Returns the type of object that is passed in. If the object passed in is null or undefined it
559          * return false otherwise it returns one of the following values:<ul>
560          * <li><b>string</b>: If the object passed is a string</li>
561          * <li><b>number</b>: If the object passed is a number</li>
562          * <li><b>boolean</b>: If the object passed is a boolean value</li>
563          * <li><b>function</b>: If the object passed is a function reference</li>
564          * <li><b>object</b>: If the object passed is an object</li>
565          * <li><b>array</b>: If the object passed is an array</li>
566          * <li><b>regexp</b>: If the object passed is a regular expression</li>
567          * <li><b>element</b>: If the object passed is a DOM Element</li>
568          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
569          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
570          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
571          * @param {Mixed} object
572          * @return {String}
573          */
574         type : function(o){
575             if(o === undefined || o === null){
576                 return false;
577             }
578             if(o.htmlElement){
579                 return 'element';
580             }
581             var t = typeof o;
582             if(t == 'object' && o.nodeName) {
583                 switch(o.nodeType) {
584                     case 1: return 'element';
585                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
586                 }
587             }
588             if(t == 'object' || t == 'function') {
589                 switch(o.constructor) {
590                     case Array: return 'array';
591                     case RegExp: return 'regexp';
592                 }
593                 if(typeof o.length == 'number' && typeof o.item == 'function') {
594                     return 'nodelist';
595                 }
596             }
597             return t;
598         },
599
600         /**
601          * Returns true if the passed value is null, undefined or an empty string (optional).
602          * @param {Mixed} value The value to test
603          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
604          * @return {Boolean}
605          */
606         isEmpty : function(v, allowBlank){
607             return v === null || v === undefined || (!allowBlank ? v === '' : false);
608         },
609         
610         /** @type Boolean */
611         isOpera : isOpera,
612         /** @type Boolean */
613         isSafari : isSafari,
614         /** @type Boolean */
615         isFirefox : isFirefox,
616         /** @type Boolean */
617         isIE : isIE,
618         /** @type Boolean */
619         isIE7 : isIE7,
620         /** @type Boolean */
621         isIE11 : isIE11,
622         /** @type Boolean */
623         isGecko : isGecko,
624         /** @type Boolean */
625         isBorderBox : isBorderBox,
626         /** @type Boolean */
627         isWindows : isWindows,
628         /** @type Boolean */
629         isLinux : isLinux,
630         /** @type Boolean */
631         isMac : isMac,
632         /** @type Boolean */
633         isTouch : isTouch,
634
635         /**
636          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
637          * you may want to set this to true.
638          * @type Boolean
639          */
640         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
641         
642         
643                 
644         /**
645          * Selects a single element as a Roo Element
646          * This is about as close as you can get to jQuery's $('do crazy stuff')
647          * @param {String} selector The selector/xpath query
648          * @param {Node} root (optional) The start of the query (defaults to document).
649          * @return {Roo.Element}
650          */
651         selectNode : function(selector, root) 
652         {
653             var node = Roo.DomQuery.selectNode(selector,root);
654             return node ? Roo.get(node) : new Roo.Element(false);
655         }
656         
657     });
658
659
660 })();
661
662 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
663                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
664                 "Roo.app", "Roo.ux",
665                 "Roo.bootstrap",
666                 "Roo.bootstrap.dash");
667 /*
668  * Based on:
669  * Ext JS Library 1.1.1
670  * Copyright(c) 2006-2007, Ext JS, LLC.
671  *
672  * Originally Released Under LGPL - original licence link has changed is not relivant.
673  *
674  * Fork - LGPL
675  * <script type="text/javascript">
676  */
677
678 (function() {    
679     // wrappedn so fnCleanup is not in global scope...
680     if(Roo.isIE) {
681         function fnCleanUp() {
682             var p = Function.prototype;
683             delete p.createSequence;
684             delete p.defer;
685             delete p.createDelegate;
686             delete p.createCallback;
687             delete p.createInterceptor;
688
689             window.detachEvent("onunload", fnCleanUp);
690         }
691         window.attachEvent("onunload", fnCleanUp);
692     }
693 })();
694
695
696 /**
697  * @class Function
698  * These functions are available on every Function object (any JavaScript function).
699  */
700 Roo.apply(Function.prototype, {
701      /**
702      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
703      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
704      * Will create a function that is bound to those 2 args.
705      * @return {Function} The new function
706     */
707     createCallback : function(/*args...*/){
708         // make args available, in function below
709         var args = arguments;
710         var method = this;
711         return function() {
712             return method.apply(window, args);
713         };
714     },
715
716     /**
717      * Creates a delegate (callback) that sets the scope to obj.
718      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
719      * Will create a function that is automatically scoped to this.
720      * @param {Object} obj (optional) The object for which the scope is set
721      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
722      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
723      *                                             if a number the args are inserted at the specified position
724      * @return {Function} The new function
725      */
726     createDelegate : function(obj, args, appendArgs){
727         var method = this;
728         return function() {
729             var callArgs = args || arguments;
730             if(appendArgs === true){
731                 callArgs = Array.prototype.slice.call(arguments, 0);
732                 callArgs = callArgs.concat(args);
733             }else if(typeof appendArgs == "number"){
734                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
735                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
736                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
737             }
738             return method.apply(obj || window, callArgs);
739         };
740     },
741
742     /**
743      * Calls this function after the number of millseconds specified.
744      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
745      * @param {Object} obj (optional) The object for which the scope is set
746      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
747      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
748      *                                             if a number the args are inserted at the specified position
749      * @return {Number} The timeout id that can be used with clearTimeout
750      */
751     defer : function(millis, obj, args, appendArgs){
752         var fn = this.createDelegate(obj, args, appendArgs);
753         if(millis){
754             return setTimeout(fn, millis);
755         }
756         fn();
757         return 0;
758     },
759     /**
760      * Create a combined function call sequence of the original function + the passed function.
761      * The resulting function returns the results of the original function.
762      * The passed fcn is called with the parameters of the original function
763      * @param {Function} fcn The function to sequence
764      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
765      * @return {Function} The new function
766      */
767     createSequence : function(fcn, scope){
768         if(typeof fcn != "function"){
769             return this;
770         }
771         var method = this;
772         return function() {
773             var retval = method.apply(this || window, arguments);
774             fcn.apply(scope || this || window, arguments);
775             return retval;
776         };
777     },
778
779     /**
780      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
781      * The resulting function returns the results of the original function.
782      * The passed fcn is called with the parameters of the original function.
783      * @addon
784      * @param {Function} fcn The function to call before the original
785      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
786      * @return {Function} The new function
787      */
788     createInterceptor : function(fcn, scope){
789         if(typeof fcn != "function"){
790             return this;
791         }
792         var method = this;
793         return function() {
794             fcn.target = this;
795             fcn.method = method;
796             if(fcn.apply(scope || this || window, arguments) === false){
797                 return;
798             }
799             return method.apply(this || window, arguments);
800         };
801     }
802 });
803 /*
804  * Based on:
805  * Ext JS Library 1.1.1
806  * Copyright(c) 2006-2007, Ext JS, LLC.
807  *
808  * Originally Released Under LGPL - original licence link has changed is not relivant.
809  *
810  * Fork - LGPL
811  * <script type="text/javascript">
812  */
813
814 Roo.applyIf(String, {
815     
816     /** @scope String */
817     
818     /**
819      * Escapes the passed string for ' and \
820      * @param {String} string The string to escape
821      * @return {String} The escaped string
822      * @static
823      */
824     escape : function(string) {
825         return string.replace(/('|\\)/g, "\\$1");
826     },
827
828     /**
829      * Pads the left side of a string with a specified character.  This is especially useful
830      * for normalizing number and date strings.  Example usage:
831      * <pre><code>
832 var s = String.leftPad('123', 5, '0');
833 // s now contains the string: '00123'
834 </code></pre>
835      * @param {String} string The original string
836      * @param {Number} size The total length of the output string
837      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
838      * @return {String} The padded string
839      * @static
840      */
841     leftPad : function (val, size, ch) {
842         var result = new String(val);
843         if(ch === null || ch === undefined || ch === '') {
844             ch = " ";
845         }
846         while (result.length < size) {
847             result = ch + result;
848         }
849         return result;
850     },
851
852     /**
853      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
854      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
855      * <pre><code>
856 var cls = 'my-class', text = 'Some text';
857 var s = String.format('<div class="{0}">{1}</div>', cls, text);
858 // s now contains the string: '<div class="my-class">Some text</div>'
859 </code></pre>
860      * @param {String} string The tokenized string to be formatted
861      * @param {String} value1 The value to replace token {0}
862      * @param {String} value2 Etc...
863      * @return {String} The formatted string
864      * @static
865      */
866     format : function(format){
867         var args = Array.prototype.slice.call(arguments, 1);
868         return format.replace(/\{(\d+)\}/g, function(m, i){
869             return Roo.util.Format.htmlEncode(args[i]);
870         });
871     }
872 });
873
874 /**
875  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
876  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
877  * they are already different, the first value passed in is returned.  Note that this method returns the new value
878  * but does not change the current string.
879  * <pre><code>
880 // alternate sort directions
881 sort = sort.toggle('ASC', 'DESC');
882
883 // instead of conditional logic:
884 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
885 </code></pre>
886  * @param {String} value The value to compare to the current string
887  * @param {String} other The new value to use if the string already equals the first value passed in
888  * @return {String} The new value
889  */
890  
891 String.prototype.toggle = function(value, other){
892     return this == value ? other : value;
893 };/*
894  * Based on:
895  * Ext JS Library 1.1.1
896  * Copyright(c) 2006-2007, Ext JS, LLC.
897  *
898  * Originally Released Under LGPL - original licence link has changed is not relivant.
899  *
900  * Fork - LGPL
901  * <script type="text/javascript">
902  */
903
904  /**
905  * @class Number
906  */
907 Roo.applyIf(Number.prototype, {
908     /**
909      * Checks whether or not the current number is within a desired range.  If the number is already within the
910      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
911      * exceeded.  Note that this method returns the constrained value but does not change the current number.
912      * @param {Number} min The minimum number in the range
913      * @param {Number} max The maximum number in the range
914      * @return {Number} The constrained value if outside the range, otherwise the current value
915      */
916     constrain : function(min, max){
917         return Math.min(Math.max(this, min), max);
918     }
919 });/*
920  * Based on:
921  * Ext JS Library 1.1.1
922  * Copyright(c) 2006-2007, Ext JS, LLC.
923  *
924  * Originally Released Under LGPL - original licence link has changed is not relivant.
925  *
926  * Fork - LGPL
927  * <script type="text/javascript">
928  */
929  /**
930  * @class Array
931  */
932 Roo.applyIf(Array.prototype, {
933     /**
934      * 
935      * Checks whether or not the specified object exists in the array.
936      * @param {Object} o The object to check for
937      * @return {Number} The index of o in the array (or -1 if it is not found)
938      */
939     indexOf : function(o){
940        for (var i = 0, len = this.length; i < len; i++){
941               if(this[i] == o) return i;
942        }
943            return -1;
944     },
945
946     /**
947      * Removes the specified object from the array.  If the object is not found nothing happens.
948      * @param {Object} o The object to remove
949      */
950     remove : function(o){
951        var index = this.indexOf(o);
952        if(index != -1){
953            this.splice(index, 1);
954        }
955     },
956     /**
957      * Map (JS 1.6 compatibility)
958      * @param {Function} function  to call
959      */
960     map : function(fun )
961     {
962         var len = this.length >>> 0;
963         if (typeof fun != "function")
964             throw new TypeError();
965
966         var res = new Array(len);
967         var thisp = arguments[1];
968         for (var i = 0; i < len; i++)
969         {
970             if (i in this)
971                 res[i] = fun.call(thisp, this[i], i, this);
972         }
973
974         return res;
975     }
976     
977 });
978
979
980  /*
981  * Based on:
982  * Ext JS Library 1.1.1
983  * Copyright(c) 2006-2007, Ext JS, LLC.
984  *
985  * Originally Released Under LGPL - original licence link has changed is not relivant.
986  *
987  * Fork - LGPL
988  * <script type="text/javascript">
989  */
990
991 /**
992  * @class Date
993  *
994  * The date parsing and format syntax is a subset of
995  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
996  * supported will provide results equivalent to their PHP versions.
997  *
998  * Following is the list of all currently supported formats:
999  *<pre>
1000 Sample date:
1001 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1002
1003 Format  Output      Description
1004 ------  ----------  --------------------------------------------------------------
1005   d      10         Day of the month, 2 digits with leading zeros
1006   D      Wed        A textual representation of a day, three letters
1007   j      10         Day of the month without leading zeros
1008   l      Wednesday  A full textual representation of the day of the week
1009   S      th         English ordinal day of month suffix, 2 chars (use with j)
1010   w      3          Numeric representation of the day of the week
1011   z      9          The julian date, or day of the year (0-365)
1012   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1013   F      January    A full textual representation of the month
1014   m      01         Numeric representation of a month, with leading zeros
1015   M      Jan        Month name abbreviation, three letters
1016   n      1          Numeric representation of a month, without leading zeros
1017   t      31         Number of days in the given month
1018   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1019   Y      2007       A full numeric representation of a year, 4 digits
1020   y      07         A two digit representation of a year
1021   a      pm         Lowercase Ante meridiem and Post meridiem
1022   A      PM         Uppercase Ante meridiem and Post meridiem
1023   g      3          12-hour format of an hour without leading zeros
1024   G      15         24-hour format of an hour without leading zeros
1025   h      03         12-hour format of an hour with leading zeros
1026   H      15         24-hour format of an hour with leading zeros
1027   i      05         Minutes with leading zeros
1028   s      01         Seconds, with leading zeros
1029   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1030   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1031   T      CST        Timezone setting of the machine running the code
1032   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1033 </pre>
1034  *
1035  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1036  * <pre><code>
1037 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1038 document.write(dt.format('Y-m-d'));                         //2007-01-10
1039 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1040 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
1041  </code></pre>
1042  *
1043  * Here are some standard date/time patterns that you might find helpful.  They
1044  * are not part of the source of Date.js, but to use them you can simply copy this
1045  * block of code into any script that is included after Date.js and they will also become
1046  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1047  * <pre><code>
1048 Date.patterns = {
1049     ISO8601Long:"Y-m-d H:i:s",
1050     ISO8601Short:"Y-m-d",
1051     ShortDate: "n/j/Y",
1052     LongDate: "l, F d, Y",
1053     FullDateTime: "l, F d, Y g:i:s A",
1054     MonthDay: "F d",
1055     ShortTime: "g:i A",
1056     LongTime: "g:i:s A",
1057     SortableDateTime: "Y-m-d\\TH:i:s",
1058     UniversalSortableDateTime: "Y-m-d H:i:sO",
1059     YearMonth: "F, Y"
1060 };
1061 </code></pre>
1062  *
1063  * Example usage:
1064  * <pre><code>
1065 var dt = new Date();
1066 document.write(dt.format(Date.patterns.ShortDate));
1067  </code></pre>
1068  */
1069
1070 /*
1071  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1072  * They generate precompiled functions from date formats instead of parsing and
1073  * processing the pattern every time you format a date.  These functions are available
1074  * on every Date object (any javascript function).
1075  *
1076  * The original article and download are here:
1077  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1078  *
1079  */
1080  
1081  
1082  // was in core
1083 /**
1084  Returns the number of milliseconds between this date and date
1085  @param {Date} date (optional) Defaults to now
1086  @return {Number} The diff in milliseconds
1087  @member Date getElapsed
1088  */
1089 Date.prototype.getElapsed = function(date) {
1090         return Math.abs((date || new Date()).getTime()-this.getTime());
1091 };
1092 // was in date file..
1093
1094
1095 // private
1096 Date.parseFunctions = {count:0};
1097 // private
1098 Date.parseRegexes = [];
1099 // private
1100 Date.formatFunctions = {count:0};
1101
1102 // private
1103 Date.prototype.dateFormat = function(format) {
1104     if (Date.formatFunctions[format] == null) {
1105         Date.createNewFormat(format);
1106     }
1107     var func = Date.formatFunctions[format];
1108     return this[func]();
1109 };
1110
1111
1112 /**
1113  * Formats a date given the supplied format string
1114  * @param {String} format The format string
1115  * @return {String} The formatted date
1116  * @method
1117  */
1118 Date.prototype.format = Date.prototype.dateFormat;
1119
1120 // private
1121 Date.createNewFormat = function(format) {
1122     var funcName = "format" + Date.formatFunctions.count++;
1123     Date.formatFunctions[format] = funcName;
1124     var code = "Date.prototype." + funcName + " = function(){return ";
1125     var special = false;
1126     var ch = '';
1127     for (var i = 0; i < format.length; ++i) {
1128         ch = format.charAt(i);
1129         if (!special && ch == "\\") {
1130             special = true;
1131         }
1132         else if (special) {
1133             special = false;
1134             code += "'" + String.escape(ch) + "' + ";
1135         }
1136         else {
1137             code += Date.getFormatCode(ch);
1138         }
1139     }
1140     /** eval:var:zzzzzzzzzzzzz */
1141     eval(code.substring(0, code.length - 3) + ";}");
1142 };
1143
1144 // private
1145 Date.getFormatCode = function(character) {
1146     switch (character) {
1147     case "d":
1148         return "String.leftPad(this.getDate(), 2, '0') + ";
1149     case "D":
1150         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1151     case "j":
1152         return "this.getDate() + ";
1153     case "l":
1154         return "Date.dayNames[this.getDay()] + ";
1155     case "S":
1156         return "this.getSuffix() + ";
1157     case "w":
1158         return "this.getDay() + ";
1159     case "z":
1160         return "this.getDayOfYear() + ";
1161     case "W":
1162         return "this.getWeekOfYear() + ";
1163     case "F":
1164         return "Date.monthNames[this.getMonth()] + ";
1165     case "m":
1166         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1167     case "M":
1168         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1169     case "n":
1170         return "(this.getMonth() + 1) + ";
1171     case "t":
1172         return "this.getDaysInMonth() + ";
1173     case "L":
1174         return "(this.isLeapYear() ? 1 : 0) + ";
1175     case "Y":
1176         return "this.getFullYear() + ";
1177     case "y":
1178         return "('' + this.getFullYear()).substring(2, 4) + ";
1179     case "a":
1180         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1181     case "A":
1182         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1183     case "g":
1184         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1185     case "G":
1186         return "this.getHours() + ";
1187     case "h":
1188         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1189     case "H":
1190         return "String.leftPad(this.getHours(), 2, '0') + ";
1191     case "i":
1192         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1193     case "s":
1194         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1195     case "O":
1196         return "this.getGMTOffset() + ";
1197     case "P":
1198         return "this.getGMTColonOffset() + ";
1199     case "T":
1200         return "this.getTimezone() + ";
1201     case "Z":
1202         return "(this.getTimezoneOffset() * -60) + ";
1203     default:
1204         return "'" + String.escape(character) + "' + ";
1205     }
1206 };
1207
1208 /**
1209  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1210  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1211  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1212  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1213  * string or the parse operation will fail.
1214  * Example Usage:
1215 <pre><code>
1216 //dt = Fri May 25 2007 (current date)
1217 var dt = new Date();
1218
1219 //dt = Thu May 25 2006 (today's month/day in 2006)
1220 dt = Date.parseDate("2006", "Y");
1221
1222 //dt = Sun Jan 15 2006 (all date parts specified)
1223 dt = Date.parseDate("2006-1-15", "Y-m-d");
1224
1225 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1226 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1227 </code></pre>
1228  * @param {String} input The unparsed date as a string
1229  * @param {String} format The format the date is in
1230  * @return {Date} The parsed date
1231  * @static
1232  */
1233 Date.parseDate = function(input, format) {
1234     if (Date.parseFunctions[format] == null) {
1235         Date.createParser(format);
1236     }
1237     var func = Date.parseFunctions[format];
1238     return Date[func](input);
1239 };
1240 /**
1241  * @private
1242  */
1243
1244 Date.createParser = function(format) {
1245     var funcName = "parse" + Date.parseFunctions.count++;
1246     var regexNum = Date.parseRegexes.length;
1247     var currentGroup = 1;
1248     Date.parseFunctions[format] = funcName;
1249
1250     var code = "Date." + funcName + " = function(input){\n"
1251         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1252         + "var d = new Date();\n"
1253         + "y = d.getFullYear();\n"
1254         + "m = d.getMonth();\n"
1255         + "d = d.getDate();\n"
1256         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1257         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1258         + "if (results && results.length > 0) {";
1259     var regex = "";
1260
1261     var special = false;
1262     var ch = '';
1263     for (var i = 0; i < format.length; ++i) {
1264         ch = format.charAt(i);
1265         if (!special && ch == "\\") {
1266             special = true;
1267         }
1268         else if (special) {
1269             special = false;
1270             regex += String.escape(ch);
1271         }
1272         else {
1273             var obj = Date.formatCodeToRegex(ch, currentGroup);
1274             currentGroup += obj.g;
1275             regex += obj.s;
1276             if (obj.g && obj.c) {
1277                 code += obj.c;
1278             }
1279         }
1280     }
1281
1282     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1283         + "{v = new Date(y, m, d, h, i, s);}\n"
1284         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1285         + "{v = new Date(y, m, d, h, i);}\n"
1286         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1287         + "{v = new Date(y, m, d, h);}\n"
1288         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1289         + "{v = new Date(y, m, d);}\n"
1290         + "else if (y >= 0 && m >= 0)\n"
1291         + "{v = new Date(y, m);}\n"
1292         + "else if (y >= 0)\n"
1293         + "{v = new Date(y);}\n"
1294         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1295         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1296         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1297         + ";}";
1298
1299     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1300     /** eval:var:zzzzzzzzzzzzz */
1301     eval(code);
1302 };
1303
1304 // private
1305 Date.formatCodeToRegex = function(character, currentGroup) {
1306     switch (character) {
1307     case "D":
1308         return {g:0,
1309         c:null,
1310         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1311     case "j":
1312         return {g:1,
1313             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1314             s:"(\\d{1,2})"}; // day of month without leading zeroes
1315     case "d":
1316         return {g:1,
1317             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1318             s:"(\\d{2})"}; // day of month with leading zeroes
1319     case "l":
1320         return {g:0,
1321             c:null,
1322             s:"(?:" + Date.dayNames.join("|") + ")"};
1323     case "S":
1324         return {g:0,
1325             c:null,
1326             s:"(?:st|nd|rd|th)"};
1327     case "w":
1328         return {g:0,
1329             c:null,
1330             s:"\\d"};
1331     case "z":
1332         return {g:0,
1333             c:null,
1334             s:"(?:\\d{1,3})"};
1335     case "W":
1336         return {g:0,
1337             c:null,
1338             s:"(?:\\d{2})"};
1339     case "F":
1340         return {g:1,
1341             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1342             s:"(" + Date.monthNames.join("|") + ")"};
1343     case "M":
1344         return {g:1,
1345             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1346             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1347     case "n":
1348         return {g:1,
1349             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1350             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1351     case "m":
1352         return {g:1,
1353             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1354             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1355     case "t":
1356         return {g:0,
1357             c:null,
1358             s:"\\d{1,2}"};
1359     case "L":
1360         return {g:0,
1361             c:null,
1362             s:"(?:1|0)"};
1363     case "Y":
1364         return {g:1,
1365             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{4})"};
1367     case "y":
1368         return {g:1,
1369             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1370                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1371             s:"(\\d{1,2})"};
1372     case "a":
1373         return {g:1,
1374             c:"if (results[" + currentGroup + "] == 'am') {\n"
1375                 + "if (h == 12) { h = 0; }\n"
1376                 + "} else { if (h < 12) { h += 12; }}",
1377             s:"(am|pm)"};
1378     case "A":
1379         return {g:1,
1380             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1381                 + "if (h == 12) { h = 0; }\n"
1382                 + "} else { if (h < 12) { h += 12; }}",
1383             s:"(AM|PM)"};
1384     case "g":
1385     case "G":
1386         return {g:1,
1387             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1388             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1389     case "h":
1390     case "H":
1391         return {g:1,
1392             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1393             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1394     case "i":
1395         return {g:1,
1396             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1397             s:"(\\d{2})"};
1398     case "s":
1399         return {g:1,
1400             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1401             s:"(\\d{2})"};
1402     case "O":
1403         return {g:1,
1404             c:[
1405                 "o = results[", currentGroup, "];\n",
1406                 "var sn = o.substring(0,1);\n", // get + / - sign
1407                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1408                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1409                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1410                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1411             ].join(""),
1412             s:"([+\-]\\d{2,4})"};
1413     
1414     
1415     case "P":
1416         return {g:1,
1417                 c:[
1418                    "o = results[", currentGroup, "];\n",
1419                    "var sn = o.substring(0,1);\n",
1420                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1421                    "var mn = o.substring(4,6) % 60;\n",
1422                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1423                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1424             ].join(""),
1425             s:"([+\-]\\d{4})"};
1426     case "T":
1427         return {g:0,
1428             c:null,
1429             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1430     case "Z":
1431         return {g:1,
1432             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1433                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1434             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1435     default:
1436         return {g:0,
1437             c:null,
1438             s:String.escape(character)};
1439     }
1440 };
1441
1442 /**
1443  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1444  * @return {String} The abbreviated timezone name (e.g. 'CST')
1445  */
1446 Date.prototype.getTimezone = function() {
1447     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1448 };
1449
1450 /**
1451  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1452  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1453  */
1454 Date.prototype.getGMTOffset = function() {
1455     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1456         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1457         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1458 };
1459
1460 /**
1461  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1462  * @return {String} 2-characters representing hours and 2-characters representing minutes
1463  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1464  */
1465 Date.prototype.getGMTColonOffset = function() {
1466         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1467                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1468                 + ":"
1469                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1470 }
1471
1472 /**
1473  * Get the numeric day number of the year, adjusted for leap year.
1474  * @return {Number} 0 through 364 (365 in leap years)
1475  */
1476 Date.prototype.getDayOfYear = function() {
1477     var num = 0;
1478     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1479     for (var i = 0; i < this.getMonth(); ++i) {
1480         num += Date.daysInMonth[i];
1481     }
1482     return num + this.getDate() - 1;
1483 };
1484
1485 /**
1486  * Get the string representation of the numeric week number of the year
1487  * (equivalent to the format specifier 'W').
1488  * @return {String} '00' through '52'
1489  */
1490 Date.prototype.getWeekOfYear = function() {
1491     // Skip to Thursday of this week
1492     var now = this.getDayOfYear() + (4 - this.getDay());
1493     // Find the first Thursday of the year
1494     var jan1 = new Date(this.getFullYear(), 0, 1);
1495     var then = (7 - jan1.getDay() + 4);
1496     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1497 };
1498
1499 /**
1500  * Whether or not the current date is in a leap year.
1501  * @return {Boolean} True if the current date is in a leap year, else false
1502  */
1503 Date.prototype.isLeapYear = function() {
1504     var year = this.getFullYear();
1505     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1506 };
1507
1508 /**
1509  * Get the first day of the current month, adjusted for leap year.  The returned value
1510  * is the numeric day index within the week (0-6) which can be used in conjunction with
1511  * the {@link #monthNames} array to retrieve the textual day name.
1512  * Example:
1513  *<pre><code>
1514 var dt = new Date('1/10/2007');
1515 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1516 </code></pre>
1517  * @return {Number} The day number (0-6)
1518  */
1519 Date.prototype.getFirstDayOfMonth = function() {
1520     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1521     return (day < 0) ? (day + 7) : day;
1522 };
1523
1524 /**
1525  * Get the last day of the current month, adjusted for leap year.  The returned value
1526  * is the numeric day index within the week (0-6) which can be used in conjunction with
1527  * the {@link #monthNames} array to retrieve the textual day name.
1528  * Example:
1529  *<pre><code>
1530 var dt = new Date('1/10/2007');
1531 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1532 </code></pre>
1533  * @return {Number} The day number (0-6)
1534  */
1535 Date.prototype.getLastDayOfMonth = function() {
1536     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1537     return (day < 0) ? (day + 7) : day;
1538 };
1539
1540
1541 /**
1542  * Get the first date of this date's month
1543  * @return {Date}
1544  */
1545 Date.prototype.getFirstDateOfMonth = function() {
1546     return new Date(this.getFullYear(), this.getMonth(), 1);
1547 };
1548
1549 /**
1550  * Get the last date of this date's month
1551  * @return {Date}
1552  */
1553 Date.prototype.getLastDateOfMonth = function() {
1554     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1555 };
1556 /**
1557  * Get the number of days in the current month, adjusted for leap year.
1558  * @return {Number} The number of days in the month
1559  */
1560 Date.prototype.getDaysInMonth = function() {
1561     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1562     return Date.daysInMonth[this.getMonth()];
1563 };
1564
1565 /**
1566  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1567  * @return {String} 'st, 'nd', 'rd' or 'th'
1568  */
1569 Date.prototype.getSuffix = function() {
1570     switch (this.getDate()) {
1571         case 1:
1572         case 21:
1573         case 31:
1574             return "st";
1575         case 2:
1576         case 22:
1577             return "nd";
1578         case 3:
1579         case 23:
1580             return "rd";
1581         default:
1582             return "th";
1583     }
1584 };
1585
1586 // private
1587 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1588
1589 /**
1590  * An array of textual month names.
1591  * Override these values for international dates, for example...
1592  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1593  * @type Array
1594  * @static
1595  */
1596 Date.monthNames =
1597    ["January",
1598     "February",
1599     "March",
1600     "April",
1601     "May",
1602     "June",
1603     "July",
1604     "August",
1605     "September",
1606     "October",
1607     "November",
1608     "December"];
1609
1610 /**
1611  * An array of textual day names.
1612  * Override these values for international dates, for example...
1613  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1614  * @type Array
1615  * @static
1616  */
1617 Date.dayNames =
1618    ["Sunday",
1619     "Monday",
1620     "Tuesday",
1621     "Wednesday",
1622     "Thursday",
1623     "Friday",
1624     "Saturday"];
1625
1626 // private
1627 Date.y2kYear = 50;
1628 // private
1629 Date.monthNumbers = {
1630     Jan:0,
1631     Feb:1,
1632     Mar:2,
1633     Apr:3,
1634     May:4,
1635     Jun:5,
1636     Jul:6,
1637     Aug:7,
1638     Sep:8,
1639     Oct:9,
1640     Nov:10,
1641     Dec:11};
1642
1643 /**
1644  * Creates and returns a new Date instance with the exact same date value as the called instance.
1645  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1646  * variable will also be changed.  When the intention is to create a new variable that will not
1647  * modify the original instance, you should create a clone.
1648  *
1649  * Example of correctly cloning a date:
1650  * <pre><code>
1651 //wrong way:
1652 var orig = new Date('10/1/2006');
1653 var copy = orig;
1654 copy.setDate(5);
1655 document.write(orig);  //returns 'Thu Oct 05 2006'!
1656
1657 //correct way:
1658 var orig = new Date('10/1/2006');
1659 var copy = orig.clone();
1660 copy.setDate(5);
1661 document.write(orig);  //returns 'Thu Oct 01 2006'
1662 </code></pre>
1663  * @return {Date} The new Date instance
1664  */
1665 Date.prototype.clone = function() {
1666         return new Date(this.getTime());
1667 };
1668
1669 /**
1670  * Clears any time information from this date
1671  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1672  @return {Date} this or the clone
1673  */
1674 Date.prototype.clearTime = function(clone){
1675     if(clone){
1676         return this.clone().clearTime();
1677     }
1678     this.setHours(0);
1679     this.setMinutes(0);
1680     this.setSeconds(0);
1681     this.setMilliseconds(0);
1682     return this;
1683 };
1684
1685 // private
1686 // safari setMonth is broken
1687 if(Roo.isSafari){
1688     Date.brokenSetMonth = Date.prototype.setMonth;
1689         Date.prototype.setMonth = function(num){
1690                 if(num <= -1){
1691                         var n = Math.ceil(-num);
1692                         var back_year = Math.ceil(n/12);
1693                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1694                         this.setFullYear(this.getFullYear() - back_year);
1695                         return Date.brokenSetMonth.call(this, month);
1696                 } else {
1697                         return Date.brokenSetMonth.apply(this, arguments);
1698                 }
1699         };
1700 }
1701
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.MILLI = "ms";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.SECOND = "s";
1710 /** Date interval constant 
1711 * @static 
1712 * @type String */
1713 Date.MINUTE = "mi";
1714 /** Date interval constant 
1715 * @static 
1716 * @type String */
1717 Date.HOUR = "h";
1718 /** Date interval constant 
1719 * @static 
1720 * @type String */
1721 Date.DAY = "d";
1722 /** Date interval constant 
1723 * @static 
1724 * @type String */
1725 Date.MONTH = "mo";
1726 /** Date interval constant 
1727 * @static 
1728 * @type String */
1729 Date.YEAR = "y";
1730
1731 /**
1732  * Provides a convenient method of performing basic date arithmetic.  This method
1733  * does not modify the Date instance being called - it creates and returns
1734  * a new Date instance containing the resulting date value.
1735  *
1736  * Examples:
1737  * <pre><code>
1738 //Basic usage:
1739 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1740 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1741
1742 //Negative values will subtract correctly:
1743 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1744 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1745
1746 //You can even chain several calls together in one line!
1747 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1748 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1749  </code></pre>
1750  *
1751  * @param {String} interval   A valid date interval enum value
1752  * @param {Number} value      The amount to add to the current date
1753  * @return {Date} The new Date instance
1754  */
1755 Date.prototype.add = function(interval, value){
1756   var d = this.clone();
1757   if (!interval || value === 0) return d;
1758   switch(interval.toLowerCase()){
1759     case Date.MILLI:
1760       d.setMilliseconds(this.getMilliseconds() + value);
1761       break;
1762     case Date.SECOND:
1763       d.setSeconds(this.getSeconds() + value);
1764       break;
1765     case Date.MINUTE:
1766       d.setMinutes(this.getMinutes() + value);
1767       break;
1768     case Date.HOUR:
1769       d.setHours(this.getHours() + value);
1770       break;
1771     case Date.DAY:
1772       d.setDate(this.getDate() + value);
1773       break;
1774     case Date.MONTH:
1775       var day = this.getDate();
1776       if(day > 28){
1777           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1778       }
1779       d.setDate(day);
1780       d.setMonth(this.getMonth() + value);
1781       break;
1782     case Date.YEAR:
1783       d.setFullYear(this.getFullYear() + value);
1784       break;
1785   }
1786   return d;
1787 };
1788 /*
1789  * Based on:
1790  * Ext JS Library 1.1.1
1791  * Copyright(c) 2006-2007, Ext JS, LLC.
1792  *
1793  * Originally Released Under LGPL - original licence link has changed is not relivant.
1794  *
1795  * Fork - LGPL
1796  * <script type="text/javascript">
1797  */
1798
1799 /**
1800  * @class Roo.lib.Dom
1801  * @static
1802  * 
1803  * Dom utils (from YIU afaik)
1804  * 
1805  **/
1806 Roo.lib.Dom = {
1807     /**
1808      * Get the view width
1809      * @param {Boolean} full True will get the full document, otherwise it's the view width
1810      * @return {Number} The width
1811      */
1812      
1813     getViewWidth : function(full) {
1814         return full ? this.getDocumentWidth() : this.getViewportWidth();
1815     },
1816     /**
1817      * Get the view height
1818      * @param {Boolean} full True will get the full document, otherwise it's the view height
1819      * @return {Number} The height
1820      */
1821     getViewHeight : function(full) {
1822         return full ? this.getDocumentHeight() : this.getViewportHeight();
1823     },
1824
1825     getDocumentHeight: function() {
1826         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1827         return Math.max(scrollHeight, this.getViewportHeight());
1828     },
1829
1830     getDocumentWidth: function() {
1831         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1832         return Math.max(scrollWidth, this.getViewportWidth());
1833     },
1834
1835     getViewportHeight: function() {
1836         var height = self.innerHeight;
1837         var mode = document.compatMode;
1838
1839         if ((mode || Roo.isIE) && !Roo.isOpera) {
1840             height = (mode == "CSS1Compat") ?
1841                      document.documentElement.clientHeight :
1842                      document.body.clientHeight;
1843         }
1844
1845         return height;
1846     },
1847
1848     getViewportWidth: function() {
1849         var width = self.innerWidth;
1850         var mode = document.compatMode;
1851
1852         if (mode || Roo.isIE) {
1853             width = (mode == "CSS1Compat") ?
1854                     document.documentElement.clientWidth :
1855                     document.body.clientWidth;
1856         }
1857         return width;
1858     },
1859
1860     isAncestor : function(p, c) {
1861         p = Roo.getDom(p);
1862         c = Roo.getDom(c);
1863         if (!p || !c) {
1864             return false;
1865         }
1866
1867         if (p.contains && !Roo.isSafari) {
1868             return p.contains(c);
1869         } else if (p.compareDocumentPosition) {
1870             return !!(p.compareDocumentPosition(c) & 16);
1871         } else {
1872             var parent = c.parentNode;
1873             while (parent) {
1874                 if (parent == p) {
1875                     return true;
1876                 }
1877                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1878                     return false;
1879                 }
1880                 parent = parent.parentNode;
1881             }
1882             return false;
1883         }
1884     },
1885
1886     getRegion : function(el) {
1887         return Roo.lib.Region.getRegion(el);
1888     },
1889
1890     getY : function(el) {
1891         return this.getXY(el)[1];
1892     },
1893
1894     getX : function(el) {
1895         return this.getXY(el)[0];
1896     },
1897
1898     getXY : function(el) {
1899         var p, pe, b, scroll, bd = document.body;
1900         el = Roo.getDom(el);
1901         var fly = Roo.lib.AnimBase.fly;
1902         if (el.getBoundingClientRect) {
1903             b = el.getBoundingClientRect();
1904             scroll = fly(document).getScroll();
1905             return [b.left + scroll.left, b.top + scroll.top];
1906         }
1907         var x = 0, y = 0;
1908
1909         p = el;
1910
1911         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1912
1913         while (p) {
1914
1915             x += p.offsetLeft;
1916             y += p.offsetTop;
1917
1918             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1919                 hasAbsolute = true;
1920             }
1921
1922             if (Roo.isGecko) {
1923                 pe = fly(p);
1924
1925                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1926                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1927
1928
1929                 x += bl;
1930                 y += bt;
1931
1932
1933                 if (p != el && pe.getStyle('overflow') != 'visible') {
1934                     x += bl;
1935                     y += bt;
1936                 }
1937             }
1938             p = p.offsetParent;
1939         }
1940
1941         if (Roo.isSafari && hasAbsolute) {
1942             x -= bd.offsetLeft;
1943             y -= bd.offsetTop;
1944         }
1945
1946         if (Roo.isGecko && !hasAbsolute) {
1947             var dbd = fly(bd);
1948             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1949             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1950         }
1951
1952         p = el.parentNode;
1953         while (p && p != bd) {
1954             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1955                 x -= p.scrollLeft;
1956                 y -= p.scrollTop;
1957             }
1958             p = p.parentNode;
1959         }
1960         return [x, y];
1961     },
1962  
1963   
1964
1965
1966     setXY : function(el, xy) {
1967         el = Roo.fly(el, '_setXY');
1968         el.position();
1969         var pts = el.translatePoints(xy);
1970         if (xy[0] !== false) {
1971             el.dom.style.left = pts.left + "px";
1972         }
1973         if (xy[1] !== false) {
1974             el.dom.style.top = pts.top + "px";
1975         }
1976     },
1977
1978     setX : function(el, x) {
1979         this.setXY(el, [x, false]);
1980     },
1981
1982     setY : function(el, y) {
1983         this.setXY(el, [false, y]);
1984     }
1985 };
1986 /*
1987  * Portions of this file are based on pieces of Yahoo User Interface Library
1988  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1989  * YUI licensed under the BSD License:
1990  * http://developer.yahoo.net/yui/license.txt
1991  * <script type="text/javascript">
1992  *
1993  */
1994
1995 Roo.lib.Event = function() {
1996     var loadComplete = false;
1997     var listeners = [];
1998     var unloadListeners = [];
1999     var retryCount = 0;
2000     var onAvailStack = [];
2001     var counter = 0;
2002     var lastError = null;
2003
2004     return {
2005         POLL_RETRYS: 200,
2006         POLL_INTERVAL: 20,
2007         EL: 0,
2008         TYPE: 1,
2009         FN: 2,
2010         WFN: 3,
2011         OBJ: 3,
2012         ADJ_SCOPE: 4,
2013         _interval: null,
2014
2015         startInterval: function() {
2016             if (!this._interval) {
2017                 var self = this;
2018                 var callback = function() {
2019                     self._tryPreloadAttach();
2020                 };
2021                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2022
2023             }
2024         },
2025
2026         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2027             onAvailStack.push({ id:         p_id,
2028                 fn:         p_fn,
2029                 obj:        p_obj,
2030                 override:   p_override,
2031                 checkReady: false    });
2032
2033             retryCount = this.POLL_RETRYS;
2034             this.startInterval();
2035         },
2036
2037
2038         addListener: function(el, eventName, fn) {
2039             el = Roo.getDom(el);
2040             if (!el || !fn) {
2041                 return false;
2042             }
2043
2044             if ("unload" == eventName) {
2045                 unloadListeners[unloadListeners.length] =
2046                 [el, eventName, fn];
2047                 return true;
2048             }
2049
2050             var wrappedFn = function(e) {
2051                 return fn(Roo.lib.Event.getEvent(e));
2052             };
2053
2054             var li = [el, eventName, fn, wrappedFn];
2055
2056             var index = listeners.length;
2057             listeners[index] = li;
2058
2059             this.doAdd(el, eventName, wrappedFn, false);
2060             return true;
2061
2062         },
2063
2064
2065         removeListener: function(el, eventName, fn) {
2066             var i, len;
2067
2068             el = Roo.getDom(el);
2069
2070             if(!fn) {
2071                 return this.purgeElement(el, false, eventName);
2072             }
2073
2074
2075             if ("unload" == eventName) {
2076
2077                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2078                     var li = unloadListeners[i];
2079                     if (li &&
2080                         li[0] == el &&
2081                         li[1] == eventName &&
2082                         li[2] == fn) {
2083                         unloadListeners.splice(i, 1);
2084                         return true;
2085                     }
2086                 }
2087
2088                 return false;
2089             }
2090
2091             var cacheItem = null;
2092
2093
2094             var index = arguments[3];
2095
2096             if ("undefined" == typeof index) {
2097                 index = this._getCacheIndex(el, eventName, fn);
2098             }
2099
2100             if (index >= 0) {
2101                 cacheItem = listeners[index];
2102             }
2103
2104             if (!el || !cacheItem) {
2105                 return false;
2106             }
2107
2108             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2109
2110             delete listeners[index][this.WFN];
2111             delete listeners[index][this.FN];
2112             listeners.splice(index, 1);
2113
2114             return true;
2115
2116         },
2117
2118
2119         getTarget: function(ev, resolveTextNode) {
2120             ev = ev.browserEvent || ev;
2121             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2122             var t = ev.target || ev.srcElement;
2123             return this.resolveTextNode(t);
2124         },
2125
2126
2127         resolveTextNode: function(node) {
2128             if (Roo.isSafari && node && 3 == node.nodeType) {
2129                 return node.parentNode;
2130             } else {
2131                 return node;
2132             }
2133         },
2134
2135
2136         getPageX: function(ev) {
2137             ev = ev.browserEvent || ev;
2138             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2139             var x = ev.pageX;
2140             if (!x && 0 !== x) {
2141                 x = ev.clientX || 0;
2142
2143                 if (Roo.isIE) {
2144                     x += this.getScroll()[1];
2145                 }
2146             }
2147
2148             return x;
2149         },
2150
2151
2152         getPageY: function(ev) {
2153             ev = ev.browserEvent || ev;
2154             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2155             var y = ev.pageY;
2156             if (!y && 0 !== y) {
2157                 y = ev.clientY || 0;
2158
2159                 if (Roo.isIE) {
2160                     y += this.getScroll()[0];
2161                 }
2162             }
2163
2164
2165             return y;
2166         },
2167
2168
2169         getXY: function(ev) {
2170             ev = ev.browserEvent || ev;
2171             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2172             return [this.getPageX(ev), this.getPageY(ev)];
2173         },
2174
2175
2176         getRelatedTarget: function(ev) {
2177             ev = ev.browserEvent || ev;
2178             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2179             var t = ev.relatedTarget;
2180             if (!t) {
2181                 if (ev.type == "mouseout") {
2182                     t = ev.toElement;
2183                 } else if (ev.type == "mouseover") {
2184                     t = ev.fromElement;
2185                 }
2186             }
2187
2188             return this.resolveTextNode(t);
2189         },
2190
2191
2192         getTime: function(ev) {
2193             ev = ev.browserEvent || ev;
2194             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2195             if (!ev.time) {
2196                 var t = new Date().getTime();
2197                 try {
2198                     ev.time = t;
2199                 } catch(ex) {
2200                     this.lastError = ex;
2201                     return t;
2202                 }
2203             }
2204
2205             return ev.time;
2206         },
2207
2208
2209         stopEvent: function(ev) {
2210             this.stopPropagation(ev);
2211             this.preventDefault(ev);
2212         },
2213
2214
2215         stopPropagation: function(ev) {
2216             ev = ev.browserEvent || ev;
2217             if (ev.stopPropagation) {
2218                 ev.stopPropagation();
2219             } else {
2220                 ev.cancelBubble = true;
2221             }
2222         },
2223
2224
2225         preventDefault: function(ev) {
2226             ev = ev.browserEvent || ev;
2227             if(ev.preventDefault) {
2228                 ev.preventDefault();
2229             } else {
2230                 ev.returnValue = false;
2231             }
2232         },
2233
2234
2235         getEvent: function(e) {
2236             var ev = e || window.event;
2237             if (!ev) {
2238                 var c = this.getEvent.caller;
2239                 while (c) {
2240                     ev = c.arguments[0];
2241                     if (ev && Event == ev.constructor) {
2242                         break;
2243                     }
2244                     c = c.caller;
2245                 }
2246             }
2247             return ev;
2248         },
2249
2250
2251         getCharCode: function(ev) {
2252             ev = ev.browserEvent || ev;
2253             return ev.charCode || ev.keyCode || 0;
2254         },
2255
2256
2257         _getCacheIndex: function(el, eventName, fn) {
2258             for (var i = 0,len = listeners.length; i < len; ++i) {
2259                 var li = listeners[i];
2260                 if (li &&
2261                     li[this.FN] == fn &&
2262                     li[this.EL] == el &&
2263                     li[this.TYPE] == eventName) {
2264                     return i;
2265                 }
2266             }
2267
2268             return -1;
2269         },
2270
2271
2272         elCache: {},
2273
2274
2275         getEl: function(id) {
2276             return document.getElementById(id);
2277         },
2278
2279
2280         clearCache: function() {
2281         },
2282
2283
2284         _load: function(e) {
2285             loadComplete = true;
2286             var EU = Roo.lib.Event;
2287
2288
2289             if (Roo.isIE) {
2290                 EU.doRemove(window, "load", EU._load);
2291             }
2292         },
2293
2294
2295         _tryPreloadAttach: function() {
2296
2297             if (this.locked) {
2298                 return false;
2299             }
2300
2301             this.locked = true;
2302
2303
2304             var tryAgain = !loadComplete;
2305             if (!tryAgain) {
2306                 tryAgain = (retryCount > 0);
2307             }
2308
2309
2310             var notAvail = [];
2311             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2312                 var item = onAvailStack[i];
2313                 if (item) {
2314                     var el = this.getEl(item.id);
2315
2316                     if (el) {
2317                         if (!item.checkReady ||
2318                             loadComplete ||
2319                             el.nextSibling ||
2320                             (document && document.body)) {
2321
2322                             var scope = el;
2323                             if (item.override) {
2324                                 if (item.override === true) {
2325                                     scope = item.obj;
2326                                 } else {
2327                                     scope = item.override;
2328                                 }
2329                             }
2330                             item.fn.call(scope, item.obj);
2331                             onAvailStack[i] = null;
2332                         }
2333                     } else {
2334                         notAvail.push(item);
2335                     }
2336                 }
2337             }
2338
2339             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2340
2341             if (tryAgain) {
2342
2343                 this.startInterval();
2344             } else {
2345                 clearInterval(this._interval);
2346                 this._interval = null;
2347             }
2348
2349             this.locked = false;
2350
2351             return true;
2352
2353         },
2354
2355
2356         purgeElement: function(el, recurse, eventName) {
2357             var elListeners = this.getListeners(el, eventName);
2358             if (elListeners) {
2359                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2360                     var l = elListeners[i];
2361                     this.removeListener(el, l.type, l.fn);
2362                 }
2363             }
2364
2365             if (recurse && el && el.childNodes) {
2366                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2367                     this.purgeElement(el.childNodes[i], recurse, eventName);
2368                 }
2369             }
2370         },
2371
2372
2373         getListeners: function(el, eventName) {
2374             var results = [], searchLists;
2375             if (!eventName) {
2376                 searchLists = [listeners, unloadListeners];
2377             } else if (eventName == "unload") {
2378                 searchLists = [unloadListeners];
2379             } else {
2380                 searchLists = [listeners];
2381             }
2382
2383             for (var j = 0; j < searchLists.length; ++j) {
2384                 var searchList = searchLists[j];
2385                 if (searchList && searchList.length > 0) {
2386                     for (var i = 0,len = searchList.length; i < len; ++i) {
2387                         var l = searchList[i];
2388                         if (l && l[this.EL] === el &&
2389                             (!eventName || eventName === l[this.TYPE])) {
2390                             results.push({
2391                                 type:   l[this.TYPE],
2392                                 fn:     l[this.FN],
2393                                 obj:    l[this.OBJ],
2394                                 adjust: l[this.ADJ_SCOPE],
2395                                 index:  i
2396                             });
2397                         }
2398                     }
2399                 }
2400             }
2401
2402             return (results.length) ? results : null;
2403         },
2404
2405
2406         _unload: function(e) {
2407
2408             var EU = Roo.lib.Event, i, j, l, len, index;
2409
2410             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2411                 l = unloadListeners[i];
2412                 if (l) {
2413                     var scope = window;
2414                     if (l[EU.ADJ_SCOPE]) {
2415                         if (l[EU.ADJ_SCOPE] === true) {
2416                             scope = l[EU.OBJ];
2417                         } else {
2418                             scope = l[EU.ADJ_SCOPE];
2419                         }
2420                     }
2421                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2422                     unloadListeners[i] = null;
2423                     l = null;
2424                     scope = null;
2425                 }
2426             }
2427
2428             unloadListeners = null;
2429
2430             if (listeners && listeners.length > 0) {
2431                 j = listeners.length;
2432                 while (j) {
2433                     index = j - 1;
2434                     l = listeners[index];
2435                     if (l) {
2436                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2437                                 l[EU.FN], index);
2438                     }
2439                     j = j - 1;
2440                 }
2441                 l = null;
2442
2443                 EU.clearCache();
2444             }
2445
2446             EU.doRemove(window, "unload", EU._unload);
2447
2448         },
2449
2450
2451         getScroll: function() {
2452             var dd = document.documentElement, db = document.body;
2453             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2454                 return [dd.scrollTop, dd.scrollLeft];
2455             } else if (db) {
2456                 return [db.scrollTop, db.scrollLeft];
2457             } else {
2458                 return [0, 0];
2459             }
2460         },
2461
2462
2463         doAdd: function () {
2464             if (window.addEventListener) {
2465                 return function(el, eventName, fn, capture) {
2466                     el.addEventListener(eventName, fn, (capture));
2467                 };
2468             } else if (window.attachEvent) {
2469                 return function(el, eventName, fn, capture) {
2470                     el.attachEvent("on" + eventName, fn);
2471                 };
2472             } else {
2473                 return function() {
2474                 };
2475             }
2476         }(),
2477
2478
2479         doRemove: function() {
2480             if (window.removeEventListener) {
2481                 return function (el, eventName, fn, capture) {
2482                     el.removeEventListener(eventName, fn, (capture));
2483                 };
2484             } else if (window.detachEvent) {
2485                 return function (el, eventName, fn) {
2486                     el.detachEvent("on" + eventName, fn);
2487                 };
2488             } else {
2489                 return function() {
2490                 };
2491             }
2492         }()
2493     };
2494     
2495 }();
2496 (function() {     
2497    
2498     var E = Roo.lib.Event;
2499     E.on = E.addListener;
2500     E.un = E.removeListener;
2501
2502     if (document && document.body) {
2503         E._load();
2504     } else {
2505         E.doAdd(window, "load", E._load);
2506     }
2507     E.doAdd(window, "unload", E._unload);
2508     E._tryPreloadAttach();
2509 })();
2510
2511 /*
2512  * Portions of this file are based on pieces of Yahoo User Interface Library
2513  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2514  * YUI licensed under the BSD License:
2515  * http://developer.yahoo.net/yui/license.txt
2516  * <script type="text/javascript">
2517  *
2518  */
2519
2520 (function() {
2521     /**
2522      * @class Roo.lib.Ajax
2523      *
2524      */
2525     Roo.lib.Ajax = {
2526         /**
2527          * @static 
2528          */
2529         request : function(method, uri, cb, data, options) {
2530             if(options){
2531                 var hs = options.headers;
2532                 if(hs){
2533                     for(var h in hs){
2534                         if(hs.hasOwnProperty(h)){
2535                             this.initHeader(h, hs[h], false);
2536                         }
2537                     }
2538                 }
2539                 if(options.xmlData){
2540                     this.initHeader('Content-Type', 'text/xml', false);
2541                     method = 'POST';
2542                     data = options.xmlData;
2543                 }
2544             }
2545
2546             return this.asyncRequest(method, uri, cb, data);
2547         },
2548
2549         serializeForm : function(form) {
2550             if(typeof form == 'string') {
2551                 form = (document.getElementById(form) || document.forms[form]);
2552             }
2553
2554             var el, name, val, disabled, data = '', hasSubmit = false;
2555             for (var i = 0; i < form.elements.length; i++) {
2556                 el = form.elements[i];
2557                 disabled = form.elements[i].disabled;
2558                 name = form.elements[i].name;
2559                 val = form.elements[i].value;
2560
2561                 if (!disabled && name){
2562                     switch (el.type)
2563                             {
2564                         case 'select-one':
2565                         case 'select-multiple':
2566                             for (var j = 0; j < el.options.length; j++) {
2567                                 if (el.options[j].selected) {
2568                                     if (Roo.isIE) {
2569                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2570                                     }
2571                                     else {
2572                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2573                                     }
2574                                 }
2575                             }
2576                             break;
2577                         case 'radio':
2578                         case 'checkbox':
2579                             if (el.checked) {
2580                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2581                             }
2582                             break;
2583                         case 'file':
2584
2585                         case undefined:
2586
2587                         case 'reset':
2588
2589                         case 'button':
2590
2591                             break;
2592                         case 'submit':
2593                             if(hasSubmit == false) {
2594                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2595                                 hasSubmit = true;
2596                             }
2597                             break;
2598                         default:
2599                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2600                             break;
2601                     }
2602                 }
2603             }
2604             data = data.substr(0, data.length - 1);
2605             return data;
2606         },
2607
2608         headers:{},
2609
2610         hasHeaders:false,
2611
2612         useDefaultHeader:true,
2613
2614         defaultPostHeader:'application/x-www-form-urlencoded',
2615
2616         useDefaultXhrHeader:true,
2617
2618         defaultXhrHeader:'XMLHttpRequest',
2619
2620         hasDefaultHeaders:true,
2621
2622         defaultHeaders:{},
2623
2624         poll:{},
2625
2626         timeout:{},
2627
2628         pollInterval:50,
2629
2630         transactionId:0,
2631
2632         setProgId:function(id)
2633         {
2634             this.activeX.unshift(id);
2635         },
2636
2637         setDefaultPostHeader:function(b)
2638         {
2639             this.useDefaultHeader = b;
2640         },
2641
2642         setDefaultXhrHeader:function(b)
2643         {
2644             this.useDefaultXhrHeader = b;
2645         },
2646
2647         setPollingInterval:function(i)
2648         {
2649             if (typeof i == 'number' && isFinite(i)) {
2650                 this.pollInterval = i;
2651             }
2652         },
2653
2654         createXhrObject:function(transactionId)
2655         {
2656             var obj,http;
2657             try
2658             {
2659
2660                 http = new XMLHttpRequest();
2661
2662                 obj = { conn:http, tId:transactionId };
2663             }
2664             catch(e)
2665             {
2666                 for (var i = 0; i < this.activeX.length; ++i) {
2667                     try
2668                     {
2669
2670                         http = new ActiveXObject(this.activeX[i]);
2671
2672                         obj = { conn:http, tId:transactionId };
2673                         break;
2674                     }
2675                     catch(e) {
2676                     }
2677                 }
2678             }
2679             finally
2680             {
2681                 return obj;
2682             }
2683         },
2684
2685         getConnectionObject:function()
2686         {
2687             var o;
2688             var tId = this.transactionId;
2689
2690             try
2691             {
2692                 o = this.createXhrObject(tId);
2693                 if (o) {
2694                     this.transactionId++;
2695                 }
2696             }
2697             catch(e) {
2698             }
2699             finally
2700             {
2701                 return o;
2702             }
2703         },
2704
2705         asyncRequest:function(method, uri, callback, postData)
2706         {
2707             var o = this.getConnectionObject();
2708
2709             if (!o) {
2710                 return null;
2711             }
2712             else {
2713                 o.conn.open(method, uri, true);
2714
2715                 if (this.useDefaultXhrHeader) {
2716                     if (!this.defaultHeaders['X-Requested-With']) {
2717                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2718                     }
2719                 }
2720
2721                 if(postData && this.useDefaultHeader){
2722                     this.initHeader('Content-Type', this.defaultPostHeader);
2723                 }
2724
2725                  if (this.hasDefaultHeaders || this.hasHeaders) {
2726                     this.setHeader(o);
2727                 }
2728
2729                 this.handleReadyState(o, callback);
2730                 o.conn.send(postData || null);
2731
2732                 return o;
2733             }
2734         },
2735
2736         handleReadyState:function(o, callback)
2737         {
2738             var oConn = this;
2739
2740             if (callback && callback.timeout) {
2741                 
2742                 this.timeout[o.tId] = window.setTimeout(function() {
2743                     oConn.abort(o, callback, true);
2744                 }, callback.timeout);
2745             }
2746
2747             this.poll[o.tId] = window.setInterval(
2748                     function() {
2749                         if (o.conn && o.conn.readyState == 4) {
2750                             window.clearInterval(oConn.poll[o.tId]);
2751                             delete oConn.poll[o.tId];
2752
2753                             if(callback && callback.timeout) {
2754                                 window.clearTimeout(oConn.timeout[o.tId]);
2755                                 delete oConn.timeout[o.tId];
2756                             }
2757
2758                             oConn.handleTransactionResponse(o, callback);
2759                         }
2760                     }
2761                     , this.pollInterval);
2762         },
2763
2764         handleTransactionResponse:function(o, callback, isAbort)
2765         {
2766
2767             if (!callback) {
2768                 this.releaseObject(o);
2769                 return;
2770             }
2771
2772             var httpStatus, responseObject;
2773
2774             try
2775             {
2776                 if (o.conn.status !== undefined && o.conn.status != 0) {
2777                     httpStatus = o.conn.status;
2778                 }
2779                 else {
2780                     httpStatus = 13030;
2781                 }
2782             }
2783             catch(e) {
2784
2785
2786                 httpStatus = 13030;
2787             }
2788
2789             if (httpStatus >= 200 && httpStatus < 300) {
2790                 responseObject = this.createResponseObject(o, callback.argument);
2791                 if (callback.success) {
2792                     if (!callback.scope) {
2793                         callback.success(responseObject);
2794                     }
2795                     else {
2796
2797
2798                         callback.success.apply(callback.scope, [responseObject]);
2799                     }
2800                 }
2801             }
2802             else {
2803                 switch (httpStatus) {
2804
2805                     case 12002:
2806                     case 12029:
2807                     case 12030:
2808                     case 12031:
2809                     case 12152:
2810                     case 13030:
2811                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2812                         if (callback.failure) {
2813                             if (!callback.scope) {
2814                                 callback.failure(responseObject);
2815                             }
2816                             else {
2817                                 callback.failure.apply(callback.scope, [responseObject]);
2818                             }
2819                         }
2820                         break;
2821                     default:
2822                         responseObject = this.createResponseObject(o, callback.argument);
2823                         if (callback.failure) {
2824                             if (!callback.scope) {
2825                                 callback.failure(responseObject);
2826                             }
2827                             else {
2828                                 callback.failure.apply(callback.scope, [responseObject]);
2829                             }
2830                         }
2831                 }
2832             }
2833
2834             this.releaseObject(o);
2835             responseObject = null;
2836         },
2837
2838         createResponseObject:function(o, callbackArg)
2839         {
2840             var obj = {};
2841             var headerObj = {};
2842
2843             try
2844             {
2845                 var headerStr = o.conn.getAllResponseHeaders();
2846                 var header = headerStr.split('\n');
2847                 for (var i = 0; i < header.length; i++) {
2848                     var delimitPos = header[i].indexOf(':');
2849                     if (delimitPos != -1) {
2850                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2851                     }
2852                 }
2853             }
2854             catch(e) {
2855             }
2856
2857             obj.tId = o.tId;
2858             obj.status = o.conn.status;
2859             obj.statusText = o.conn.statusText;
2860             obj.getResponseHeader = headerObj;
2861             obj.getAllResponseHeaders = headerStr;
2862             obj.responseText = o.conn.responseText;
2863             obj.responseXML = o.conn.responseXML;
2864
2865             if (typeof callbackArg !== undefined) {
2866                 obj.argument = callbackArg;
2867             }
2868
2869             return obj;
2870         },
2871
2872         createExceptionObject:function(tId, callbackArg, isAbort)
2873         {
2874             var COMM_CODE = 0;
2875             var COMM_ERROR = 'communication failure';
2876             var ABORT_CODE = -1;
2877             var ABORT_ERROR = 'transaction aborted';
2878
2879             var obj = {};
2880
2881             obj.tId = tId;
2882             if (isAbort) {
2883                 obj.status = ABORT_CODE;
2884                 obj.statusText = ABORT_ERROR;
2885             }
2886             else {
2887                 obj.status = COMM_CODE;
2888                 obj.statusText = COMM_ERROR;
2889             }
2890
2891             if (callbackArg) {
2892                 obj.argument = callbackArg;
2893             }
2894
2895             return obj;
2896         },
2897
2898         initHeader:function(label, value, isDefault)
2899         {
2900             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2901
2902             if (headerObj[label] === undefined) {
2903                 headerObj[label] = value;
2904             }
2905             else {
2906
2907
2908                 headerObj[label] = value + "," + headerObj[label];
2909             }
2910
2911             if (isDefault) {
2912                 this.hasDefaultHeaders = true;
2913             }
2914             else {
2915                 this.hasHeaders = true;
2916             }
2917         },
2918
2919
2920         setHeader:function(o)
2921         {
2922             if (this.hasDefaultHeaders) {
2923                 for (var prop in this.defaultHeaders) {
2924                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2925                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2926                     }
2927                 }
2928             }
2929
2930             if (this.hasHeaders) {
2931                 for (var prop in this.headers) {
2932                     if (this.headers.hasOwnProperty(prop)) {
2933                         o.conn.setRequestHeader(prop, this.headers[prop]);
2934                     }
2935                 }
2936                 this.headers = {};
2937                 this.hasHeaders = false;
2938             }
2939         },
2940
2941         resetDefaultHeaders:function() {
2942             delete this.defaultHeaders;
2943             this.defaultHeaders = {};
2944             this.hasDefaultHeaders = false;
2945         },
2946
2947         abort:function(o, callback, isTimeout)
2948         {
2949             if(this.isCallInProgress(o)) {
2950                 o.conn.abort();
2951                 window.clearInterval(this.poll[o.tId]);
2952                 delete this.poll[o.tId];
2953                 if (isTimeout) {
2954                     delete this.timeout[o.tId];
2955                 }
2956
2957                 this.handleTransactionResponse(o, callback, true);
2958
2959                 return true;
2960             }
2961             else {
2962                 return false;
2963             }
2964         },
2965
2966
2967         isCallInProgress:function(o)
2968         {
2969             if (o && o.conn) {
2970                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2971             }
2972             else {
2973
2974                 return false;
2975             }
2976         },
2977
2978
2979         releaseObject:function(o)
2980         {
2981
2982             o.conn = null;
2983
2984             o = null;
2985         },
2986
2987         activeX:[
2988         'MSXML2.XMLHTTP.3.0',
2989         'MSXML2.XMLHTTP',
2990         'Microsoft.XMLHTTP'
2991         ]
2992
2993
2994     };
2995 })();/*
2996  * Portions of this file are based on pieces of Yahoo User Interface Library
2997  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2998  * YUI licensed under the BSD License:
2999  * http://developer.yahoo.net/yui/license.txt
3000  * <script type="text/javascript">
3001  *
3002  */
3003
3004 Roo.lib.Region = function(t, r, b, l) {
3005     this.top = t;
3006     this[1] = t;
3007     this.right = r;
3008     this.bottom = b;
3009     this.left = l;
3010     this[0] = l;
3011 };
3012
3013
3014 Roo.lib.Region.prototype = {
3015     contains : function(region) {
3016         return ( region.left >= this.left &&
3017                  region.right <= this.right &&
3018                  region.top >= this.top &&
3019                  region.bottom <= this.bottom    );
3020
3021     },
3022
3023     getArea : function() {
3024         return ( (this.bottom - this.top) * (this.right - this.left) );
3025     },
3026
3027     intersect : function(region) {
3028         var t = Math.max(this.top, region.top);
3029         var r = Math.min(this.right, region.right);
3030         var b = Math.min(this.bottom, region.bottom);
3031         var l = Math.max(this.left, region.left);
3032
3033         if (b >= t && r >= l) {
3034             return new Roo.lib.Region(t, r, b, l);
3035         } else {
3036             return null;
3037         }
3038     },
3039     union : function(region) {
3040         var t = Math.min(this.top, region.top);
3041         var r = Math.max(this.right, region.right);
3042         var b = Math.max(this.bottom, region.bottom);
3043         var l = Math.min(this.left, region.left);
3044
3045         return new Roo.lib.Region(t, r, b, l);
3046     },
3047
3048     adjust : function(t, l, b, r) {
3049         this.top += t;
3050         this.left += l;
3051         this.right += r;
3052         this.bottom += b;
3053         return this;
3054     }
3055 };
3056
3057 Roo.lib.Region.getRegion = function(el) {
3058     var p = Roo.lib.Dom.getXY(el);
3059
3060     var t = p[1];
3061     var r = p[0] + el.offsetWidth;
3062     var b = p[1] + el.offsetHeight;
3063     var l = p[0];
3064
3065     return new Roo.lib.Region(t, r, b, l);
3066 };
3067 /*
3068  * Portions of this file are based on pieces of Yahoo User Interface Library
3069  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3070  * YUI licensed under the BSD License:
3071  * http://developer.yahoo.net/yui/license.txt
3072  * <script type="text/javascript">
3073  *
3074  */
3075 //@@dep Roo.lib.Region
3076
3077
3078 Roo.lib.Point = function(x, y) {
3079     if (x instanceof Array) {
3080         y = x[1];
3081         x = x[0];
3082     }
3083     this.x = this.right = this.left = this[0] = x;
3084     this.y = this.top = this.bottom = this[1] = y;
3085 };
3086
3087 Roo.lib.Point.prototype = new Roo.lib.Region();
3088 /*
3089  * Portions of this file are based on pieces of Yahoo User Interface Library
3090  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3091  * YUI licensed under the BSD License:
3092  * http://developer.yahoo.net/yui/license.txt
3093  * <script type="text/javascript">
3094  *
3095  */
3096  
3097 (function() {   
3098
3099     Roo.lib.Anim = {
3100         scroll : function(el, args, duration, easing, cb, scope) {
3101             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3102         },
3103
3104         motion : function(el, args, duration, easing, cb, scope) {
3105             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3106         },
3107
3108         color : function(el, args, duration, easing, cb, scope) {
3109             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3110         },
3111
3112         run : function(el, args, duration, easing, cb, scope, type) {
3113             type = type || Roo.lib.AnimBase;
3114             if (typeof easing == "string") {
3115                 easing = Roo.lib.Easing[easing];
3116             }
3117             var anim = new type(el, args, duration, easing);
3118             anim.animateX(function() {
3119                 Roo.callback(cb, scope);
3120             });
3121             return anim;
3122         }
3123     };
3124 })();/*
3125  * Portions of this file are based on pieces of Yahoo User Interface Library
3126  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3127  * YUI licensed under the BSD License:
3128  * http://developer.yahoo.net/yui/license.txt
3129  * <script type="text/javascript">
3130  *
3131  */
3132
3133 (function() {    
3134     var libFlyweight;
3135     
3136     function fly(el) {
3137         if (!libFlyweight) {
3138             libFlyweight = new Roo.Element.Flyweight();
3139         }
3140         libFlyweight.dom = el;
3141         return libFlyweight;
3142     }
3143
3144     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3145     
3146    
3147     
3148     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3149         if (el) {
3150             this.init(el, attributes, duration, method);
3151         }
3152     };
3153
3154     Roo.lib.AnimBase.fly = fly;
3155     
3156     
3157     
3158     Roo.lib.AnimBase.prototype = {
3159
3160         toString: function() {
3161             var el = this.getEl();
3162             var id = el.id || el.tagName;
3163             return ("Anim " + id);
3164         },
3165
3166         patterns: {
3167             noNegatives:        /width|height|opacity|padding/i,
3168             offsetAttribute:  /^((width|height)|(top|left))$/,
3169             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3170             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3171         },
3172
3173
3174         doMethod: function(attr, start, end) {
3175             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3176         },
3177
3178
3179         setAttribute: function(attr, val, unit) {
3180             if (this.patterns.noNegatives.test(attr)) {
3181                 val = (val > 0) ? val : 0;
3182             }
3183
3184             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3185         },
3186
3187
3188         getAttribute: function(attr) {
3189             var el = this.getEl();
3190             var val = fly(el).getStyle(attr);
3191
3192             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3193                 return parseFloat(val);
3194             }
3195
3196             var a = this.patterns.offsetAttribute.exec(attr) || [];
3197             var pos = !!( a[3] );
3198             var box = !!( a[2] );
3199
3200
3201             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3202                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3203             } else {
3204                 val = 0;
3205             }
3206
3207             return val;
3208         },
3209
3210
3211         getDefaultUnit: function(attr) {
3212             if (this.patterns.defaultUnit.test(attr)) {
3213                 return 'px';
3214             }
3215
3216             return '';
3217         },
3218
3219         animateX : function(callback, scope) {
3220             var f = function() {
3221                 this.onComplete.removeListener(f);
3222                 if (typeof callback == "function") {
3223                     callback.call(scope || this, this);
3224                 }
3225             };
3226             this.onComplete.addListener(f, this);
3227             this.animate();
3228         },
3229
3230
3231         setRuntimeAttribute: function(attr) {
3232             var start;
3233             var end;
3234             var attributes = this.attributes;
3235
3236             this.runtimeAttributes[attr] = {};
3237
3238             var isset = function(prop) {
3239                 return (typeof prop !== 'undefined');
3240             };
3241
3242             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3243                 return false;
3244             }
3245
3246             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3247
3248
3249             if (isset(attributes[attr]['to'])) {
3250                 end = attributes[attr]['to'];
3251             } else if (isset(attributes[attr]['by'])) {
3252                 if (start.constructor == Array) {
3253                     end = [];
3254                     for (var i = 0, len = start.length; i < len; ++i) {
3255                         end[i] = start[i] + attributes[attr]['by'][i];
3256                     }
3257                 } else {
3258                     end = start + attributes[attr]['by'];
3259                 }
3260             }
3261
3262             this.runtimeAttributes[attr].start = start;
3263             this.runtimeAttributes[attr].end = end;
3264
3265
3266             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3267         },
3268
3269
3270         init: function(el, attributes, duration, method) {
3271
3272             var isAnimated = false;
3273
3274
3275             var startTime = null;
3276
3277
3278             var actualFrames = 0;
3279
3280
3281             el = Roo.getDom(el);
3282
3283
3284             this.attributes = attributes || {};
3285
3286
3287             this.duration = duration || 1;
3288
3289
3290             this.method = method || Roo.lib.Easing.easeNone;
3291
3292
3293             this.useSeconds = true;
3294
3295
3296             this.currentFrame = 0;
3297
3298
3299             this.totalFrames = Roo.lib.AnimMgr.fps;
3300
3301
3302             this.getEl = function() {
3303                 return el;
3304             };
3305
3306
3307             this.isAnimated = function() {
3308                 return isAnimated;
3309             };
3310
3311
3312             this.getStartTime = function() {
3313                 return startTime;
3314             };
3315
3316             this.runtimeAttributes = {};
3317
3318
3319             this.animate = function() {
3320                 if (this.isAnimated()) {
3321                     return false;
3322                 }
3323
3324                 this.currentFrame = 0;
3325
3326                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3327
3328                 Roo.lib.AnimMgr.registerElement(this);
3329             };
3330
3331
3332             this.stop = function(finish) {
3333                 if (finish) {
3334                     this.currentFrame = this.totalFrames;
3335                     this._onTween.fire();
3336                 }
3337                 Roo.lib.AnimMgr.stop(this);
3338             };
3339
3340             var onStart = function() {
3341                 this.onStart.fire();
3342
3343                 this.runtimeAttributes = {};
3344                 for (var attr in this.attributes) {
3345                     this.setRuntimeAttribute(attr);
3346                 }
3347
3348                 isAnimated = true;
3349                 actualFrames = 0;
3350                 startTime = new Date();
3351             };
3352
3353
3354             var onTween = function() {
3355                 var data = {
3356                     duration: new Date() - this.getStartTime(),
3357                     currentFrame: this.currentFrame
3358                 };
3359
3360                 data.toString = function() {
3361                     return (
3362                             'duration: ' + data.duration +
3363                             ', currentFrame: ' + data.currentFrame
3364                             );
3365                 };
3366
3367                 this.onTween.fire(data);
3368
3369                 var runtimeAttributes = this.runtimeAttributes;
3370
3371                 for (var attr in runtimeAttributes) {
3372                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3373                 }
3374
3375                 actualFrames += 1;
3376             };
3377
3378             var onComplete = function() {
3379                 var actual_duration = (new Date() - startTime) / 1000 ;
3380
3381                 var data = {
3382                     duration: actual_duration,
3383                     frames: actualFrames,
3384                     fps: actualFrames / actual_duration
3385                 };
3386
3387                 data.toString = function() {
3388                     return (
3389                             'duration: ' + data.duration +
3390                             ', frames: ' + data.frames +
3391                             ', fps: ' + data.fps
3392                             );
3393                 };
3394
3395                 isAnimated = false;
3396                 actualFrames = 0;
3397                 this.onComplete.fire(data);
3398             };
3399
3400
3401             this._onStart = new Roo.util.Event(this);
3402             this.onStart = new Roo.util.Event(this);
3403             this.onTween = new Roo.util.Event(this);
3404             this._onTween = new Roo.util.Event(this);
3405             this.onComplete = new Roo.util.Event(this);
3406             this._onComplete = new Roo.util.Event(this);
3407             this._onStart.addListener(onStart);
3408             this._onTween.addListener(onTween);
3409             this._onComplete.addListener(onComplete);
3410         }
3411     };
3412 })();
3413 /*
3414  * Portions of this file are based on pieces of Yahoo User Interface Library
3415  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3416  * YUI licensed under the BSD License:
3417  * http://developer.yahoo.net/yui/license.txt
3418  * <script type="text/javascript">
3419  *
3420  */
3421
3422 Roo.lib.AnimMgr = new function() {
3423
3424     var thread = null;
3425
3426
3427     var queue = [];
3428
3429
3430     var tweenCount = 0;
3431
3432
3433     this.fps = 1000;
3434
3435
3436     this.delay = 1;
3437
3438
3439     this.registerElement = function(tween) {
3440         queue[queue.length] = tween;
3441         tweenCount += 1;
3442         tween._onStart.fire();
3443         this.start();
3444     };
3445
3446
3447     this.unRegister = function(tween, index) {
3448         tween._onComplete.fire();
3449         index = index || getIndex(tween);
3450         if (index != -1) {
3451             queue.splice(index, 1);
3452         }
3453
3454         tweenCount -= 1;
3455         if (tweenCount <= 0) {
3456             this.stop();
3457         }
3458     };
3459
3460
3461     this.start = function() {
3462         if (thread === null) {
3463             thread = setInterval(this.run, this.delay);
3464         }
3465     };
3466
3467
3468     this.stop = function(tween) {
3469         if (!tween) {
3470             clearInterval(thread);
3471
3472             for (var i = 0, len = queue.length; i < len; ++i) {
3473                 if (queue[0].isAnimated()) {
3474                     this.unRegister(queue[0], 0);
3475                 }
3476             }
3477
3478             queue = [];
3479             thread = null;
3480             tweenCount = 0;
3481         }
3482         else {
3483             this.unRegister(tween);
3484         }
3485     };
3486
3487
3488     this.run = function() {
3489         for (var i = 0, len = queue.length; i < len; ++i) {
3490             var tween = queue[i];
3491             if (!tween || !tween.isAnimated()) {
3492                 continue;
3493             }
3494
3495             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3496             {
3497                 tween.currentFrame += 1;
3498
3499                 if (tween.useSeconds) {
3500                     correctFrame(tween);
3501                 }
3502                 tween._onTween.fire();
3503             }
3504             else {
3505                 Roo.lib.AnimMgr.stop(tween, i);
3506             }
3507         }
3508     };
3509
3510     var getIndex = function(anim) {
3511         for (var i = 0, len = queue.length; i < len; ++i) {
3512             if (queue[i] == anim) {
3513                 return i;
3514             }
3515         }
3516         return -1;
3517     };
3518
3519
3520     var correctFrame = function(tween) {
3521         var frames = tween.totalFrames;
3522         var frame = tween.currentFrame;
3523         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3524         var elapsed = (new Date() - tween.getStartTime());
3525         var tweak = 0;
3526
3527         if (elapsed < tween.duration * 1000) {
3528             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3529         } else {
3530             tweak = frames - (frame + 1);
3531         }
3532         if (tweak > 0 && isFinite(tweak)) {
3533             if (tween.currentFrame + tweak >= frames) {
3534                 tweak = frames - (frame + 1);
3535             }
3536
3537             tween.currentFrame += tweak;
3538         }
3539     };
3540 };
3541
3542     /*
3543  * Portions of this file are based on pieces of Yahoo User Interface Library
3544  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3545  * YUI licensed under the BSD License:
3546  * http://developer.yahoo.net/yui/license.txt
3547  * <script type="text/javascript">
3548  *
3549  */
3550 Roo.lib.Bezier = new function() {
3551
3552         this.getPosition = function(points, t) {
3553             var n = points.length;
3554             var tmp = [];
3555
3556             for (var i = 0; i < n; ++i) {
3557                 tmp[i] = [points[i][0], points[i][1]];
3558             }
3559
3560             for (var j = 1; j < n; ++j) {
3561                 for (i = 0; i < n - j; ++i) {
3562                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3563                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3564                 }
3565             }
3566
3567             return [ tmp[0][0], tmp[0][1] ];
3568
3569         };
3570     };/*
3571  * Portions of this file are based on pieces of Yahoo User Interface Library
3572  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3573  * YUI licensed under the BSD License:
3574  * http://developer.yahoo.net/yui/license.txt
3575  * <script type="text/javascript">
3576  *
3577  */
3578 (function() {
3579
3580     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3581         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3582     };
3583
3584     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3585
3586     var fly = Roo.lib.AnimBase.fly;
3587     var Y = Roo.lib;
3588     var superclass = Y.ColorAnim.superclass;
3589     var proto = Y.ColorAnim.prototype;
3590
3591     proto.toString = function() {
3592         var el = this.getEl();
3593         var id = el.id || el.tagName;
3594         return ("ColorAnim " + id);
3595     };
3596
3597     proto.patterns.color = /color$/i;
3598     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3599     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3600     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3601     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3602
3603
3604     proto.parseColor = function(s) {
3605         if (s.length == 3) {
3606             return s;
3607         }
3608
3609         var c = this.patterns.hex.exec(s);
3610         if (c && c.length == 4) {
3611             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3612         }
3613
3614         c = this.patterns.rgb.exec(s);
3615         if (c && c.length == 4) {
3616             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3617         }
3618
3619         c = this.patterns.hex3.exec(s);
3620         if (c && c.length == 4) {
3621             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3622         }
3623
3624         return null;
3625     };
3626     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3627     proto.getAttribute = function(attr) {
3628         var el = this.getEl();
3629         if (this.patterns.color.test(attr)) {
3630             var val = fly(el).getStyle(attr);
3631
3632             if (this.patterns.transparent.test(val)) {
3633                 var parent = el.parentNode;
3634                 val = fly(parent).getStyle(attr);
3635
3636                 while (parent && this.patterns.transparent.test(val)) {
3637                     parent = parent.parentNode;
3638                     val = fly(parent).getStyle(attr);
3639                     if (parent.tagName.toUpperCase() == 'HTML') {
3640                         val = '#fff';
3641                     }
3642                 }
3643             }
3644         } else {
3645             val = superclass.getAttribute.call(this, attr);
3646         }
3647
3648         return val;
3649     };
3650     proto.getAttribute = function(attr) {
3651         var el = this.getEl();
3652         if (this.patterns.color.test(attr)) {
3653             var val = fly(el).getStyle(attr);
3654
3655             if (this.patterns.transparent.test(val)) {
3656                 var parent = el.parentNode;
3657                 val = fly(parent).getStyle(attr);
3658
3659                 while (parent && this.patterns.transparent.test(val)) {
3660                     parent = parent.parentNode;
3661                     val = fly(parent).getStyle(attr);
3662                     if (parent.tagName.toUpperCase() == 'HTML') {
3663                         val = '#fff';
3664                     }
3665                 }
3666             }
3667         } else {
3668             val = superclass.getAttribute.call(this, attr);
3669         }
3670
3671         return val;
3672     };
3673
3674     proto.doMethod = function(attr, start, end) {
3675         var val;
3676
3677         if (this.patterns.color.test(attr)) {
3678             val = [];
3679             for (var i = 0, len = start.length; i < len; ++i) {
3680                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3681             }
3682
3683             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3684         }
3685         else {
3686             val = superclass.doMethod.call(this, attr, start, end);
3687         }
3688
3689         return val;
3690     };
3691
3692     proto.setRuntimeAttribute = function(attr) {
3693         superclass.setRuntimeAttribute.call(this, attr);
3694
3695         if (this.patterns.color.test(attr)) {
3696             var attributes = this.attributes;
3697             var start = this.parseColor(this.runtimeAttributes[attr].start);
3698             var end = this.parseColor(this.runtimeAttributes[attr].end);
3699
3700             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3701                 end = this.parseColor(attributes[attr].by);
3702
3703                 for (var i = 0, len = start.length; i < len; ++i) {
3704                     end[i] = start[i] + end[i];
3705                 }
3706             }
3707
3708             this.runtimeAttributes[attr].start = start;
3709             this.runtimeAttributes[attr].end = end;
3710         }
3711     };
3712 })();
3713
3714 /*
3715  * Portions of this file are based on pieces of Yahoo User Interface Library
3716  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3717  * YUI licensed under the BSD License:
3718  * http://developer.yahoo.net/yui/license.txt
3719  * <script type="text/javascript">
3720  *
3721  */
3722 Roo.lib.Easing = {
3723
3724
3725     easeNone: function (t, b, c, d) {
3726         return c * t / d + b;
3727     },
3728
3729
3730     easeIn: function (t, b, c, d) {
3731         return c * (t /= d) * t + b;
3732     },
3733
3734
3735     easeOut: function (t, b, c, d) {
3736         return -c * (t /= d) * (t - 2) + b;
3737     },
3738
3739
3740     easeBoth: function (t, b, c, d) {
3741         if ((t /= d / 2) < 1) {
3742             return c / 2 * t * t + b;
3743         }
3744
3745         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3746     },
3747
3748
3749     easeInStrong: function (t, b, c, d) {
3750         return c * (t /= d) * t * t * t + b;
3751     },
3752
3753
3754     easeOutStrong: function (t, b, c, d) {
3755         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3756     },
3757
3758
3759     easeBothStrong: function (t, b, c, d) {
3760         if ((t /= d / 2) < 1) {
3761             return c / 2 * t * t * t * t + b;
3762         }
3763
3764         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3765     },
3766
3767
3768
3769     elasticIn: function (t, b, c, d, a, p) {
3770         if (t == 0) {
3771             return b;
3772         }
3773         if ((t /= d) == 1) {
3774             return b + c;
3775         }
3776         if (!p) {
3777             p = d * .3;
3778         }
3779
3780         if (!a || a < Math.abs(c)) {
3781             a = c;
3782             var s = p / 4;
3783         }
3784         else {
3785             var s = p / (2 * Math.PI) * Math.asin(c / a);
3786         }
3787
3788         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789     },
3790
3791
3792     elasticOut: function (t, b, c, d, a, p) {
3793         if (t == 0) {
3794             return b;
3795         }
3796         if ((t /= d) == 1) {
3797             return b + c;
3798         }
3799         if (!p) {
3800             p = d * .3;
3801         }
3802
3803         if (!a || a < Math.abs(c)) {
3804             a = c;
3805             var s = p / 4;
3806         }
3807         else {
3808             var s = p / (2 * Math.PI) * Math.asin(c / a);
3809         }
3810
3811         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3812     },
3813
3814
3815     elasticBoth: function (t, b, c, d, a, p) {
3816         if (t == 0) {
3817             return b;
3818         }
3819
3820         if ((t /= d / 2) == 2) {
3821             return b + c;
3822         }
3823
3824         if (!p) {
3825             p = d * (.3 * 1.5);
3826         }
3827
3828         if (!a || a < Math.abs(c)) {
3829             a = c;
3830             var s = p / 4;
3831         }
3832         else {
3833             var s = p / (2 * Math.PI) * Math.asin(c / a);
3834         }
3835
3836         if (t < 1) {
3837             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3838                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3839         }
3840         return a * Math.pow(2, -10 * (t -= 1)) *
3841                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3842     },
3843
3844
3845
3846     backIn: function (t, b, c, d, s) {
3847         if (typeof s == 'undefined') {
3848             s = 1.70158;
3849         }
3850         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3851     },
3852
3853
3854     backOut: function (t, b, c, d, s) {
3855         if (typeof s == 'undefined') {
3856             s = 1.70158;
3857         }
3858         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3859     },
3860
3861
3862     backBoth: function (t, b, c, d, s) {
3863         if (typeof s == 'undefined') {
3864             s = 1.70158;
3865         }
3866
3867         if ((t /= d / 2 ) < 1) {
3868             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3869         }
3870         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3871     },
3872
3873
3874     bounceIn: function (t, b, c, d) {
3875         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3876     },
3877
3878
3879     bounceOut: function (t, b, c, d) {
3880         if ((t /= d) < (1 / 2.75)) {
3881             return c * (7.5625 * t * t) + b;
3882         } else if (t < (2 / 2.75)) {
3883             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3884         } else if (t < (2.5 / 2.75)) {
3885             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3886         }
3887         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3888     },
3889
3890
3891     bounceBoth: function (t, b, c, d) {
3892         if (t < d / 2) {
3893             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3894         }
3895         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3896     }
3897 };/*
3898  * Portions of this file are based on pieces of Yahoo User Interface Library
3899  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3900  * YUI licensed under the BSD License:
3901  * http://developer.yahoo.net/yui/license.txt
3902  * <script type="text/javascript">
3903  *
3904  */
3905     (function() {
3906         Roo.lib.Motion = function(el, attributes, duration, method) {
3907             if (el) {
3908                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3909             }
3910         };
3911
3912         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3913
3914
3915         var Y = Roo.lib;
3916         var superclass = Y.Motion.superclass;
3917         var proto = Y.Motion.prototype;
3918
3919         proto.toString = function() {
3920             var el = this.getEl();
3921             var id = el.id || el.tagName;
3922             return ("Motion " + id);
3923         };
3924
3925         proto.patterns.points = /^points$/i;
3926
3927         proto.setAttribute = function(attr, val, unit) {
3928             if (this.patterns.points.test(attr)) {
3929                 unit = unit || 'px';
3930                 superclass.setAttribute.call(this, 'left', val[0], unit);
3931                 superclass.setAttribute.call(this, 'top', val[1], unit);
3932             } else {
3933                 superclass.setAttribute.call(this, attr, val, unit);
3934             }
3935         };
3936
3937         proto.getAttribute = function(attr) {
3938             if (this.patterns.points.test(attr)) {
3939                 var val = [
3940                         superclass.getAttribute.call(this, 'left'),
3941                         superclass.getAttribute.call(this, 'top')
3942                         ];
3943             } else {
3944                 val = superclass.getAttribute.call(this, attr);
3945             }
3946
3947             return val;
3948         };
3949
3950         proto.doMethod = function(attr, start, end) {
3951             var val = null;
3952
3953             if (this.patterns.points.test(attr)) {
3954                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3955                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3956             } else {
3957                 val = superclass.doMethod.call(this, attr, start, end);
3958             }
3959             return val;
3960         };
3961
3962         proto.setRuntimeAttribute = function(attr) {
3963             if (this.patterns.points.test(attr)) {
3964                 var el = this.getEl();
3965                 var attributes = this.attributes;
3966                 var start;
3967                 var control = attributes['points']['control'] || [];
3968                 var end;
3969                 var i, len;
3970
3971                 if (control.length > 0 && !(control[0] instanceof Array)) {
3972                     control = [control];
3973                 } else {
3974                     var tmp = [];
3975                     for (i = 0,len = control.length; i < len; ++i) {
3976                         tmp[i] = control[i];
3977                     }
3978                     control = tmp;
3979                 }
3980
3981                 Roo.fly(el).position();
3982
3983                 if (isset(attributes['points']['from'])) {
3984                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3985                 }
3986                 else {
3987                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3988                 }
3989
3990                 start = this.getAttribute('points');
3991
3992
3993                 if (isset(attributes['points']['to'])) {
3994                     end = translateValues.call(this, attributes['points']['to'], start);
3995
3996                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3997                     for (i = 0,len = control.length; i < len; ++i) {
3998                         control[i] = translateValues.call(this, control[i], start);
3999                     }
4000
4001
4002                 } else if (isset(attributes['points']['by'])) {
4003                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4004
4005                     for (i = 0,len = control.length; i < len; ++i) {
4006                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4007                     }
4008                 }
4009
4010                 this.runtimeAttributes[attr] = [start];
4011
4012                 if (control.length > 0) {
4013                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4014                 }
4015
4016                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4017             }
4018             else {
4019                 superclass.setRuntimeAttribute.call(this, attr);
4020             }
4021         };
4022
4023         var translateValues = function(val, start) {
4024             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4025             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4026
4027             return val;
4028         };
4029
4030         var isset = function(prop) {
4031             return (typeof prop !== 'undefined');
4032         };
4033     })();
4034 /*
4035  * Portions of this file are based on pieces of Yahoo User Interface Library
4036  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4037  * YUI licensed under the BSD License:
4038  * http://developer.yahoo.net/yui/license.txt
4039  * <script type="text/javascript">
4040  *
4041  */
4042     (function() {
4043         Roo.lib.Scroll = function(el, attributes, duration, method) {
4044             if (el) {
4045                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4046             }
4047         };
4048
4049         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4050
4051
4052         var Y = Roo.lib;
4053         var superclass = Y.Scroll.superclass;
4054         var proto = Y.Scroll.prototype;
4055
4056         proto.toString = function() {
4057             var el = this.getEl();
4058             var id = el.id || el.tagName;
4059             return ("Scroll " + id);
4060         };
4061
4062         proto.doMethod = function(attr, start, end) {
4063             var val = null;
4064
4065             if (attr == 'scroll') {
4066                 val = [
4067                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4068                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4069                         ];
4070
4071             } else {
4072                 val = superclass.doMethod.call(this, attr, start, end);
4073             }
4074             return val;
4075         };
4076
4077         proto.getAttribute = function(attr) {
4078             var val = null;
4079             var el = this.getEl();
4080
4081             if (attr == 'scroll') {
4082                 val = [ el.scrollLeft, el.scrollTop ];
4083             } else {
4084                 val = superclass.getAttribute.call(this, attr);
4085             }
4086
4087             return val;
4088         };
4089
4090         proto.setAttribute = function(attr, val, unit) {
4091             var el = this.getEl();
4092
4093             if (attr == 'scroll') {
4094                 el.scrollLeft = val[0];
4095                 el.scrollTop = val[1];
4096             } else {
4097                 superclass.setAttribute.call(this, attr, val, unit);
4098             }
4099         };
4100     })();
4101 /*
4102  * Based on:
4103  * Ext JS Library 1.1.1
4104  * Copyright(c) 2006-2007, Ext JS, LLC.
4105  *
4106  * Originally Released Under LGPL - original licence link has changed is not relivant.
4107  *
4108  * Fork - LGPL
4109  * <script type="text/javascript">
4110  */
4111
4112
4113 // nasty IE9 hack - what a pile of crap that is..
4114
4115  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4116     Range.prototype.createContextualFragment = function (html) {
4117         var doc = window.document;
4118         var container = doc.createElement("div");
4119         container.innerHTML = html;
4120         var frag = doc.createDocumentFragment(), n;
4121         while ((n = container.firstChild)) {
4122             frag.appendChild(n);
4123         }
4124         return frag;
4125     };
4126 }
4127
4128 /**
4129  * @class Roo.DomHelper
4130  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4131  * 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>.
4132  * @singleton
4133  */
4134 Roo.DomHelper = function(){
4135     var tempTableEl = null;
4136     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4137     var tableRe = /^table|tbody|tr|td$/i;
4138     var xmlns = {};
4139     // build as innerHTML where available
4140     /** @ignore */
4141     var createHtml = function(o){
4142         if(typeof o == 'string'){
4143             return o;
4144         }
4145         var b = "";
4146         if(!o.tag){
4147             o.tag = "div";
4148         }
4149         b += "<" + o.tag;
4150         for(var attr in o){
4151             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4152             if(attr == "style"){
4153                 var s = o["style"];
4154                 if(typeof s == "function"){
4155                     s = s.call();
4156                 }
4157                 if(typeof s == "string"){
4158                     b += ' style="' + s + '"';
4159                 }else if(typeof s == "object"){
4160                     b += ' style="';
4161                     for(var key in s){
4162                         if(typeof s[key] != "function"){
4163                             b += key + ":" + s[key] + ";";
4164                         }
4165                     }
4166                     b += '"';
4167                 }
4168             }else{
4169                 if(attr == "cls"){
4170                     b += ' class="' + o["cls"] + '"';
4171                 }else if(attr == "htmlFor"){
4172                     b += ' for="' + o["htmlFor"] + '"';
4173                 }else{
4174                     b += " " + attr + '="' + o[attr] + '"';
4175                 }
4176             }
4177         }
4178         if(emptyTags.test(o.tag)){
4179             b += "/>";
4180         }else{
4181             b += ">";
4182             var cn = o.children || o.cn;
4183             if(cn){
4184                 //http://bugs.kde.org/show_bug.cgi?id=71506
4185                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4186                     for(var i = 0, len = cn.length; i < len; i++) {
4187                         b += createHtml(cn[i], b);
4188                     }
4189                 }else{
4190                     b += createHtml(cn, b);
4191                 }
4192             }
4193             if(o.html){
4194                 b += o.html;
4195             }
4196             b += "</" + o.tag + ">";
4197         }
4198         return b;
4199     };
4200
4201     // build as dom
4202     /** @ignore */
4203     var createDom = function(o, parentNode){
4204          
4205         // defininition craeted..
4206         var ns = false;
4207         if (o.ns && o.ns != 'html') {
4208                
4209             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4210                 xmlns[o.ns] = o.xmlns;
4211                 ns = o.xmlns;
4212             }
4213             if (typeof(xmlns[o.ns]) == 'undefined') {
4214                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4215             }
4216             ns = xmlns[o.ns];
4217         }
4218         
4219         
4220         if (typeof(o) == 'string') {
4221             return parentNode.appendChild(document.createTextNode(o));
4222         }
4223         o.tag = o.tag || div;
4224         if (o.ns && Roo.isIE) {
4225             ns = false;
4226             o.tag = o.ns + ':' + o.tag;
4227             
4228         }
4229         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4230         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4231         for(var attr in o){
4232             
4233             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4234                     attr == "style" || typeof o[attr] == "function") continue;
4235                     
4236             if(attr=="cls" && Roo.isIE){
4237                 el.className = o["cls"];
4238             }else{
4239                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4240                 else el[attr] = o[attr];
4241             }
4242         }
4243         Roo.DomHelper.applyStyles(el, o.style);
4244         var cn = o.children || o.cn;
4245         if(cn){
4246             //http://bugs.kde.org/show_bug.cgi?id=71506
4247              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4248                 for(var i = 0, len = cn.length; i < len; i++) {
4249                     createDom(cn[i], el);
4250                 }
4251             }else{
4252                 createDom(cn, el);
4253             }
4254         }
4255         if(o.html){
4256             el.innerHTML = o.html;
4257         }
4258         if(parentNode){
4259            parentNode.appendChild(el);
4260         }
4261         return el;
4262     };
4263
4264     var ieTable = function(depth, s, h, e){
4265         tempTableEl.innerHTML = [s, h, e].join('');
4266         var i = -1, el = tempTableEl;
4267         while(++i < depth){
4268             el = el.firstChild;
4269         }
4270         return el;
4271     };
4272
4273     // kill repeat to save bytes
4274     var ts = '<table>',
4275         te = '</table>',
4276         tbs = ts+'<tbody>',
4277         tbe = '</tbody>'+te,
4278         trs = tbs + '<tr>',
4279         tre = '</tr>'+tbe;
4280
4281     /**
4282      * @ignore
4283      * Nasty code for IE's broken table implementation
4284      */
4285     var insertIntoTable = function(tag, where, el, html){
4286         if(!tempTableEl){
4287             tempTableEl = document.createElement('div');
4288         }
4289         var node;
4290         var before = null;
4291         if(tag == 'td'){
4292             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4293                 return;
4294             }
4295             if(where == 'beforebegin'){
4296                 before = el;
4297                 el = el.parentNode;
4298             } else{
4299                 before = el.nextSibling;
4300                 el = el.parentNode;
4301             }
4302             node = ieTable(4, trs, html, tre);
4303         }
4304         else if(tag == 'tr'){
4305             if(where == 'beforebegin'){
4306                 before = el;
4307                 el = el.parentNode;
4308                 node = ieTable(3, tbs, html, tbe);
4309             } else if(where == 'afterend'){
4310                 before = el.nextSibling;
4311                 el = el.parentNode;
4312                 node = ieTable(3, tbs, html, tbe);
4313             } else{ // INTO a TR
4314                 if(where == 'afterbegin'){
4315                     before = el.firstChild;
4316                 }
4317                 node = ieTable(4, trs, html, tre);
4318             }
4319         } else if(tag == 'tbody'){
4320             if(where == 'beforebegin'){
4321                 before = el;
4322                 el = el.parentNode;
4323                 node = ieTable(2, ts, html, te);
4324             } else if(where == 'afterend'){
4325                 before = el.nextSibling;
4326                 el = el.parentNode;
4327                 node = ieTable(2, ts, html, te);
4328             } else{
4329                 if(where == 'afterbegin'){
4330                     before = el.firstChild;
4331                 }
4332                 node = ieTable(3, tbs, html, tbe);
4333             }
4334         } else{ // TABLE
4335             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4336                 return;
4337             }
4338             if(where == 'afterbegin'){
4339                 before = el.firstChild;
4340             }
4341             node = ieTable(2, ts, html, te);
4342         }
4343         el.insertBefore(node, before);
4344         return node;
4345     };
4346
4347     return {
4348     /** True to force the use of DOM instead of html fragments @type Boolean */
4349     useDom : false,
4350
4351     /**
4352      * Returns the markup for the passed Element(s) config
4353      * @param {Object} o The Dom object spec (and children)
4354      * @return {String}
4355      */
4356     markup : function(o){
4357         return createHtml(o);
4358     },
4359
4360     /**
4361      * Applies a style specification to an element
4362      * @param {String/HTMLElement} el The element to apply styles to
4363      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4364      * a function which returns such a specification.
4365      */
4366     applyStyles : function(el, styles){
4367         if(styles){
4368            el = Roo.fly(el);
4369            if(typeof styles == "string"){
4370                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4371                var matches;
4372                while ((matches = re.exec(styles)) != null){
4373                    el.setStyle(matches[1], matches[2]);
4374                }
4375            }else if (typeof styles == "object"){
4376                for (var style in styles){
4377                   el.setStyle(style, styles[style]);
4378                }
4379            }else if (typeof styles == "function"){
4380                 Roo.DomHelper.applyStyles(el, styles.call());
4381            }
4382         }
4383     },
4384
4385     /**
4386      * Inserts an HTML fragment into the Dom
4387      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4388      * @param {HTMLElement} el The context element
4389      * @param {String} html The HTML fragmenet
4390      * @return {HTMLElement} The new node
4391      */
4392     insertHtml : function(where, el, html){
4393         where = where.toLowerCase();
4394         if(el.insertAdjacentHTML){
4395             if(tableRe.test(el.tagName)){
4396                 var rs;
4397                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4398                     return rs;
4399                 }
4400             }
4401             switch(where){
4402                 case "beforebegin":
4403                     el.insertAdjacentHTML('BeforeBegin', html);
4404                     return el.previousSibling;
4405                 case "afterbegin":
4406                     el.insertAdjacentHTML('AfterBegin', html);
4407                     return el.firstChild;
4408                 case "beforeend":
4409                     el.insertAdjacentHTML('BeforeEnd', html);
4410                     return el.lastChild;
4411                 case "afterend":
4412                     el.insertAdjacentHTML('AfterEnd', html);
4413                     return el.nextSibling;
4414             }
4415             throw 'Illegal insertion point -> "' + where + '"';
4416         }
4417         var range = el.ownerDocument.createRange();
4418         var frag;
4419         switch(where){
4420              case "beforebegin":
4421                 range.setStartBefore(el);
4422                 frag = range.createContextualFragment(html);
4423                 el.parentNode.insertBefore(frag, el);
4424                 return el.previousSibling;
4425              case "afterbegin":
4426                 if(el.firstChild){
4427                     range.setStartBefore(el.firstChild);
4428                     frag = range.createContextualFragment(html);
4429                     el.insertBefore(frag, el.firstChild);
4430                     return el.firstChild;
4431                 }else{
4432                     el.innerHTML = html;
4433                     return el.firstChild;
4434                 }
4435             case "beforeend":
4436                 if(el.lastChild){
4437                     range.setStartAfter(el.lastChild);
4438                     frag = range.createContextualFragment(html);
4439                     el.appendChild(frag);
4440                     return el.lastChild;
4441                 }else{
4442                     el.innerHTML = html;
4443                     return el.lastChild;
4444                 }
4445             case "afterend":
4446                 range.setStartAfter(el);
4447                 frag = range.createContextualFragment(html);
4448                 el.parentNode.insertBefore(frag, el.nextSibling);
4449                 return el.nextSibling;
4450             }
4451             throw 'Illegal insertion point -> "' + where + '"';
4452     },
4453
4454     /**
4455      * Creates new Dom element(s) and inserts them before el
4456      * @param {String/HTMLElement/Element} el The context element
4457      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4458      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4459      * @return {HTMLElement/Roo.Element} The new node
4460      */
4461     insertBefore : function(el, o, returnElement){
4462         return this.doInsert(el, o, returnElement, "beforeBegin");
4463     },
4464
4465     /**
4466      * Creates new Dom element(s) and inserts them after el
4467      * @param {String/HTMLElement/Element} el The context element
4468      * @param {Object} o The Dom object spec (and children)
4469      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4470      * @return {HTMLElement/Roo.Element} The new node
4471      */
4472     insertAfter : function(el, o, returnElement){
4473         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4474     },
4475
4476     /**
4477      * Creates new Dom element(s) and inserts them as the first child of el
4478      * @param {String/HTMLElement/Element} el The context element
4479      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4480      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4481      * @return {HTMLElement/Roo.Element} The new node
4482      */
4483     insertFirst : function(el, o, returnElement){
4484         return this.doInsert(el, o, returnElement, "afterBegin");
4485     },
4486
4487     // private
4488     doInsert : function(el, o, returnElement, pos, sibling){
4489         el = Roo.getDom(el);
4490         var newNode;
4491         if(this.useDom || o.ns){
4492             newNode = createDom(o, null);
4493             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4494         }else{
4495             var html = createHtml(o);
4496             newNode = this.insertHtml(pos, el, html);
4497         }
4498         return returnElement ? Roo.get(newNode, true) : newNode;
4499     },
4500
4501     /**
4502      * Creates new Dom element(s) and appends them to el
4503      * @param {String/HTMLElement/Element} el The context element
4504      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4505      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4506      * @return {HTMLElement/Roo.Element} The new node
4507      */
4508     append : function(el, o, returnElement){
4509         el = Roo.getDom(el);
4510         var newNode;
4511         if(this.useDom || o.ns){
4512             newNode = createDom(o, null);
4513             el.appendChild(newNode);
4514         }else{
4515             var html = createHtml(o);
4516             newNode = this.insertHtml("beforeEnd", el, html);
4517         }
4518         return returnElement ? Roo.get(newNode, true) : newNode;
4519     },
4520
4521     /**
4522      * Creates new Dom element(s) and overwrites the contents of el with them
4523      * @param {String/HTMLElement/Element} el The context element
4524      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4525      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4526      * @return {HTMLElement/Roo.Element} The new node
4527      */
4528     overwrite : function(el, o, returnElement){
4529         el = Roo.getDom(el);
4530         if (o.ns) {
4531           
4532             while (el.childNodes.length) {
4533                 el.removeChild(el.firstChild);
4534             }
4535             createDom(o, el);
4536         } else {
4537             el.innerHTML = createHtml(o);   
4538         }
4539         
4540         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4541     },
4542
4543     /**
4544      * Creates a new Roo.DomHelper.Template from the Dom object spec
4545      * @param {Object} o The Dom object spec (and children)
4546      * @return {Roo.DomHelper.Template} The new template
4547      */
4548     createTemplate : function(o){
4549         var html = createHtml(o);
4550         return new Roo.Template(html);
4551     }
4552     };
4553 }();
4554 /*
4555  * Based on:
4556  * Ext JS Library 1.1.1
4557  * Copyright(c) 2006-2007, Ext JS, LLC.
4558  *
4559  * Originally Released Under LGPL - original licence link has changed is not relivant.
4560  *
4561  * Fork - LGPL
4562  * <script type="text/javascript">
4563  */
4564  
4565 /**
4566 * @class Roo.Template
4567 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4568 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4569 * Usage:
4570 <pre><code>
4571 var t = new Roo.Template({
4572     html :  '&lt;div name="{id}"&gt;' + 
4573         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4574         '&lt;/div&gt;',
4575     myformat: function (value, allValues) {
4576         return 'XX' + value;
4577     }
4578 });
4579 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4580 </code></pre>
4581 * For more information see this blog post with examples:
4582 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4583      - Create Elements using DOM, HTML fragments and Templates</a>. 
4584 * @constructor
4585 * @param {Object} cfg - Configuration object.
4586 */
4587 Roo.Template = function(cfg){
4588     // BC!
4589     if(cfg instanceof Array){
4590         cfg = cfg.join("");
4591     }else if(arguments.length > 1){
4592         cfg = Array.prototype.join.call(arguments, "");
4593     }
4594     
4595     
4596     if (typeof(cfg) == 'object') {
4597         Roo.apply(this,cfg)
4598     } else {
4599         // bc
4600         this.html = cfg;
4601     }
4602     if (this.url) {
4603         this.load();
4604     }
4605     
4606 };
4607 Roo.Template.prototype = {
4608     
4609     /**
4610      * @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..
4611      *                    it should be fixed so that template is observable...
4612      */
4613     url : false,
4614     /**
4615      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4616      */
4617     html : '',
4618     /**
4619      * Returns an HTML fragment of this template with the specified values applied.
4620      * @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'})
4621      * @return {String} The HTML fragment
4622      */
4623     applyTemplate : function(values){
4624         try {
4625            
4626             if(this.compiled){
4627                 return this.compiled(values);
4628             }
4629             var useF = this.disableFormats !== true;
4630             var fm = Roo.util.Format, tpl = this;
4631             var fn = function(m, name, format, args){
4632                 if(format && useF){
4633                     if(format.substr(0, 5) == "this."){
4634                         return tpl.call(format.substr(5), values[name], values);
4635                     }else{
4636                         if(args){
4637                             // quoted values are required for strings in compiled templates, 
4638                             // but for non compiled we need to strip them
4639                             // quoted reversed for jsmin
4640                             var re = /^\s*['"](.*)["']\s*$/;
4641                             args = args.split(',');
4642                             for(var i = 0, len = args.length; i < len; i++){
4643                                 args[i] = args[i].replace(re, "$1");
4644                             }
4645                             args = [values[name]].concat(args);
4646                         }else{
4647                             args = [values[name]];
4648                         }
4649                         return fm[format].apply(fm, args);
4650                     }
4651                 }else{
4652                     return values[name] !== undefined ? values[name] : "";
4653                 }
4654             };
4655             return this.html.replace(this.re, fn);
4656         } catch (e) {
4657             Roo.log(e);
4658             throw e;
4659         }
4660          
4661     },
4662     
4663     loading : false,
4664       
4665     load : function ()
4666     {
4667          
4668         if (this.loading) {
4669             return;
4670         }
4671         var _t = this;
4672         
4673         this.loading = true;
4674         this.compiled = false;
4675         
4676         var cx = new Roo.data.Connection();
4677         cx.request({
4678             url : this.url,
4679             method : 'GET',
4680             success : function (response) {
4681                 _t.loading = false;
4682                 _t.html = response.responseText;
4683                 _t.url = false;
4684                 _t.compile();
4685              },
4686             failure : function(response) {
4687                 Roo.log("Template failed to load from " + _t.url);
4688                 _t.loading = false;
4689             }
4690         });
4691     },
4692
4693     /**
4694      * Sets the HTML used as the template and optionally compiles it.
4695      * @param {String} html
4696      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4697      * @return {Roo.Template} this
4698      */
4699     set : function(html, compile){
4700         this.html = html;
4701         this.compiled = null;
4702         if(compile){
4703             this.compile();
4704         }
4705         return this;
4706     },
4707     
4708     /**
4709      * True to disable format functions (defaults to false)
4710      * @type Boolean
4711      */
4712     disableFormats : false,
4713     
4714     /**
4715     * The regular expression used to match template variables 
4716     * @type RegExp
4717     * @property 
4718     */
4719     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4720     
4721     /**
4722      * Compiles the template into an internal function, eliminating the RegEx overhead.
4723      * @return {Roo.Template} this
4724      */
4725     compile : function(){
4726         var fm = Roo.util.Format;
4727         var useF = this.disableFormats !== true;
4728         var sep = Roo.isGecko ? "+" : ",";
4729         var fn = function(m, name, format, args){
4730             if(format && useF){
4731                 args = args ? ',' + args : "";
4732                 if(format.substr(0, 5) != "this."){
4733                     format = "fm." + format + '(';
4734                 }else{
4735                     format = 'this.call("'+ format.substr(5) + '", ';
4736                     args = ", values";
4737                 }
4738             }else{
4739                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4740             }
4741             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4742         };
4743         var body;
4744         // branched to use + in gecko and [].join() in others
4745         if(Roo.isGecko){
4746             body = "this.compiled = function(values){ return '" +
4747                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4748                     "';};";
4749         }else{
4750             body = ["this.compiled = function(values){ return ['"];
4751             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4752             body.push("'].join('');};");
4753             body = body.join('');
4754         }
4755         /**
4756          * eval:var:values
4757          * eval:var:fm
4758          */
4759         eval(body);
4760         return this;
4761     },
4762     
4763     // private function used to call members
4764     call : function(fnName, value, allValues){
4765         return this[fnName](value, allValues);
4766     },
4767     
4768     /**
4769      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4770      * @param {String/HTMLElement/Roo.Element} el The context element
4771      * @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'})
4772      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4773      * @return {HTMLElement/Roo.Element} The new node or Element
4774      */
4775     insertFirst: function(el, values, returnElement){
4776         return this.doInsert('afterBegin', el, values, returnElement);
4777     },
4778
4779     /**
4780      * Applies the supplied values to the template and inserts the new node(s) before el.
4781      * @param {String/HTMLElement/Roo.Element} el The context element
4782      * @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'})
4783      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4784      * @return {HTMLElement/Roo.Element} The new node or Element
4785      */
4786     insertBefore: function(el, values, returnElement){
4787         return this.doInsert('beforeBegin', el, values, returnElement);
4788     },
4789
4790     /**
4791      * Applies the supplied values to the template and inserts the new node(s) after el.
4792      * @param {String/HTMLElement/Roo.Element} el The context element
4793      * @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'})
4794      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4795      * @return {HTMLElement/Roo.Element} The new node or Element
4796      */
4797     insertAfter : function(el, values, returnElement){
4798         return this.doInsert('afterEnd', el, values, returnElement);
4799     },
4800     
4801     /**
4802      * Applies the supplied values to the template and appends the new node(s) to el.
4803      * @param {String/HTMLElement/Roo.Element} el The context element
4804      * @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'})
4805      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4806      * @return {HTMLElement/Roo.Element} The new node or Element
4807      */
4808     append : function(el, values, returnElement){
4809         return this.doInsert('beforeEnd', el, values, returnElement);
4810     },
4811
4812     doInsert : function(where, el, values, returnEl){
4813         el = Roo.getDom(el);
4814         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4815         return returnEl ? Roo.get(newNode, true) : newNode;
4816     },
4817
4818     /**
4819      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4820      * @param {String/HTMLElement/Roo.Element} el The context element
4821      * @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'})
4822      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4823      * @return {HTMLElement/Roo.Element} The new node or Element
4824      */
4825     overwrite : function(el, values, returnElement){
4826         el = Roo.getDom(el);
4827         el.innerHTML = this.applyTemplate(values);
4828         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4829     }
4830 };
4831 /**
4832  * Alias for {@link #applyTemplate}
4833  * @method
4834  */
4835 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4836
4837 // backwards compat
4838 Roo.DomHelper.Template = Roo.Template;
4839
4840 /**
4841  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4842  * @param {String/HTMLElement} el A DOM element or its id
4843  * @returns {Roo.Template} The created template
4844  * @static
4845  */
4846 Roo.Template.from = function(el){
4847     el = Roo.getDom(el);
4848     return new Roo.Template(el.value || el.innerHTML);
4849 };/*
4850  * Based on:
4851  * Ext JS Library 1.1.1
4852  * Copyright(c) 2006-2007, Ext JS, LLC.
4853  *
4854  * Originally Released Under LGPL - original licence link has changed is not relivant.
4855  *
4856  * Fork - LGPL
4857  * <script type="text/javascript">
4858  */
4859  
4860
4861 /*
4862  * This is code is also distributed under MIT license for use
4863  * with jQuery and prototype JavaScript libraries.
4864  */
4865 /**
4866  * @class Roo.DomQuery
4867 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).
4868 <p>
4869 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>
4870
4871 <p>
4872 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.
4873 </p>
4874 <h4>Element Selectors:</h4>
4875 <ul class="list">
4876     <li> <b>*</b> any element</li>
4877     <li> <b>E</b> an element with the tag E</li>
4878     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4879     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4880     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4881     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4882 </ul>
4883 <h4>Attribute Selectors:</h4>
4884 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4885 <ul class="list">
4886     <li> <b>E[foo]</b> has an attribute "foo"</li>
4887     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4888     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4889     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4890     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4891     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4892     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4893 </ul>
4894 <h4>Pseudo Classes:</h4>
4895 <ul class="list">
4896     <li> <b>E:first-child</b> E is the first child of its parent</li>
4897     <li> <b>E:last-child</b> E is the last child of its parent</li>
4898     <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>
4899     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4900     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4901     <li> <b>E:only-child</b> E is the only child of its parent</li>
4902     <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>
4903     <li> <b>E:first</b> the first E in the resultset</li>
4904     <li> <b>E:last</b> the last E in the resultset</li>
4905     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4906     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4907     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4908     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4909     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4910     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4911     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4912     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4913     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4914 </ul>
4915 <h4>CSS Value Selectors:</h4>
4916 <ul class="list">
4917     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4918     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4919     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4920     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4921     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4922     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4923 </ul>
4924  * @singleton
4925  */
4926 Roo.DomQuery = function(){
4927     var cache = {}, simpleCache = {}, valueCache = {};
4928     var nonSpace = /\S/;
4929     var trimRe = /^\s+|\s+$/g;
4930     var tplRe = /\{(\d+)\}/g;
4931     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4932     var tagTokenRe = /^(#)?([\w-\*]+)/;
4933     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4934
4935     function child(p, index){
4936         var i = 0;
4937         var n = p.firstChild;
4938         while(n){
4939             if(n.nodeType == 1){
4940                if(++i == index){
4941                    return n;
4942                }
4943             }
4944             n = n.nextSibling;
4945         }
4946         return null;
4947     };
4948
4949     function next(n){
4950         while((n = n.nextSibling) && n.nodeType != 1);
4951         return n;
4952     };
4953
4954     function prev(n){
4955         while((n = n.previousSibling) && n.nodeType != 1);
4956         return n;
4957     };
4958
4959     function children(d){
4960         var n = d.firstChild, ni = -1;
4961             while(n){
4962                 var nx = n.nextSibling;
4963                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4964                     d.removeChild(n);
4965                 }else{
4966                     n.nodeIndex = ++ni;
4967                 }
4968                 n = nx;
4969             }
4970             return this;
4971         };
4972
4973     function byClassName(c, a, v){
4974         if(!v){
4975             return c;
4976         }
4977         var r = [], ri = -1, cn;
4978         for(var i = 0, ci; ci = c[i]; i++){
4979             if((' '+ci.className+' ').indexOf(v) != -1){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function attrValue(n, attr){
4987         if(!n.tagName && typeof n.length != "undefined"){
4988             n = n[0];
4989         }
4990         if(!n){
4991             return null;
4992         }
4993         if(attr == "for"){
4994             return n.htmlFor;
4995         }
4996         if(attr == "class" || attr == "className"){
4997             return n.className;
4998         }
4999         return n.getAttribute(attr) || n[attr];
5000
5001     };
5002
5003     function getNodes(ns, mode, tagName){
5004         var result = [], ri = -1, cs;
5005         if(!ns){
5006             return result;
5007         }
5008         tagName = tagName || "*";
5009         if(typeof ns.getElementsByTagName != "undefined"){
5010             ns = [ns];
5011         }
5012         if(!mode){
5013             for(var i = 0, ni; ni = ns[i]; i++){
5014                 cs = ni.getElementsByTagName(tagName);
5015                 for(var j = 0, ci; ci = cs[j]; j++){
5016                     result[++ri] = ci;
5017                 }
5018             }
5019         }else if(mode == "/" || mode == ">"){
5020             var utag = tagName.toUpperCase();
5021             for(var i = 0, ni, cn; ni = ns[i]; i++){
5022                 cn = ni.children || ni.childNodes;
5023                 for(var j = 0, cj; cj = cn[j]; j++){
5024                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5025                         result[++ri] = cj;
5026                     }
5027                 }
5028             }
5029         }else if(mode == "+"){
5030             var utag = tagName.toUpperCase();
5031             for(var i = 0, n; n = ns[i]; i++){
5032                 while((n = n.nextSibling) && n.nodeType != 1);
5033                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5034                     result[++ri] = n;
5035                 }
5036             }
5037         }else if(mode == "~"){
5038             for(var i = 0, n; n = ns[i]; i++){
5039                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5040                 if(n){
5041                     result[++ri] = n;
5042                 }
5043             }
5044         }
5045         return result;
5046     };
5047
5048     function concat(a, b){
5049         if(b.slice){
5050             return a.concat(b);
5051         }
5052         for(var i = 0, l = b.length; i < l; i++){
5053             a[a.length] = b[i];
5054         }
5055         return a;
5056     }
5057
5058     function byTag(cs, tagName){
5059         if(cs.tagName || cs == document){
5060             cs = [cs];
5061         }
5062         if(!tagName){
5063             return cs;
5064         }
5065         var r = [], ri = -1;
5066         tagName = tagName.toLowerCase();
5067         for(var i = 0, ci; ci = cs[i]; i++){
5068             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5069                 r[++ri] = ci;
5070             }
5071         }
5072         return r;
5073     };
5074
5075     function byId(cs, attr, id){
5076         if(cs.tagName || cs == document){
5077             cs = [cs];
5078         }
5079         if(!id){
5080             return cs;
5081         }
5082         var r = [], ri = -1;
5083         for(var i = 0,ci; ci = cs[i]; i++){
5084             if(ci && ci.id == id){
5085                 r[++ri] = ci;
5086                 return r;
5087             }
5088         }
5089         return r;
5090     };
5091
5092     function byAttribute(cs, attr, value, op, custom){
5093         var r = [], ri = -1, st = custom=="{";
5094         var f = Roo.DomQuery.operators[op];
5095         for(var i = 0, ci; ci = cs[i]; i++){
5096             var a;
5097             if(st){
5098                 a = Roo.DomQuery.getStyle(ci, attr);
5099             }
5100             else if(attr == "class" || attr == "className"){
5101                 a = ci.className;
5102             }else if(attr == "for"){
5103                 a = ci.htmlFor;
5104             }else if(attr == "href"){
5105                 a = ci.getAttribute("href", 2);
5106             }else{
5107                 a = ci.getAttribute(attr);
5108             }
5109             if((f && f(a, value)) || (!f && a)){
5110                 r[++ri] = ci;
5111             }
5112         }
5113         return r;
5114     };
5115
5116     function byPseudo(cs, name, value){
5117         return Roo.DomQuery.pseudos[name](cs, value);
5118     };
5119
5120     // This is for IE MSXML which does not support expandos.
5121     // IE runs the same speed using setAttribute, however FF slows way down
5122     // and Safari completely fails so they need to continue to use expandos.
5123     var isIE = window.ActiveXObject ? true : false;
5124
5125     // this eval is stop the compressor from
5126     // renaming the variable to something shorter
5127     
5128     /** eval:var:batch */
5129     var batch = 30803; 
5130
5131     var key = 30803;
5132
5133     function nodupIEXml(cs){
5134         var d = ++key;
5135         cs[0].setAttribute("_nodup", d);
5136         var r = [cs[0]];
5137         for(var i = 1, len = cs.length; i < len; i++){
5138             var c = cs[i];
5139             if(!c.getAttribute("_nodup") != d){
5140                 c.setAttribute("_nodup", d);
5141                 r[r.length] = c;
5142             }
5143         }
5144         for(var i = 0, len = cs.length; i < len; i++){
5145             cs[i].removeAttribute("_nodup");
5146         }
5147         return r;
5148     }
5149
5150     function nodup(cs){
5151         if(!cs){
5152             return [];
5153         }
5154         var len = cs.length, c, i, r = cs, cj, ri = -1;
5155         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5156             return cs;
5157         }
5158         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5159             return nodupIEXml(cs);
5160         }
5161         var d = ++key;
5162         cs[0]._nodup = d;
5163         for(i = 1; c = cs[i]; i++){
5164             if(c._nodup != d){
5165                 c._nodup = d;
5166             }else{
5167                 r = [];
5168                 for(var j = 0; j < i; j++){
5169                     r[++ri] = cs[j];
5170                 }
5171                 for(j = i+1; cj = cs[j]; j++){
5172                     if(cj._nodup != d){
5173                         cj._nodup = d;
5174                         r[++ri] = cj;
5175                     }
5176                 }
5177                 return r;
5178             }
5179         }
5180         return r;
5181     }
5182
5183     function quickDiffIEXml(c1, c2){
5184         var d = ++key;
5185         for(var i = 0, len = c1.length; i < len; i++){
5186             c1[i].setAttribute("_qdiff", d);
5187         }
5188         var r = [];
5189         for(var i = 0, len = c2.length; i < len; i++){
5190             if(c2[i].getAttribute("_qdiff") != d){
5191                 r[r.length] = c2[i];
5192             }
5193         }
5194         for(var i = 0, len = c1.length; i < len; i++){
5195            c1[i].removeAttribute("_qdiff");
5196         }
5197         return r;
5198     }
5199
5200     function quickDiff(c1, c2){
5201         var len1 = c1.length;
5202         if(!len1){
5203             return c2;
5204         }
5205         if(isIE && c1[0].selectSingleNode){
5206             return quickDiffIEXml(c1, c2);
5207         }
5208         var d = ++key;
5209         for(var i = 0; i < len1; i++){
5210             c1[i]._qdiff = d;
5211         }
5212         var r = [];
5213         for(var i = 0, len = c2.length; i < len; i++){
5214             if(c2[i]._qdiff != d){
5215                 r[r.length] = c2[i];
5216             }
5217         }
5218         return r;
5219     }
5220
5221     function quickId(ns, mode, root, id){
5222         if(ns == root){
5223            var d = root.ownerDocument || root;
5224            return d.getElementById(id);
5225         }
5226         ns = getNodes(ns, mode, "*");
5227         return byId(ns, null, id);
5228     }
5229
5230     return {
5231         getStyle : function(el, name){
5232             return Roo.fly(el).getStyle(name);
5233         },
5234         /**
5235          * Compiles a selector/xpath query into a reusable function. The returned function
5236          * takes one parameter "root" (optional), which is the context node from where the query should start.
5237          * @param {String} selector The selector/xpath query
5238          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5239          * @return {Function}
5240          */
5241         compile : function(path, type){
5242             type = type || "select";
5243             
5244             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5245             var q = path, mode, lq;
5246             var tk = Roo.DomQuery.matchers;
5247             var tklen = tk.length;
5248             var mm;
5249
5250             // accept leading mode switch
5251             var lmode = q.match(modeRe);
5252             if(lmode && lmode[1]){
5253                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5254                 q = q.replace(lmode[1], "");
5255             }
5256             // strip leading slashes
5257             while(path.substr(0, 1)=="/"){
5258                 path = path.substr(1);
5259             }
5260
5261             while(q && lq != q){
5262                 lq = q;
5263                 var tm = q.match(tagTokenRe);
5264                 if(type == "select"){
5265                     if(tm){
5266                         if(tm[1] == "#"){
5267                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5268                         }else{
5269                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5270                         }
5271                         q = q.replace(tm[0], "");
5272                     }else if(q.substr(0, 1) != '@'){
5273                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5274                     }
5275                 }else{
5276                     if(tm){
5277                         if(tm[1] == "#"){
5278                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5279                         }else{
5280                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5281                         }
5282                         q = q.replace(tm[0], "");
5283                     }
5284                 }
5285                 while(!(mm = q.match(modeRe))){
5286                     var matched = false;
5287                     for(var j = 0; j < tklen; j++){
5288                         var t = tk[j];
5289                         var m = q.match(t.re);
5290                         if(m){
5291                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5292                                                     return m[i];
5293                                                 });
5294                             q = q.replace(m[0], "");
5295                             matched = true;
5296                             break;
5297                         }
5298                     }
5299                     // prevent infinite loop on bad selector
5300                     if(!matched){
5301                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5302                     }
5303                 }
5304                 if(mm[1]){
5305                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5306                     q = q.replace(mm[1], "");
5307                 }
5308             }
5309             fn[fn.length] = "return nodup(n);\n}";
5310             
5311              /** 
5312               * list of variables that need from compression as they are used by eval.
5313              *  eval:var:batch 
5314              *  eval:var:nodup
5315              *  eval:var:byTag
5316              *  eval:var:ById
5317              *  eval:var:getNodes
5318              *  eval:var:quickId
5319              *  eval:var:mode
5320              *  eval:var:root
5321              *  eval:var:n
5322              *  eval:var:byClassName
5323              *  eval:var:byPseudo
5324              *  eval:var:byAttribute
5325              *  eval:var:attrValue
5326              * 
5327              **/ 
5328             eval(fn.join(""));
5329             return f;
5330         },
5331
5332         /**
5333          * Selects a group of elements.
5334          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5335          * @param {Node} root (optional) The start of the query (defaults to document).
5336          * @return {Array}
5337          */
5338         select : function(path, root, type){
5339             if(!root || root == document){
5340                 root = document;
5341             }
5342             if(typeof root == "string"){
5343                 root = document.getElementById(root);
5344             }
5345             var paths = path.split(",");
5346             var results = [];
5347             for(var i = 0, len = paths.length; i < len; i++){
5348                 var p = paths[i].replace(trimRe, "");
5349                 if(!cache[p]){
5350                     cache[p] = Roo.DomQuery.compile(p);
5351                     if(!cache[p]){
5352                         throw p + " is not a valid selector";
5353                     }
5354                 }
5355                 var result = cache[p](root);
5356                 if(result && result != document){
5357                     results = results.concat(result);
5358                 }
5359             }
5360             if(paths.length > 1){
5361                 return nodup(results);
5362             }
5363             return results;
5364         },
5365
5366         /**
5367          * Selects a single element.
5368          * @param {String} selector The selector/xpath query
5369          * @param {Node} root (optional) The start of the query (defaults to document).
5370          * @return {Element}
5371          */
5372         selectNode : function(path, root){
5373             return Roo.DomQuery.select(path, root)[0];
5374         },
5375
5376         /**
5377          * Selects the value of a node, optionally replacing null with the defaultValue.
5378          * @param {String} selector The selector/xpath query
5379          * @param {Node} root (optional) The start of the query (defaults to document).
5380          * @param {String} defaultValue
5381          */
5382         selectValue : function(path, root, defaultValue){
5383             path = path.replace(trimRe, "");
5384             if(!valueCache[path]){
5385                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5386             }
5387             var n = valueCache[path](root);
5388             n = n[0] ? n[0] : n;
5389             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5390             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5391         },
5392
5393         /**
5394          * Selects the value of a node, parsing integers and floats.
5395          * @param {String} selector The selector/xpath query
5396          * @param {Node} root (optional) The start of the query (defaults to document).
5397          * @param {Number} defaultValue
5398          * @return {Number}
5399          */
5400         selectNumber : function(path, root, defaultValue){
5401             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5402             return parseFloat(v);
5403         },
5404
5405         /**
5406          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5407          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5408          * @param {String} selector The simple selector to test
5409          * @return {Boolean}
5410          */
5411         is : function(el, ss){
5412             if(typeof el == "string"){
5413                 el = document.getElementById(el);
5414             }
5415             var isArray = (el instanceof Array);
5416             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5417             return isArray ? (result.length == el.length) : (result.length > 0);
5418         },
5419
5420         /**
5421          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5422          * @param {Array} el An array of elements to filter
5423          * @param {String} selector The simple selector to test
5424          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5425          * the selector instead of the ones that match
5426          * @return {Array}
5427          */
5428         filter : function(els, ss, nonMatches){
5429             ss = ss.replace(trimRe, "");
5430             if(!simpleCache[ss]){
5431                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5432             }
5433             var result = simpleCache[ss](els);
5434             return nonMatches ? quickDiff(result, els) : result;
5435         },
5436
5437         /**
5438          * Collection of matching regular expressions and code snippets.
5439          */
5440         matchers : [{
5441                 re: /^\.([\w-]+)/,
5442                 select: 'n = byClassName(n, null, " {1} ");'
5443             }, {
5444                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5445                 select: 'n = byPseudo(n, "{1}", "{2}");'
5446             },{
5447                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5448                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5449             }, {
5450                 re: /^#([\w-]+)/,
5451                 select: 'n = byId(n, null, "{1}");'
5452             },{
5453                 re: /^@([\w-]+)/,
5454                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5455             }
5456         ],
5457
5458         /**
5459          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5460          * 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;.
5461          */
5462         operators : {
5463             "=" : function(a, v){
5464                 return a == v;
5465             },
5466             "!=" : function(a, v){
5467                 return a != v;
5468             },
5469             "^=" : function(a, v){
5470                 return a && a.substr(0, v.length) == v;
5471             },
5472             "$=" : function(a, v){
5473                 return a && a.substr(a.length-v.length) == v;
5474             },
5475             "*=" : function(a, v){
5476                 return a && a.indexOf(v) !== -1;
5477             },
5478             "%=" : function(a, v){
5479                 return (a % v) == 0;
5480             },
5481             "|=" : function(a, v){
5482                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5483             },
5484             "~=" : function(a, v){
5485                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5486             }
5487         },
5488
5489         /**
5490          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5491          * and the argument (if any) supplied in the selector.
5492          */
5493         pseudos : {
5494             "first-child" : function(c){
5495                 var r = [], ri = -1, n;
5496                 for(var i = 0, ci; ci = n = c[i]; i++){
5497                     while((n = n.previousSibling) && n.nodeType != 1);
5498                     if(!n){
5499                         r[++ri] = ci;
5500                     }
5501                 }
5502                 return r;
5503             },
5504
5505             "last-child" : function(c){
5506                 var r = [], ri = -1, n;
5507                 for(var i = 0, ci; ci = n = c[i]; i++){
5508                     while((n = n.nextSibling) && n.nodeType != 1);
5509                     if(!n){
5510                         r[++ri] = ci;
5511                     }
5512                 }
5513                 return r;
5514             },
5515
5516             "nth-child" : function(c, a) {
5517                 var r = [], ri = -1;
5518                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5519                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5520                 for(var i = 0, n; n = c[i]; i++){
5521                     var pn = n.parentNode;
5522                     if (batch != pn._batch) {
5523                         var j = 0;
5524                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5525                             if(cn.nodeType == 1){
5526                                cn.nodeIndex = ++j;
5527                             }
5528                         }
5529                         pn._batch = batch;
5530                     }
5531                     if (f == 1) {
5532                         if (l == 0 || n.nodeIndex == l){
5533                             r[++ri] = n;
5534                         }
5535                     } else if ((n.nodeIndex + l) % f == 0){
5536                         r[++ri] = n;
5537                     }
5538                 }
5539
5540                 return r;
5541             },
5542
5543             "only-child" : function(c){
5544                 var r = [], ri = -1;;
5545                 for(var i = 0, ci; ci = c[i]; i++){
5546                     if(!prev(ci) && !next(ci)){
5547                         r[++ri] = ci;
5548                     }
5549                 }
5550                 return r;
5551             },
5552
5553             "empty" : function(c){
5554                 var r = [], ri = -1;
5555                 for(var i = 0, ci; ci = c[i]; i++){
5556                     var cns = ci.childNodes, j = 0, cn, empty = true;
5557                     while(cn = cns[j]){
5558                         ++j;
5559                         if(cn.nodeType == 1 || cn.nodeType == 3){
5560                             empty = false;
5561                             break;
5562                         }
5563                     }
5564                     if(empty){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             },
5570
5571             "contains" : function(c, v){
5572                 var r = [], ri = -1;
5573                 for(var i = 0, ci; ci = c[i]; i++){
5574                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5575                         r[++ri] = ci;
5576                     }
5577                 }
5578                 return r;
5579             },
5580
5581             "nodeValue" : function(c, v){
5582                 var r = [], ri = -1;
5583                 for(var i = 0, ci; ci = c[i]; i++){
5584                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5585                         r[++ri] = ci;
5586                     }
5587                 }
5588                 return r;
5589             },
5590
5591             "checked" : function(c){
5592                 var r = [], ri = -1;
5593                 for(var i = 0, ci; ci = c[i]; i++){
5594                     if(ci.checked == true){
5595                         r[++ri] = ci;
5596                     }
5597                 }
5598                 return r;
5599             },
5600
5601             "not" : function(c, ss){
5602                 return Roo.DomQuery.filter(c, ss, true);
5603             },
5604
5605             "odd" : function(c){
5606                 return this["nth-child"](c, "odd");
5607             },
5608
5609             "even" : function(c){
5610                 return this["nth-child"](c, "even");
5611             },
5612
5613             "nth" : function(c, a){
5614                 return c[a-1] || [];
5615             },
5616
5617             "first" : function(c){
5618                 return c[0] || [];
5619             },
5620
5621             "last" : function(c){
5622                 return c[c.length-1] || [];
5623             },
5624
5625             "has" : function(c, ss){
5626                 var s = Roo.DomQuery.select;
5627                 var r = [], ri = -1;
5628                 for(var i = 0, ci; ci = c[i]; i++){
5629                     if(s(ss, ci).length > 0){
5630                         r[++ri] = ci;
5631                     }
5632                 }
5633                 return r;
5634             },
5635
5636             "next" : function(c, ss){
5637                 var is = Roo.DomQuery.is;
5638                 var r = [], ri = -1;
5639                 for(var i = 0, ci; ci = c[i]; i++){
5640                     var n = next(ci);
5641                     if(n && is(n, ss)){
5642                         r[++ri] = ci;
5643                     }
5644                 }
5645                 return r;
5646             },
5647
5648             "prev" : function(c, ss){
5649                 var is = Roo.DomQuery.is;
5650                 var r = [], ri = -1;
5651                 for(var i = 0, ci; ci = c[i]; i++){
5652                     var n = prev(ci);
5653                     if(n && is(n, ss)){
5654                         r[++ri] = ci;
5655                     }
5656                 }
5657                 return r;
5658             }
5659         }
5660     };
5661 }();
5662
5663 /**
5664  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5665  * @param {String} path The selector/xpath query
5666  * @param {Node} root (optional) The start of the query (defaults to document).
5667  * @return {Array}
5668  * @member Roo
5669  * @method query
5670  */
5671 Roo.query = Roo.DomQuery.select;
5672 /*
5673  * Based on:
5674  * Ext JS Library 1.1.1
5675  * Copyright(c) 2006-2007, Ext JS, LLC.
5676  *
5677  * Originally Released Under LGPL - original licence link has changed is not relivant.
5678  *
5679  * Fork - LGPL
5680  * <script type="text/javascript">
5681  */
5682
5683 /**
5684  * @class Roo.util.Observable
5685  * Base class that provides a common interface for publishing events. Subclasses are expected to
5686  * to have a property "events" with all the events defined.<br>
5687  * For example:
5688  * <pre><code>
5689  Employee = function(name){
5690     this.name = name;
5691     this.addEvents({
5692         "fired" : true,
5693         "quit" : true
5694     });
5695  }
5696  Roo.extend(Employee, Roo.util.Observable);
5697 </code></pre>
5698  * @param {Object} config properties to use (incuding events / listeners)
5699  */
5700
5701 Roo.util.Observable = function(cfg){
5702     
5703     cfg = cfg|| {};
5704     this.addEvents(cfg.events || {});
5705     if (cfg.events) {
5706         delete cfg.events; // make sure
5707     }
5708      
5709     Roo.apply(this, cfg);
5710     
5711     if(this.listeners){
5712         this.on(this.listeners);
5713         delete this.listeners;
5714     }
5715 };
5716 Roo.util.Observable.prototype = {
5717     /** 
5718  * @cfg {Object} listeners  list of events and functions to call for this object, 
5719  * For example :
5720  * <pre><code>
5721     listeners :  { 
5722        'click' : function(e) {
5723            ..... 
5724         } ,
5725         .... 
5726     } 
5727   </code></pre>
5728  */
5729     
5730     
5731     /**
5732      * Fires the specified event with the passed parameters (minus the event name).
5733      * @param {String} eventName
5734      * @param {Object...} args Variable number of parameters are passed to handlers
5735      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5736      */
5737     fireEvent : function(){
5738         var ce = this.events[arguments[0].toLowerCase()];
5739         if(typeof ce == "object"){
5740             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5741         }else{
5742             return true;
5743         }
5744     },
5745
5746     // private
5747     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5748
5749     /**
5750      * Appends an event handler to this component
5751      * @param {String}   eventName The type of event to listen for
5752      * @param {Function} handler The method the event invokes
5753      * @param {Object}   scope (optional) The scope in which to execute the handler
5754      * function. The handler function's "this" context.
5755      * @param {Object}   options (optional) An object containing handler configuration
5756      * properties. This may contain any of the following properties:<ul>
5757      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5758      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5759      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5760      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5761      * by the specified number of milliseconds. If the event fires again within that time, the original
5762      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5763      * </ul><br>
5764      * <p>
5765      * <b>Combining Options</b><br>
5766      * Using the options argument, it is possible to combine different types of listeners:<br>
5767      * <br>
5768      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5769                 <pre><code>
5770                 el.on('click', this.onClick, this, {
5771                         single: true,
5772                 delay: 100,
5773                 forumId: 4
5774                 });
5775                 </code></pre>
5776      * <p>
5777      * <b>Attaching multiple handlers in 1 call</b><br>
5778      * The method also allows for a single argument to be passed which is a config object containing properties
5779      * which specify multiple handlers.
5780      * <pre><code>
5781                 el.on({
5782                         'click': {
5783                         fn: this.onClick,
5784                         scope: this,
5785                         delay: 100
5786                 }, 
5787                 'mouseover': {
5788                         fn: this.onMouseOver,
5789                         scope: this
5790                 },
5791                 'mouseout': {
5792                         fn: this.onMouseOut,
5793                         scope: this
5794                 }
5795                 });
5796                 </code></pre>
5797      * <p>
5798      * Or a shorthand syntax which passes the same scope object to all handlers:
5799         <pre><code>
5800                 el.on({
5801                         'click': this.onClick,
5802                 'mouseover': this.onMouseOver,
5803                 'mouseout': this.onMouseOut,
5804                 scope: this
5805                 });
5806                 </code></pre>
5807      */
5808     addListener : function(eventName, fn, scope, o){
5809         if(typeof eventName == "object"){
5810             o = eventName;
5811             for(var e in o){
5812                 if(this.filterOptRe.test(e)){
5813                     continue;
5814                 }
5815                 if(typeof o[e] == "function"){
5816                     // shared options
5817                     this.addListener(e, o[e], o.scope,  o);
5818                 }else{
5819                     // individual options
5820                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5821                 }
5822             }
5823             return;
5824         }
5825         o = (!o || typeof o == "boolean") ? {} : o;
5826         eventName = eventName.toLowerCase();
5827         var ce = this.events[eventName] || true;
5828         if(typeof ce == "boolean"){
5829             ce = new Roo.util.Event(this, eventName);
5830             this.events[eventName] = ce;
5831         }
5832         ce.addListener(fn, scope, o);
5833     },
5834
5835     /**
5836      * Removes a listener
5837      * @param {String}   eventName     The type of event to listen for
5838      * @param {Function} handler        The handler to remove
5839      * @param {Object}   scope  (optional) The scope (this object) for the handler
5840      */
5841     removeListener : function(eventName, fn, scope){
5842         var ce = this.events[eventName.toLowerCase()];
5843         if(typeof ce == "object"){
5844             ce.removeListener(fn, scope);
5845         }
5846     },
5847
5848     /**
5849      * Removes all listeners for this object
5850      */
5851     purgeListeners : function(){
5852         for(var evt in this.events){
5853             if(typeof this.events[evt] == "object"){
5854                  this.events[evt].clearListeners();
5855             }
5856         }
5857     },
5858
5859     relayEvents : function(o, events){
5860         var createHandler = function(ename){
5861             return function(){
5862                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5863             };
5864         };
5865         for(var i = 0, len = events.length; i < len; i++){
5866             var ename = events[i];
5867             if(!this.events[ename]){ this.events[ename] = true; };
5868             o.on(ename, createHandler(ename), this);
5869         }
5870     },
5871
5872     /**
5873      * Used to define events on this Observable
5874      * @param {Object} object The object with the events defined
5875      */
5876     addEvents : function(o){
5877         if(!this.events){
5878             this.events = {};
5879         }
5880         Roo.applyIf(this.events, o);
5881     },
5882
5883     /**
5884      * Checks to see if this object has any listeners for a specified event
5885      * @param {String} eventName The name of the event to check for
5886      * @return {Boolean} True if the event is being listened for, else false
5887      */
5888     hasListener : function(eventName){
5889         var e = this.events[eventName];
5890         return typeof e == "object" && e.listeners.length > 0;
5891     }
5892 };
5893 /**
5894  * Appends an event handler to this element (shorthand for addListener)
5895  * @param {String}   eventName     The type of event to listen for
5896  * @param {Function} handler        The method the event invokes
5897  * @param {Object}   scope (optional) The scope in which to execute the handler
5898  * function. The handler function's "this" context.
5899  * @param {Object}   options  (optional)
5900  * @method
5901  */
5902 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5903 /**
5904  * Removes a listener (shorthand for removeListener)
5905  * @param {String}   eventName     The type of event to listen for
5906  * @param {Function} handler        The handler to remove
5907  * @param {Object}   scope  (optional) The scope (this object) for the handler
5908  * @method
5909  */
5910 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5911
5912 /**
5913  * Starts capture on the specified Observable. All events will be passed
5914  * to the supplied function with the event name + standard signature of the event
5915  * <b>before</b> the event is fired. If the supplied function returns false,
5916  * the event will not fire.
5917  * @param {Observable} o The Observable to capture
5918  * @param {Function} fn The function to call
5919  * @param {Object} scope (optional) The scope (this object) for the fn
5920  * @static
5921  */
5922 Roo.util.Observable.capture = function(o, fn, scope){
5923     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5924 };
5925
5926 /**
5927  * Removes <b>all</b> added captures from the Observable.
5928  * @param {Observable} o The Observable to release
5929  * @static
5930  */
5931 Roo.util.Observable.releaseCapture = function(o){
5932     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5933 };
5934
5935 (function(){
5936
5937     var createBuffered = function(h, o, scope){
5938         var task = new Roo.util.DelayedTask();
5939         return function(){
5940             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5941         };
5942     };
5943
5944     var createSingle = function(h, e, fn, scope){
5945         return function(){
5946             e.removeListener(fn, scope);
5947             return h.apply(scope, arguments);
5948         };
5949     };
5950
5951     var createDelayed = function(h, o, scope){
5952         return function(){
5953             var args = Array.prototype.slice.call(arguments, 0);
5954             setTimeout(function(){
5955                 h.apply(scope, args);
5956             }, o.delay || 10);
5957         };
5958     };
5959
5960     Roo.util.Event = function(obj, name){
5961         this.name = name;
5962         this.obj = obj;
5963         this.listeners = [];
5964     };
5965
5966     Roo.util.Event.prototype = {
5967         addListener : function(fn, scope, options){
5968             var o = options || {};
5969             scope = scope || this.obj;
5970             if(!this.isListening(fn, scope)){
5971                 var l = {fn: fn, scope: scope, options: o};
5972                 var h = fn;
5973                 if(o.delay){
5974                     h = createDelayed(h, o, scope);
5975                 }
5976                 if(o.single){
5977                     h = createSingle(h, this, fn, scope);
5978                 }
5979                 if(o.buffer){
5980                     h = createBuffered(h, o, scope);
5981                 }
5982                 l.fireFn = h;
5983                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5984                     this.listeners.push(l);
5985                 }else{
5986                     this.listeners = this.listeners.slice(0);
5987                     this.listeners.push(l);
5988                 }
5989             }
5990         },
5991
5992         findListener : function(fn, scope){
5993             scope = scope || this.obj;
5994             var ls = this.listeners;
5995             for(var i = 0, len = ls.length; i < len; i++){
5996                 var l = ls[i];
5997                 if(l.fn == fn && l.scope == scope){
5998                     return i;
5999                 }
6000             }
6001             return -1;
6002         },
6003
6004         isListening : function(fn, scope){
6005             return this.findListener(fn, scope) != -1;
6006         },
6007
6008         removeListener : function(fn, scope){
6009             var index;
6010             if((index = this.findListener(fn, scope)) != -1){
6011                 if(!this.firing){
6012                     this.listeners.splice(index, 1);
6013                 }else{
6014                     this.listeners = this.listeners.slice(0);
6015                     this.listeners.splice(index, 1);
6016                 }
6017                 return true;
6018             }
6019             return false;
6020         },
6021
6022         clearListeners : function(){
6023             this.listeners = [];
6024         },
6025
6026         fire : function(){
6027             var ls = this.listeners, scope, len = ls.length;
6028             if(len > 0){
6029                 this.firing = true;
6030                 var args = Array.prototype.slice.call(arguments, 0);
6031                 for(var i = 0; i < len; i++){
6032                     var l = ls[i];
6033                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6034                         this.firing = false;
6035                         return false;
6036                     }
6037                 }
6038                 this.firing = false;
6039             }
6040             return true;
6041         }
6042     };
6043 })();/*
6044  * Based on:
6045  * Ext JS Library 1.1.1
6046  * Copyright(c) 2006-2007, Ext JS, LLC.
6047  *
6048  * Originally Released Under LGPL - original licence link has changed is not relivant.
6049  *
6050  * Fork - LGPL
6051  * <script type="text/javascript">
6052  */
6053
6054 /**
6055  * @class Roo.EventManager
6056  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6057  * several useful events directly.
6058  * See {@link Roo.EventObject} for more details on normalized event objects.
6059  * @singleton
6060  */
6061 Roo.EventManager = function(){
6062     var docReadyEvent, docReadyProcId, docReadyState = false;
6063     var resizeEvent, resizeTask, textEvent, textSize;
6064     var E = Roo.lib.Event;
6065     var D = Roo.lib.Dom;
6066
6067     
6068     
6069
6070     var fireDocReady = function(){
6071         if(!docReadyState){
6072             docReadyState = true;
6073             Roo.isReady = true;
6074             if(docReadyProcId){
6075                 clearInterval(docReadyProcId);
6076             }
6077             if(Roo.isGecko || Roo.isOpera) {
6078                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6079             }
6080             if(Roo.isIE){
6081                 var defer = document.getElementById("ie-deferred-loader");
6082                 if(defer){
6083                     defer.onreadystatechange = null;
6084                     defer.parentNode.removeChild(defer);
6085                 }
6086             }
6087             if(docReadyEvent){
6088                 docReadyEvent.fire();
6089                 docReadyEvent.clearListeners();
6090             }
6091         }
6092     };
6093     
6094     var initDocReady = function(){
6095         docReadyEvent = new Roo.util.Event();
6096         if(Roo.isGecko || Roo.isOpera) {
6097             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6098         }else if(Roo.isIE){
6099             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6100             var defer = document.getElementById("ie-deferred-loader");
6101             defer.onreadystatechange = function(){
6102                 if(this.readyState == "complete"){
6103                     fireDocReady();
6104                 }
6105             };
6106         }else if(Roo.isSafari){ 
6107             docReadyProcId = setInterval(function(){
6108                 var rs = document.readyState;
6109                 if(rs == "complete") {
6110                     fireDocReady();     
6111                  }
6112             }, 10);
6113         }
6114         // no matter what, make sure it fires on load
6115         E.on(window, "load", fireDocReady);
6116     };
6117
6118     var createBuffered = function(h, o){
6119         var task = new Roo.util.DelayedTask(h);
6120         return function(e){
6121             // create new event object impl so new events don't wipe out properties
6122             e = new Roo.EventObjectImpl(e);
6123             task.delay(o.buffer, h, null, [e]);
6124         };
6125     };
6126
6127     var createSingle = function(h, el, ename, fn){
6128         return function(e){
6129             Roo.EventManager.removeListener(el, ename, fn);
6130             h(e);
6131         };
6132     };
6133
6134     var createDelayed = function(h, o){
6135         return function(e){
6136             // create new event object impl so new events don't wipe out properties
6137             e = new Roo.EventObjectImpl(e);
6138             setTimeout(function(){
6139                 h(e);
6140             }, o.delay || 10);
6141         };
6142     };
6143     var transitionEndVal = false;
6144     
6145     var transitionEnd = function()
6146     {
6147         if (transitionEndVal) {
6148             return transitionEndVal;
6149         }
6150         var el = document.createElement('div');
6151
6152         var transEndEventNames = {
6153             WebkitTransition : 'webkitTransitionEnd',
6154             MozTransition    : 'transitionend',
6155             OTransition      : 'oTransitionEnd otransitionend',
6156             transition       : 'transitionend'
6157         };
6158     
6159         for (var name in transEndEventNames) {
6160             if (el.style[name] !== undefined) {
6161                 transitionEndVal = transEndEventNames[name];
6162                 return  transitionEndVal ;
6163             }
6164         }
6165     }
6166     
6167
6168     var listen = function(element, ename, opt, fn, scope){
6169         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6170         fn = fn || o.fn; scope = scope || o.scope;
6171         var el = Roo.getDom(element);
6172         
6173         
6174         if(!el){
6175             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6176         }
6177         
6178         if (ename == 'transitionend') {
6179             ename = transitionEnd();
6180         }
6181         var h = function(e){
6182             e = Roo.EventObject.setEvent(e);
6183             var t;
6184             if(o.delegate){
6185                 t = e.getTarget(o.delegate, el);
6186                 if(!t){
6187                     return;
6188                 }
6189             }else{
6190                 t = e.target;
6191             }
6192             if(o.stopEvent === true){
6193                 e.stopEvent();
6194             }
6195             if(o.preventDefault === true){
6196                e.preventDefault();
6197             }
6198             if(o.stopPropagation === true){
6199                 e.stopPropagation();
6200             }
6201
6202             if(o.normalized === false){
6203                 e = e.browserEvent;
6204             }
6205
6206             fn.call(scope || el, e, t, o);
6207         };
6208         if(o.delay){
6209             h = createDelayed(h, o);
6210         }
6211         if(o.single){
6212             h = createSingle(h, el, ename, fn);
6213         }
6214         if(o.buffer){
6215             h = createBuffered(h, o);
6216         }
6217         fn._handlers = fn._handlers || [];
6218         
6219         
6220         fn._handlers.push([Roo.id(el), ename, h]);
6221         
6222         
6223          
6224         E.on(el, ename, h);
6225         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6226             el.addEventListener("DOMMouseScroll", h, false);
6227             E.on(window, 'unload', function(){
6228                 el.removeEventListener("DOMMouseScroll", h, false);
6229             });
6230         }
6231         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6232             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6233         }
6234         return h;
6235     };
6236
6237     var stopListening = function(el, ename, fn){
6238         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6239         if(hds){
6240             for(var i = 0, len = hds.length; i < len; i++){
6241                 var h = hds[i];
6242                 if(h[0] == id && h[1] == ename){
6243                     hd = h[2];
6244                     hds.splice(i, 1);
6245                     break;
6246                 }
6247             }
6248         }
6249         E.un(el, ename, hd);
6250         el = Roo.getDom(el);
6251         if(ename == "mousewheel" && el.addEventListener){
6252             el.removeEventListener("DOMMouseScroll", hd, false);
6253         }
6254         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6255             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6256         }
6257     };
6258
6259     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6260     
6261     var pub = {
6262         
6263         
6264         /** 
6265          * Fix for doc tools
6266          * @scope Roo.EventManager
6267          */
6268         
6269         
6270         /** 
6271          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6272          * object with a Roo.EventObject
6273          * @param {Function} fn        The method the event invokes
6274          * @param {Object}   scope    An object that becomes the scope of the handler
6275          * @param {boolean}  override If true, the obj passed in becomes
6276          *                             the execution scope of the listener
6277          * @return {Function} The wrapped function
6278          * @deprecated
6279          */
6280         wrap : function(fn, scope, override){
6281             return function(e){
6282                 Roo.EventObject.setEvent(e);
6283                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6284             };
6285         },
6286         
6287         /**
6288      * Appends an event handler to an element (shorthand for addListener)
6289      * @param {String/HTMLElement}   element        The html element or id to assign the
6290      * @param {String}   eventName The type of event to listen for
6291      * @param {Function} handler The method the event invokes
6292      * @param {Object}   scope (optional) The scope in which to execute the handler
6293      * function. The handler function's "this" context.
6294      * @param {Object}   options (optional) An object containing handler configuration
6295      * properties. This may contain any of the following properties:<ul>
6296      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6297      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6298      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6299      * <li>preventDefault {Boolean} True to prevent the default action</li>
6300      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6301      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6302      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6303      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6304      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6305      * by the specified number of milliseconds. If the event fires again within that time, the original
6306      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6307      * </ul><br>
6308      * <p>
6309      * <b>Combining Options</b><br>
6310      * Using the options argument, it is possible to combine different types of listeners:<br>
6311      * <br>
6312      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6313      * Code:<pre><code>
6314 el.on('click', this.onClick, this, {
6315     single: true,
6316     delay: 100,
6317     stopEvent : true,
6318     forumId: 4
6319 });</code></pre>
6320      * <p>
6321      * <b>Attaching multiple handlers in 1 call</b><br>
6322       * The method also allows for a single argument to be passed which is a config object containing properties
6323      * which specify multiple handlers.
6324      * <p>
6325      * Code:<pre><code>
6326 el.on({
6327     'click' : {
6328         fn: this.onClick
6329         scope: this,
6330         delay: 100
6331     },
6332     'mouseover' : {
6333         fn: this.onMouseOver
6334         scope: this
6335     },
6336     'mouseout' : {
6337         fn: this.onMouseOut
6338         scope: this
6339     }
6340 });</code></pre>
6341      * <p>
6342      * Or a shorthand syntax:<br>
6343      * Code:<pre><code>
6344 el.on({
6345     'click' : this.onClick,
6346     'mouseover' : this.onMouseOver,
6347     'mouseout' : this.onMouseOut
6348     scope: this
6349 });</code></pre>
6350      */
6351         addListener : function(element, eventName, fn, scope, options){
6352             if(typeof eventName == "object"){
6353                 var o = eventName;
6354                 for(var e in o){
6355                     if(propRe.test(e)){
6356                         continue;
6357                     }
6358                     if(typeof o[e] == "function"){
6359                         // shared options
6360                         listen(element, e, o, o[e], o.scope);
6361                     }else{
6362                         // individual options
6363                         listen(element, e, o[e]);
6364                     }
6365                 }
6366                 return;
6367             }
6368             return listen(element, eventName, options, fn, scope);
6369         },
6370         
6371         /**
6372          * Removes an event handler
6373          *
6374          * @param {String/HTMLElement}   element        The id or html element to remove the 
6375          *                             event from
6376          * @param {String}   eventName     The type of event
6377          * @param {Function} fn
6378          * @return {Boolean} True if a listener was actually removed
6379          */
6380         removeListener : function(element, eventName, fn){
6381             return stopListening(element, eventName, fn);
6382         },
6383         
6384         /**
6385          * Fires when the document is ready (before onload and before images are loaded). Can be 
6386          * accessed shorthanded Roo.onReady().
6387          * @param {Function} fn        The method the event invokes
6388          * @param {Object}   scope    An  object that becomes the scope of the handler
6389          * @param {boolean}  options
6390          */
6391         onDocumentReady : function(fn, scope, options){
6392             if(docReadyState){ // if it already fired
6393                 docReadyEvent.addListener(fn, scope, options);
6394                 docReadyEvent.fire();
6395                 docReadyEvent.clearListeners();
6396                 return;
6397             }
6398             if(!docReadyEvent){
6399                 initDocReady();
6400             }
6401             docReadyEvent.addListener(fn, scope, options);
6402         },
6403         
6404         /**
6405          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6406          * @param {Function} fn        The method the event invokes
6407          * @param {Object}   scope    An object that becomes the scope of the handler
6408          * @param {boolean}  options
6409          */
6410         onWindowResize : function(fn, scope, options){
6411             if(!resizeEvent){
6412                 resizeEvent = new Roo.util.Event();
6413                 resizeTask = new Roo.util.DelayedTask(function(){
6414                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6415                 });
6416                 E.on(window, "resize", function(){
6417                     if(Roo.isIE){
6418                         resizeTask.delay(50);
6419                     }else{
6420                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6421                     }
6422                 });
6423             }
6424             resizeEvent.addListener(fn, scope, options);
6425         },
6426
6427         /**
6428          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6429          * @param {Function} fn        The method the event invokes
6430          * @param {Object}   scope    An object that becomes the scope of the handler
6431          * @param {boolean}  options
6432          */
6433         onTextResize : function(fn, scope, options){
6434             if(!textEvent){
6435                 textEvent = new Roo.util.Event();
6436                 var textEl = new Roo.Element(document.createElement('div'));
6437                 textEl.dom.className = 'x-text-resize';
6438                 textEl.dom.innerHTML = 'X';
6439                 textEl.appendTo(document.body);
6440                 textSize = textEl.dom.offsetHeight;
6441                 setInterval(function(){
6442                     if(textEl.dom.offsetHeight != textSize){
6443                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6444                     }
6445                 }, this.textResizeInterval);
6446             }
6447             textEvent.addListener(fn, scope, options);
6448         },
6449
6450         /**
6451          * Removes the passed window resize listener.
6452          * @param {Function} fn        The method the event invokes
6453          * @param {Object}   scope    The scope of handler
6454          */
6455         removeResizeListener : function(fn, scope){
6456             if(resizeEvent){
6457                 resizeEvent.removeListener(fn, scope);
6458             }
6459         },
6460
6461         // private
6462         fireResize : function(){
6463             if(resizeEvent){
6464                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6465             }   
6466         },
6467         /**
6468          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6469          */
6470         ieDeferSrc : false,
6471         /**
6472          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6473          */
6474         textResizeInterval : 50
6475     };
6476     
6477     /**
6478      * Fix for doc tools
6479      * @scopeAlias pub=Roo.EventManager
6480      */
6481     
6482      /**
6483      * Appends an event handler to an element (shorthand for addListener)
6484      * @param {String/HTMLElement}   element        The html element or id to assign the
6485      * @param {String}   eventName The type of event to listen for
6486      * @param {Function} handler The method the event invokes
6487      * @param {Object}   scope (optional) The scope in which to execute the handler
6488      * function. The handler function's "this" context.
6489      * @param {Object}   options (optional) An object containing handler configuration
6490      * properties. This may contain any of the following properties:<ul>
6491      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6492      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6493      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6494      * <li>preventDefault {Boolean} True to prevent the default action</li>
6495      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6496      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6497      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6498      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6499      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6500      * by the specified number of milliseconds. If the event fires again within that time, the original
6501      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6502      * </ul><br>
6503      * <p>
6504      * <b>Combining Options</b><br>
6505      * Using the options argument, it is possible to combine different types of listeners:<br>
6506      * <br>
6507      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6508      * Code:<pre><code>
6509 el.on('click', this.onClick, this, {
6510     single: true,
6511     delay: 100,
6512     stopEvent : true,
6513     forumId: 4
6514 });</code></pre>
6515      * <p>
6516      * <b>Attaching multiple handlers in 1 call</b><br>
6517       * The method also allows for a single argument to be passed which is a config object containing properties
6518      * which specify multiple handlers.
6519      * <p>
6520      * Code:<pre><code>
6521 el.on({
6522     'click' : {
6523         fn: this.onClick
6524         scope: this,
6525         delay: 100
6526     },
6527     'mouseover' : {
6528         fn: this.onMouseOver
6529         scope: this
6530     },
6531     'mouseout' : {
6532         fn: this.onMouseOut
6533         scope: this
6534     }
6535 });</code></pre>
6536      * <p>
6537      * Or a shorthand syntax:<br>
6538      * Code:<pre><code>
6539 el.on({
6540     'click' : this.onClick,
6541     'mouseover' : this.onMouseOver,
6542     'mouseout' : this.onMouseOut
6543     scope: this
6544 });</code></pre>
6545      */
6546     pub.on = pub.addListener;
6547     pub.un = pub.removeListener;
6548
6549     pub.stoppedMouseDownEvent = new Roo.util.Event();
6550     return pub;
6551 }();
6552 /**
6553   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6554   * @param {Function} fn        The method the event invokes
6555   * @param {Object}   scope    An  object that becomes the scope of the handler
6556   * @param {boolean}  override If true, the obj passed in becomes
6557   *                             the execution scope of the listener
6558   * @member Roo
6559   * @method onReady
6560  */
6561 Roo.onReady = Roo.EventManager.onDocumentReady;
6562
6563 Roo.onReady(function(){
6564     var bd = Roo.get(document.body);
6565     if(!bd){ return; }
6566
6567     var cls = [
6568             Roo.isIE ? "roo-ie"
6569             : Roo.isGecko ? "roo-gecko"
6570             : Roo.isOpera ? "roo-opera"
6571             : Roo.isSafari ? "roo-safari" : ""];
6572
6573     if(Roo.isMac){
6574         cls.push("roo-mac");
6575     }
6576     if(Roo.isLinux){
6577         cls.push("roo-linux");
6578     }
6579     if(Roo.isBorderBox){
6580         cls.push('roo-border-box');
6581     }
6582     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6583         var p = bd.dom.parentNode;
6584         if(p){
6585             p.className += ' roo-strict';
6586         }
6587     }
6588     bd.addClass(cls.join(' '));
6589 });
6590
6591 /**
6592  * @class Roo.EventObject
6593  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6594  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6595  * Example:
6596  * <pre><code>
6597  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6598     e.preventDefault();
6599     var target = e.getTarget();
6600     ...
6601  }
6602  var myDiv = Roo.get("myDiv");
6603  myDiv.on("click", handleClick);
6604  //or
6605  Roo.EventManager.on("myDiv", 'click', handleClick);
6606  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6607  </code></pre>
6608  * @singleton
6609  */
6610 Roo.EventObject = function(){
6611     
6612     var E = Roo.lib.Event;
6613     
6614     // safari keypress events for special keys return bad keycodes
6615     var safariKeys = {
6616         63234 : 37, // left
6617         63235 : 39, // right
6618         63232 : 38, // up
6619         63233 : 40, // down
6620         63276 : 33, // page up
6621         63277 : 34, // page down
6622         63272 : 46, // delete
6623         63273 : 36, // home
6624         63275 : 35  // end
6625     };
6626
6627     // normalize button clicks
6628     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6629                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6630
6631     Roo.EventObjectImpl = function(e){
6632         if(e){
6633             this.setEvent(e.browserEvent || e);
6634         }
6635     };
6636     Roo.EventObjectImpl.prototype = {
6637         /**
6638          * Used to fix doc tools.
6639          * @scope Roo.EventObject.prototype
6640          */
6641             
6642
6643         
6644         
6645         /** The normal browser event */
6646         browserEvent : null,
6647         /** The button pressed in a mouse event */
6648         button : -1,
6649         /** True if the shift key was down during the event */
6650         shiftKey : false,
6651         /** True if the control key was down during the event */
6652         ctrlKey : false,
6653         /** True if the alt key was down during the event */
6654         altKey : false,
6655
6656         /** Key constant 
6657         * @type Number */
6658         BACKSPACE : 8,
6659         /** Key constant 
6660         * @type Number */
6661         TAB : 9,
6662         /** Key constant 
6663         * @type Number */
6664         RETURN : 13,
6665         /** Key constant 
6666         * @type Number */
6667         ENTER : 13,
6668         /** Key constant 
6669         * @type Number */
6670         SHIFT : 16,
6671         /** Key constant 
6672         * @type Number */
6673         CONTROL : 17,
6674         /** Key constant 
6675         * @type Number */
6676         ESC : 27,
6677         /** Key constant 
6678         * @type Number */
6679         SPACE : 32,
6680         /** Key constant 
6681         * @type Number */
6682         PAGEUP : 33,
6683         /** Key constant 
6684         * @type Number */
6685         PAGEDOWN : 34,
6686         /** Key constant 
6687         * @type Number */
6688         END : 35,
6689         /** Key constant 
6690         * @type Number */
6691         HOME : 36,
6692         /** Key constant 
6693         * @type Number */
6694         LEFT : 37,
6695         /** Key constant 
6696         * @type Number */
6697         UP : 38,
6698         /** Key constant 
6699         * @type Number */
6700         RIGHT : 39,
6701         /** Key constant 
6702         * @type Number */
6703         DOWN : 40,
6704         /** Key constant 
6705         * @type Number */
6706         DELETE : 46,
6707         /** Key constant 
6708         * @type Number */
6709         F5 : 116,
6710
6711            /** @private */
6712         setEvent : function(e){
6713             if(e == this || (e && e.browserEvent)){ // already wrapped
6714                 return e;
6715             }
6716             this.browserEvent = e;
6717             if(e){
6718                 // normalize buttons
6719                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6720                 if(e.type == 'click' && this.button == -1){
6721                     this.button = 0;
6722                 }
6723                 this.type = e.type;
6724                 this.shiftKey = e.shiftKey;
6725                 // mac metaKey behaves like ctrlKey
6726                 this.ctrlKey = e.ctrlKey || e.metaKey;
6727                 this.altKey = e.altKey;
6728                 // in getKey these will be normalized for the mac
6729                 this.keyCode = e.keyCode;
6730                 // keyup warnings on firefox.
6731                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6732                 // cache the target for the delayed and or buffered events
6733                 this.target = E.getTarget(e);
6734                 // same for XY
6735                 this.xy = E.getXY(e);
6736             }else{
6737                 this.button = -1;
6738                 this.shiftKey = false;
6739                 this.ctrlKey = false;
6740                 this.altKey = false;
6741                 this.keyCode = 0;
6742                 this.charCode =0;
6743                 this.target = null;
6744                 this.xy = [0, 0];
6745             }
6746             return this;
6747         },
6748
6749         /**
6750          * Stop the event (preventDefault and stopPropagation)
6751          */
6752         stopEvent : function(){
6753             if(this.browserEvent){
6754                 if(this.browserEvent.type == 'mousedown'){
6755                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6756                 }
6757                 E.stopEvent(this.browserEvent);
6758             }
6759         },
6760
6761         /**
6762          * Prevents the browsers default handling of the event.
6763          */
6764         preventDefault : function(){
6765             if(this.browserEvent){
6766                 E.preventDefault(this.browserEvent);
6767             }
6768         },
6769
6770         /** @private */
6771         isNavKeyPress : function(){
6772             var k = this.keyCode;
6773             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6774             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6775         },
6776
6777         isSpecialKey : function(){
6778             var k = this.keyCode;
6779             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6780             (k == 16) || (k == 17) ||
6781             (k >= 18 && k <= 20) ||
6782             (k >= 33 && k <= 35) ||
6783             (k >= 36 && k <= 39) ||
6784             (k >= 44 && k <= 45);
6785         },
6786         /**
6787          * Cancels bubbling of the event.
6788          */
6789         stopPropagation : function(){
6790             if(this.browserEvent){
6791                 if(this.type == 'mousedown'){
6792                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6793                 }
6794                 E.stopPropagation(this.browserEvent);
6795             }
6796         },
6797
6798         /**
6799          * Gets the key code for the event.
6800          * @return {Number}
6801          */
6802         getCharCode : function(){
6803             return this.charCode || this.keyCode;
6804         },
6805
6806         /**
6807          * Returns a normalized keyCode for the event.
6808          * @return {Number} The key code
6809          */
6810         getKey : function(){
6811             var k = this.keyCode || this.charCode;
6812             return Roo.isSafari ? (safariKeys[k] || k) : k;
6813         },
6814
6815         /**
6816          * Gets the x coordinate of the event.
6817          * @return {Number}
6818          */
6819         getPageX : function(){
6820             return this.xy[0];
6821         },
6822
6823         /**
6824          * Gets the y coordinate of the event.
6825          * @return {Number}
6826          */
6827         getPageY : function(){
6828             return this.xy[1];
6829         },
6830
6831         /**
6832          * Gets the time of the event.
6833          * @return {Number}
6834          */
6835         getTime : function(){
6836             if(this.browserEvent){
6837                 return E.getTime(this.browserEvent);
6838             }
6839             return null;
6840         },
6841
6842         /**
6843          * Gets the page coordinates of the event.
6844          * @return {Array} The xy values like [x, y]
6845          */
6846         getXY : function(){
6847             return this.xy;
6848         },
6849
6850         /**
6851          * Gets the target for the event.
6852          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6853          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6854                 search as a number or element (defaults to 10 || document.body)
6855          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6856          * @return {HTMLelement}
6857          */
6858         getTarget : function(selector, maxDepth, returnEl){
6859             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6860         },
6861         /**
6862          * Gets the related target.
6863          * @return {HTMLElement}
6864          */
6865         getRelatedTarget : function(){
6866             if(this.browserEvent){
6867                 return E.getRelatedTarget(this.browserEvent);
6868             }
6869             return null;
6870         },
6871
6872         /**
6873          * Normalizes mouse wheel delta across browsers
6874          * @return {Number} The delta
6875          */
6876         getWheelDelta : function(){
6877             var e = this.browserEvent;
6878             var delta = 0;
6879             if(e.wheelDelta){ /* IE/Opera. */
6880                 delta = e.wheelDelta/120;
6881             }else if(e.detail){ /* Mozilla case. */
6882                 delta = -e.detail/3;
6883             }
6884             return delta;
6885         },
6886
6887         /**
6888          * Returns true if the control, meta, shift or alt key was pressed during this event.
6889          * @return {Boolean}
6890          */
6891         hasModifier : function(){
6892             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6893         },
6894
6895         /**
6896          * Returns true if the target of this event equals el or is a child of el
6897          * @param {String/HTMLElement/Element} el
6898          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6899          * @return {Boolean}
6900          */
6901         within : function(el, related){
6902             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6903             return t && Roo.fly(el).contains(t);
6904         },
6905
6906         getPoint : function(){
6907             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6908         }
6909     };
6910
6911     return new Roo.EventObjectImpl();
6912 }();
6913             
6914     /*
6915  * Based on:
6916  * Ext JS Library 1.1.1
6917  * Copyright(c) 2006-2007, Ext JS, LLC.
6918  *
6919  * Originally Released Under LGPL - original licence link has changed is not relivant.
6920  *
6921  * Fork - LGPL
6922  * <script type="text/javascript">
6923  */
6924
6925  
6926 // was in Composite Element!??!?!
6927  
6928 (function(){
6929     var D = Roo.lib.Dom;
6930     var E = Roo.lib.Event;
6931     var A = Roo.lib.Anim;
6932
6933     // local style camelizing for speed
6934     var propCache = {};
6935     var camelRe = /(-[a-z])/gi;
6936     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6937     var view = document.defaultView;
6938
6939 /**
6940  * @class Roo.Element
6941  * Represents an Element in the DOM.<br><br>
6942  * Usage:<br>
6943 <pre><code>
6944 var el = Roo.get("my-div");
6945
6946 // or with getEl
6947 var el = getEl("my-div");
6948
6949 // or with a DOM element
6950 var el = Roo.get(myDivElement);
6951 </code></pre>
6952  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6953  * each call instead of constructing a new one.<br><br>
6954  * <b>Animations</b><br />
6955  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6956  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6957 <pre>
6958 Option    Default   Description
6959 --------- --------  ---------------------------------------------
6960 duration  .35       The duration of the animation in seconds
6961 easing    easeOut   The YUI easing method
6962 callback  none      A function to execute when the anim completes
6963 scope     this      The scope (this) of the callback function
6964 </pre>
6965 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6966 * manipulate the animation. Here's an example:
6967 <pre><code>
6968 var el = Roo.get("my-div");
6969
6970 // no animation
6971 el.setWidth(100);
6972
6973 // default animation
6974 el.setWidth(100, true);
6975
6976 // animation with some options set
6977 el.setWidth(100, {
6978     duration: 1,
6979     callback: this.foo,
6980     scope: this
6981 });
6982
6983 // using the "anim" property to get the Anim object
6984 var opt = {
6985     duration: 1,
6986     callback: this.foo,
6987     scope: this
6988 };
6989 el.setWidth(100, opt);
6990 ...
6991 if(opt.anim.isAnimated()){
6992     opt.anim.stop();
6993 }
6994 </code></pre>
6995 * <b> Composite (Collections of) Elements</b><br />
6996  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6997  * @constructor Create a new Element directly.
6998  * @param {String/HTMLElement} element
6999  * @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).
7000  */
7001     Roo.Element = function(element, forceNew){
7002         var dom = typeof element == "string" ?
7003                 document.getElementById(element) : element;
7004         if(!dom){ // invalid id/element
7005             return null;
7006         }
7007         var id = dom.id;
7008         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7009             return Roo.Element.cache[id];
7010         }
7011
7012         /**
7013          * The DOM element
7014          * @type HTMLElement
7015          */
7016         this.dom = dom;
7017
7018         /**
7019          * The DOM element ID
7020          * @type String
7021          */
7022         this.id = id || Roo.id(dom);
7023     };
7024
7025     var El = Roo.Element;
7026
7027     El.prototype = {
7028         /**
7029          * The element's default display mode  (defaults to "")
7030          * @type String
7031          */
7032         originalDisplay : "",
7033
7034         visibilityMode : 1,
7035         /**
7036          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7037          * @type String
7038          */
7039         defaultUnit : "px",
7040         /**
7041          * Sets the element's visibility mode. When setVisible() is called it
7042          * will use this to determine whether to set the visibility or the display property.
7043          * @param visMode Element.VISIBILITY or Element.DISPLAY
7044          * @return {Roo.Element} this
7045          */
7046         setVisibilityMode : function(visMode){
7047             this.visibilityMode = visMode;
7048             return this;
7049         },
7050         /**
7051          * Convenience method for setVisibilityMode(Element.DISPLAY)
7052          * @param {String} display (optional) What to set display to when visible
7053          * @return {Roo.Element} this
7054          */
7055         enableDisplayMode : function(display){
7056             this.setVisibilityMode(El.DISPLAY);
7057             if(typeof display != "undefined") this.originalDisplay = display;
7058             return this;
7059         },
7060
7061         /**
7062          * 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)
7063          * @param {String} selector The simple selector to test
7064          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7065                 search as a number or element (defaults to 10 || document.body)
7066          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7067          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7068          */
7069         findParent : function(simpleSelector, maxDepth, returnEl){
7070             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7071             maxDepth = maxDepth || 50;
7072             if(typeof maxDepth != "number"){
7073                 stopEl = Roo.getDom(maxDepth);
7074                 maxDepth = 10;
7075             }
7076             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7077                 if(dq.is(p, simpleSelector)){
7078                     return returnEl ? Roo.get(p) : p;
7079                 }
7080                 depth++;
7081                 p = p.parentNode;
7082             }
7083             return null;
7084         },
7085
7086
7087         /**
7088          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7089          * @param {String} selector The simple selector to test
7090          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7091                 search as a number or element (defaults to 10 || document.body)
7092          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7093          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7094          */
7095         findParentNode : function(simpleSelector, maxDepth, returnEl){
7096             var p = Roo.fly(this.dom.parentNode, '_internal');
7097             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7098         },
7099
7100         /**
7101          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7102          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7103          * @param {String} selector The simple selector to test
7104          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7105                 search as a number or element (defaults to 10 || document.body)
7106          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7107          */
7108         up : function(simpleSelector, maxDepth){
7109             return this.findParentNode(simpleSelector, maxDepth, true);
7110         },
7111
7112
7113
7114         /**
7115          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7116          * @param {String} selector The simple selector to test
7117          * @return {Boolean} True if this element matches the selector, else false
7118          */
7119         is : function(simpleSelector){
7120             return Roo.DomQuery.is(this.dom, simpleSelector);
7121         },
7122
7123         /**
7124          * Perform animation on this element.
7125          * @param {Object} args The YUI animation control args
7126          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7127          * @param {Function} onComplete (optional) Function to call when animation completes
7128          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7129          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7130          * @return {Roo.Element} this
7131          */
7132         animate : function(args, duration, onComplete, easing, animType){
7133             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7134             return this;
7135         },
7136
7137         /*
7138          * @private Internal animation call
7139          */
7140         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7141             animType = animType || 'run';
7142             opt = opt || {};
7143             var anim = Roo.lib.Anim[animType](
7144                 this.dom, args,
7145                 (opt.duration || defaultDur) || .35,
7146                 (opt.easing || defaultEase) || 'easeOut',
7147                 function(){
7148                     Roo.callback(cb, this);
7149                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7150                 },
7151                 this
7152             );
7153             opt.anim = anim;
7154             return anim;
7155         },
7156
7157         // private legacy anim prep
7158         preanim : function(a, i){
7159             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7160         },
7161
7162         /**
7163          * Removes worthless text nodes
7164          * @param {Boolean} forceReclean (optional) By default the element
7165          * keeps track if it has been cleaned already so
7166          * you can call this over and over. However, if you update the element and
7167          * need to force a reclean, you can pass true.
7168          */
7169         clean : function(forceReclean){
7170             if(this.isCleaned && forceReclean !== true){
7171                 return this;
7172             }
7173             var ns = /\S/;
7174             var d = this.dom, n = d.firstChild, ni = -1;
7175             while(n){
7176                 var nx = n.nextSibling;
7177                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7178                     d.removeChild(n);
7179                 }else{
7180                     n.nodeIndex = ++ni;
7181                 }
7182                 n = nx;
7183             }
7184             this.isCleaned = true;
7185             return this;
7186         },
7187
7188         // private
7189         calcOffsetsTo : function(el){
7190             el = Roo.get(el);
7191             var d = el.dom;
7192             var restorePos = false;
7193             if(el.getStyle('position') == 'static'){
7194                 el.position('relative');
7195                 restorePos = true;
7196             }
7197             var x = 0, y =0;
7198             var op = this.dom;
7199             while(op && op != d && op.tagName != 'HTML'){
7200                 x+= op.offsetLeft;
7201                 y+= op.offsetTop;
7202                 op = op.offsetParent;
7203             }
7204             if(restorePos){
7205                 el.position('static');
7206             }
7207             return [x, y];
7208         },
7209
7210         /**
7211          * Scrolls this element into view within the passed container.
7212          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7213          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7214          * @return {Roo.Element} this
7215          */
7216         scrollIntoView : function(container, hscroll){
7217             var c = Roo.getDom(container) || document.body;
7218             var el = this.dom;
7219
7220             var o = this.calcOffsetsTo(c),
7221                 l = o[0],
7222                 t = o[1],
7223                 b = t+el.offsetHeight,
7224                 r = l+el.offsetWidth;
7225
7226             var ch = c.clientHeight;
7227             var ct = parseInt(c.scrollTop, 10);
7228             var cl = parseInt(c.scrollLeft, 10);
7229             var cb = ct + ch;
7230             var cr = cl + c.clientWidth;
7231
7232             if(t < ct){
7233                 c.scrollTop = t;
7234             }else if(b > cb){
7235                 c.scrollTop = b-ch;
7236             }
7237
7238             if(hscroll !== false){
7239                 if(l < cl){
7240                     c.scrollLeft = l;
7241                 }else if(r > cr){
7242                     c.scrollLeft = r-c.clientWidth;
7243                 }
7244             }
7245             return this;
7246         },
7247
7248         // private
7249         scrollChildIntoView : function(child, hscroll){
7250             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7251         },
7252
7253         /**
7254          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7255          * the new height may not be available immediately.
7256          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7257          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7258          * @param {Function} onComplete (optional) Function to call when animation completes
7259          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7260          * @return {Roo.Element} this
7261          */
7262         autoHeight : function(animate, duration, onComplete, easing){
7263             var oldHeight = this.getHeight();
7264             this.clip();
7265             this.setHeight(1); // force clipping
7266             setTimeout(function(){
7267                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7268                 if(!animate){
7269                     this.setHeight(height);
7270                     this.unclip();
7271                     if(typeof onComplete == "function"){
7272                         onComplete();
7273                     }
7274                 }else{
7275                     this.setHeight(oldHeight); // restore original height
7276                     this.setHeight(height, animate, duration, function(){
7277                         this.unclip();
7278                         if(typeof onComplete == "function") onComplete();
7279                     }.createDelegate(this), easing);
7280                 }
7281             }.createDelegate(this), 0);
7282             return this;
7283         },
7284
7285         /**
7286          * Returns true if this element is an ancestor of the passed element
7287          * @param {HTMLElement/String} el The element to check
7288          * @return {Boolean} True if this element is an ancestor of el, else false
7289          */
7290         contains : function(el){
7291             if(!el){return false;}
7292             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7293         },
7294
7295         /**
7296          * Checks whether the element is currently visible using both visibility and display properties.
7297          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7298          * @return {Boolean} True if the element is currently visible, else false
7299          */
7300         isVisible : function(deep) {
7301             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7302             if(deep !== true || !vis){
7303                 return vis;
7304             }
7305             var p = this.dom.parentNode;
7306             while(p && p.tagName.toLowerCase() != "body"){
7307                 if(!Roo.fly(p, '_isVisible').isVisible()){
7308                     return false;
7309                 }
7310                 p = p.parentNode;
7311             }
7312             return true;
7313         },
7314
7315         /**
7316          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7317          * @param {String} selector The CSS selector
7318          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7319          * @return {CompositeElement/CompositeElementLite} The composite element
7320          */
7321         select : function(selector, unique){
7322             return El.select(selector, unique, this.dom);
7323         },
7324
7325         /**
7326          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7327          * @param {String} selector The CSS selector
7328          * @return {Array} An array of the matched nodes
7329          */
7330         query : function(selector, unique){
7331             return Roo.DomQuery.select(selector, this.dom);
7332         },
7333
7334         /**
7335          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7336          * @param {String} selector The CSS selector
7337          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7338          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7339          */
7340         child : function(selector, returnDom){
7341             var n = Roo.DomQuery.selectNode(selector, this.dom);
7342             return returnDom ? n : Roo.get(n);
7343         },
7344
7345         /**
7346          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7347          * @param {String} selector The CSS selector
7348          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7349          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7350          */
7351         down : function(selector, returnDom){
7352             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7353             return returnDom ? n : Roo.get(n);
7354         },
7355
7356         /**
7357          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7358          * @param {String} group The group the DD object is member of
7359          * @param {Object} config The DD config object
7360          * @param {Object} overrides An object containing methods to override/implement on the DD object
7361          * @return {Roo.dd.DD} The DD object
7362          */
7363         initDD : function(group, config, overrides){
7364             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7365             return Roo.apply(dd, overrides);
7366         },
7367
7368         /**
7369          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7370          * @param {String} group The group the DDProxy object is member of
7371          * @param {Object} config The DDProxy config object
7372          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7373          * @return {Roo.dd.DDProxy} The DDProxy object
7374          */
7375         initDDProxy : function(group, config, overrides){
7376             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7377             return Roo.apply(dd, overrides);
7378         },
7379
7380         /**
7381          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7382          * @param {String} group The group the DDTarget object is member of
7383          * @param {Object} config The DDTarget config object
7384          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7385          * @return {Roo.dd.DDTarget} The DDTarget object
7386          */
7387         initDDTarget : function(group, config, overrides){
7388             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7389             return Roo.apply(dd, overrides);
7390         },
7391
7392         /**
7393          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7394          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7395          * @param {Boolean} visible Whether the element is visible
7396          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7397          * @return {Roo.Element} this
7398          */
7399          setVisible : function(visible, animate){
7400             if(!animate || !A){
7401                 if(this.visibilityMode == El.DISPLAY){
7402                     this.setDisplayed(visible);
7403                 }else{
7404                     this.fixDisplay();
7405                     this.dom.style.visibility = visible ? "visible" : "hidden";
7406                 }
7407             }else{
7408                 // closure for composites
7409                 var dom = this.dom;
7410                 var visMode = this.visibilityMode;
7411                 if(visible){
7412                     this.setOpacity(.01);
7413                     this.setVisible(true);
7414                 }
7415                 this.anim({opacity: { to: (visible?1:0) }},
7416                       this.preanim(arguments, 1),
7417                       null, .35, 'easeIn', function(){
7418                          if(!visible){
7419                              if(visMode == El.DISPLAY){
7420                                  dom.style.display = "none";
7421                              }else{
7422                                  dom.style.visibility = "hidden";
7423                              }
7424                              Roo.get(dom).setOpacity(1);
7425                          }
7426                      });
7427             }
7428             return this;
7429         },
7430
7431         /**
7432          * Returns true if display is not "none"
7433          * @return {Boolean}
7434          */
7435         isDisplayed : function() {
7436             return this.getStyle("display") != "none";
7437         },
7438
7439         /**
7440          * Toggles the element's visibility or display, depending on visibility mode.
7441          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7442          * @return {Roo.Element} this
7443          */
7444         toggle : function(animate){
7445             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7446             return this;
7447         },
7448
7449         /**
7450          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7451          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7452          * @return {Roo.Element} this
7453          */
7454         setDisplayed : function(value) {
7455             if(typeof value == "boolean"){
7456                value = value ? this.originalDisplay : "none";
7457             }
7458             this.setStyle("display", value);
7459             return this;
7460         },
7461
7462         /**
7463          * Tries to focus the element. Any exceptions are caught and ignored.
7464          * @return {Roo.Element} this
7465          */
7466         focus : function() {
7467             try{
7468                 this.dom.focus();
7469             }catch(e){}
7470             return this;
7471         },
7472
7473         /**
7474          * Tries to blur the element. Any exceptions are caught and ignored.
7475          * @return {Roo.Element} this
7476          */
7477         blur : function() {
7478             try{
7479                 this.dom.blur();
7480             }catch(e){}
7481             return this;
7482         },
7483
7484         /**
7485          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7486          * @param {String/Array} className The CSS class to add, or an array of classes
7487          * @return {Roo.Element} this
7488          */
7489         addClass : function(className){
7490             if(className instanceof Array){
7491                 for(var i = 0, len = className.length; i < len; i++) {
7492                     this.addClass(className[i]);
7493                 }
7494             }else{
7495                 if(className && !this.hasClass(className)){
7496                     this.dom.className = this.dom.className + " " + className;
7497                 }
7498             }
7499             return this;
7500         },
7501
7502         /**
7503          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7504          * @param {String/Array} className The CSS class to add, or an array of classes
7505          * @return {Roo.Element} this
7506          */
7507         radioClass : function(className){
7508             var siblings = this.dom.parentNode.childNodes;
7509             for(var i = 0; i < siblings.length; i++) {
7510                 var s = siblings[i];
7511                 if(s.nodeType == 1){
7512                     Roo.get(s).removeClass(className);
7513                 }
7514             }
7515             this.addClass(className);
7516             return this;
7517         },
7518
7519         /**
7520          * Removes one or more CSS classes from the element.
7521          * @param {String/Array} className The CSS class to remove, or an array of classes
7522          * @return {Roo.Element} this
7523          */
7524         removeClass : function(className){
7525             if(!className || !this.dom.className){
7526                 return this;
7527             }
7528             if(className instanceof Array){
7529                 for(var i = 0, len = className.length; i < len; i++) {
7530                     this.removeClass(className[i]);
7531                 }
7532             }else{
7533                 if(this.hasClass(className)){
7534                     var re = this.classReCache[className];
7535                     if (!re) {
7536                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7537                        this.classReCache[className] = re;
7538                     }
7539                     this.dom.className =
7540                         this.dom.className.replace(re, " ");
7541                 }
7542             }
7543             return this;
7544         },
7545
7546         // private
7547         classReCache: {},
7548
7549         /**
7550          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7551          * @param {String} className The CSS class to toggle
7552          * @return {Roo.Element} this
7553          */
7554         toggleClass : function(className){
7555             if(this.hasClass(className)){
7556                 this.removeClass(className);
7557             }else{
7558                 this.addClass(className);
7559             }
7560             return this;
7561         },
7562
7563         /**
7564          * Checks if the specified CSS class exists on this element's DOM node.
7565          * @param {String} className The CSS class to check for
7566          * @return {Boolean} True if the class exists, else false
7567          */
7568         hasClass : function(className){
7569             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7570         },
7571
7572         /**
7573          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7574          * @param {String} oldClassName The CSS class to replace
7575          * @param {String} newClassName The replacement CSS class
7576          * @return {Roo.Element} this
7577          */
7578         replaceClass : function(oldClassName, newClassName){
7579             this.removeClass(oldClassName);
7580             this.addClass(newClassName);
7581             return this;
7582         },
7583
7584         /**
7585          * Returns an object with properties matching the styles requested.
7586          * For example, el.getStyles('color', 'font-size', 'width') might return
7587          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7588          * @param {String} style1 A style name
7589          * @param {String} style2 A style name
7590          * @param {String} etc.
7591          * @return {Object} The style object
7592          */
7593         getStyles : function(){
7594             var a = arguments, len = a.length, r = {};
7595             for(var i = 0; i < len; i++){
7596                 r[a[i]] = this.getStyle(a[i]);
7597             }
7598             return r;
7599         },
7600
7601         /**
7602          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7603          * @param {String} property The style property whose value is returned.
7604          * @return {String} The current value of the style property for this element.
7605          */
7606         getStyle : function(){
7607             return view && view.getComputedStyle ?
7608                 function(prop){
7609                     var el = this.dom, v, cs, camel;
7610                     if(prop == 'float'){
7611                         prop = "cssFloat";
7612                     }
7613                     if(el.style && (v = el.style[prop])){
7614                         return v;
7615                     }
7616                     if(cs = view.getComputedStyle(el, "")){
7617                         if(!(camel = propCache[prop])){
7618                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7619                         }
7620                         return cs[camel];
7621                     }
7622                     return null;
7623                 } :
7624                 function(prop){
7625                     var el = this.dom, v, cs, camel;
7626                     if(prop == 'opacity'){
7627                         if(typeof el.style.filter == 'string'){
7628                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7629                             if(m){
7630                                 var fv = parseFloat(m[1]);
7631                                 if(!isNaN(fv)){
7632                                     return fv ? fv / 100 : 0;
7633                                 }
7634                             }
7635                         }
7636                         return 1;
7637                     }else if(prop == 'float'){
7638                         prop = "styleFloat";
7639                     }
7640                     if(!(camel = propCache[prop])){
7641                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7642                     }
7643                     if(v = el.style[camel]){
7644                         return v;
7645                     }
7646                     if(cs = el.currentStyle){
7647                         return cs[camel];
7648                     }
7649                     return null;
7650                 };
7651         }(),
7652
7653         /**
7654          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7655          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7656          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7657          * @return {Roo.Element} this
7658          */
7659         setStyle : function(prop, value){
7660             if(typeof prop == "string"){
7661                 
7662                 if (prop == 'float') {
7663                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7664                     return this;
7665                 }
7666                 
7667                 var camel;
7668                 if(!(camel = propCache[prop])){
7669                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7670                 }
7671                 
7672                 if(camel == 'opacity') {
7673                     this.setOpacity(value);
7674                 }else{
7675                     this.dom.style[camel] = value;
7676                 }
7677             }else{
7678                 for(var style in prop){
7679                     if(typeof prop[style] != "function"){
7680                        this.setStyle(style, prop[style]);
7681                     }
7682                 }
7683             }
7684             return this;
7685         },
7686
7687         /**
7688          * More flexible version of {@link #setStyle} for setting style properties.
7689          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7690          * a function which returns such a specification.
7691          * @return {Roo.Element} this
7692          */
7693         applyStyles : function(style){
7694             Roo.DomHelper.applyStyles(this.dom, style);
7695             return this;
7696         },
7697
7698         /**
7699           * 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).
7700           * @return {Number} The X position of the element
7701           */
7702         getX : function(){
7703             return D.getX(this.dom);
7704         },
7705
7706         /**
7707           * 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).
7708           * @return {Number} The Y position of the element
7709           */
7710         getY : function(){
7711             return D.getY(this.dom);
7712         },
7713
7714         /**
7715           * 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).
7716           * @return {Array} The XY position of the element
7717           */
7718         getXY : function(){
7719             return D.getXY(this.dom);
7720         },
7721
7722         /**
7723          * 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).
7724          * @param {Number} The X position of the element
7725          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7726          * @return {Roo.Element} this
7727          */
7728         setX : function(x, animate){
7729             if(!animate || !A){
7730                 D.setX(this.dom, x);
7731             }else{
7732                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7733             }
7734             return this;
7735         },
7736
7737         /**
7738          * 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).
7739          * @param {Number} The Y position of the element
7740          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7741          * @return {Roo.Element} this
7742          */
7743         setY : function(y, animate){
7744             if(!animate || !A){
7745                 D.setY(this.dom, y);
7746             }else{
7747                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7748             }
7749             return this;
7750         },
7751
7752         /**
7753          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7754          * @param {String} left The left CSS property value
7755          * @return {Roo.Element} this
7756          */
7757         setLeft : function(left){
7758             this.setStyle("left", this.addUnits(left));
7759             return this;
7760         },
7761
7762         /**
7763          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7764          * @param {String} top The top CSS property value
7765          * @return {Roo.Element} this
7766          */
7767         setTop : function(top){
7768             this.setStyle("top", this.addUnits(top));
7769             return this;
7770         },
7771
7772         /**
7773          * Sets the element's CSS right style.
7774          * @param {String} right The right CSS property value
7775          * @return {Roo.Element} this
7776          */
7777         setRight : function(right){
7778             this.setStyle("right", this.addUnits(right));
7779             return this;
7780         },
7781
7782         /**
7783          * Sets the element's CSS bottom style.
7784          * @param {String} bottom The bottom CSS property value
7785          * @return {Roo.Element} this
7786          */
7787         setBottom : function(bottom){
7788             this.setStyle("bottom", this.addUnits(bottom));
7789             return this;
7790         },
7791
7792         /**
7793          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7794          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7795          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7796          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7797          * @return {Roo.Element} this
7798          */
7799         setXY : function(pos, animate){
7800             if(!animate || !A){
7801                 D.setXY(this.dom, pos);
7802             }else{
7803                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7804             }
7805             return this;
7806         },
7807
7808         /**
7809          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7810          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7811          * @param {Number} x X value for new position (coordinates are page-based)
7812          * @param {Number} y Y value for new position (coordinates are page-based)
7813          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7814          * @return {Roo.Element} this
7815          */
7816         setLocation : function(x, y, animate){
7817             this.setXY([x, y], this.preanim(arguments, 2));
7818             return this;
7819         },
7820
7821         /**
7822          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7823          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7824          * @param {Number} x X value for new position (coordinates are page-based)
7825          * @param {Number} y Y value for new position (coordinates are page-based)
7826          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7827          * @return {Roo.Element} this
7828          */
7829         moveTo : function(x, y, animate){
7830             this.setXY([x, y], this.preanim(arguments, 2));
7831             return this;
7832         },
7833
7834         /**
7835          * Returns the region of the given element.
7836          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7837          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7838          */
7839         getRegion : function(){
7840             return D.getRegion(this.dom);
7841         },
7842
7843         /**
7844          * Returns the offset height of the element
7845          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7846          * @return {Number} The element's height
7847          */
7848         getHeight : function(contentHeight){
7849             var h = this.dom.offsetHeight || 0;
7850             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7851         },
7852
7853         /**
7854          * Returns the offset width of the element
7855          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7856          * @return {Number} The element's width
7857          */
7858         getWidth : function(contentWidth){
7859             var w = this.dom.offsetWidth || 0;
7860             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7861         },
7862
7863         /**
7864          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7865          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7866          * if a height has not been set using CSS.
7867          * @return {Number}
7868          */
7869         getComputedHeight : function(){
7870             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7871             if(!h){
7872                 h = parseInt(this.getStyle('height'), 10) || 0;
7873                 if(!this.isBorderBox()){
7874                     h += this.getFrameWidth('tb');
7875                 }
7876             }
7877             return h;
7878         },
7879
7880         /**
7881          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7882          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7883          * if a width has not been set using CSS.
7884          * @return {Number}
7885          */
7886         getComputedWidth : function(){
7887             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7888             if(!w){
7889                 w = parseInt(this.getStyle('width'), 10) || 0;
7890                 if(!this.isBorderBox()){
7891                     w += this.getFrameWidth('lr');
7892                 }
7893             }
7894             return w;
7895         },
7896
7897         /**
7898          * Returns the size of the element.
7899          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7900          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7901          */
7902         getSize : function(contentSize){
7903             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7904         },
7905
7906         /**
7907          * Returns the width and height of the viewport.
7908          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7909          */
7910         getViewSize : function(){
7911             var d = this.dom, doc = document, aw = 0, ah = 0;
7912             if(d == doc || d == doc.body){
7913                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7914             }else{
7915                 return {
7916                     width : d.clientWidth,
7917                     height: d.clientHeight
7918                 };
7919             }
7920         },
7921
7922         /**
7923          * Returns the value of the "value" attribute
7924          * @param {Boolean} asNumber true to parse the value as a number
7925          * @return {String/Number}
7926          */
7927         getValue : function(asNumber){
7928             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7929         },
7930
7931         // private
7932         adjustWidth : function(width){
7933             if(typeof width == "number"){
7934                 if(this.autoBoxAdjust && !this.isBorderBox()){
7935                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7936                 }
7937                 if(width < 0){
7938                     width = 0;
7939                 }
7940             }
7941             return width;
7942         },
7943
7944         // private
7945         adjustHeight : function(height){
7946             if(typeof height == "number"){
7947                if(this.autoBoxAdjust && !this.isBorderBox()){
7948                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7949                }
7950                if(height < 0){
7951                    height = 0;
7952                }
7953             }
7954             return height;
7955         },
7956
7957         /**
7958          * Set the width of the element
7959          * @param {Number} width The new width
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963         setWidth : function(width, animate){
7964             width = this.adjustWidth(width);
7965             if(!animate || !A){
7966                 this.dom.style.width = this.addUnits(width);
7967             }else{
7968                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7969             }
7970             return this;
7971         },
7972
7973         /**
7974          * Set the height of the element
7975          * @param {Number} height The new height
7976          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7977          * @return {Roo.Element} this
7978          */
7979          setHeight : function(height, animate){
7980             height = this.adjustHeight(height);
7981             if(!animate || !A){
7982                 this.dom.style.height = this.addUnits(height);
7983             }else{
7984                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7985             }
7986             return this;
7987         },
7988
7989         /**
7990          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7991          * @param {Number} width The new width
7992          * @param {Number} height The new height
7993          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7994          * @return {Roo.Element} this
7995          */
7996          setSize : function(width, height, animate){
7997             if(typeof width == "object"){ // in case of object from getSize()
7998                 height = width.height; width = width.width;
7999             }
8000             width = this.adjustWidth(width); height = this.adjustHeight(height);
8001             if(!animate || !A){
8002                 this.dom.style.width = this.addUnits(width);
8003                 this.dom.style.height = this.addUnits(height);
8004             }else{
8005                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8006             }
8007             return this;
8008         },
8009
8010         /**
8011          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8012          * @param {Number} x X value for new position (coordinates are page-based)
8013          * @param {Number} y Y value for new position (coordinates are page-based)
8014          * @param {Number} width The new width
8015          * @param {Number} height The new height
8016          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8017          * @return {Roo.Element} this
8018          */
8019         setBounds : function(x, y, width, height, animate){
8020             if(!animate || !A){
8021                 this.setSize(width, height);
8022                 this.setLocation(x, y);
8023             }else{
8024                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8025                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8026                               this.preanim(arguments, 4), 'motion');
8027             }
8028             return this;
8029         },
8030
8031         /**
8032          * 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.
8033          * @param {Roo.lib.Region} region The region to fill
8034          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8035          * @return {Roo.Element} this
8036          */
8037         setRegion : function(region, animate){
8038             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8039             return this;
8040         },
8041
8042         /**
8043          * Appends an event handler
8044          *
8045          * @param {String}   eventName     The type of event to append
8046          * @param {Function} fn        The method the event invokes
8047          * @param {Object} scope       (optional) The scope (this object) of the fn
8048          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8049          */
8050         addListener : function(eventName, fn, scope, options){
8051             if (this.dom) {
8052                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8053             }
8054         },
8055
8056         /**
8057          * Removes an event handler from this element
8058          * @param {String} eventName the type of event to remove
8059          * @param {Function} fn the method the event invokes
8060          * @return {Roo.Element} this
8061          */
8062         removeListener : function(eventName, fn){
8063             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8064             return this;
8065         },
8066
8067         /**
8068          * Removes all previous added listeners from this element
8069          * @return {Roo.Element} this
8070          */
8071         removeAllListeners : function(){
8072             E.purgeElement(this.dom);
8073             return this;
8074         },
8075
8076         relayEvent : function(eventName, observable){
8077             this.on(eventName, function(e){
8078                 observable.fireEvent(eventName, e);
8079             });
8080         },
8081
8082         /**
8083          * Set the opacity of the element
8084          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8085          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8086          * @return {Roo.Element} this
8087          */
8088          setOpacity : function(opacity, animate){
8089             if(!animate || !A){
8090                 var s = this.dom.style;
8091                 if(Roo.isIE){
8092                     s.zoom = 1;
8093                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8094                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8095                 }else{
8096                     s.opacity = opacity;
8097                 }
8098             }else{
8099                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8100             }
8101             return this;
8102         },
8103
8104         /**
8105          * Gets the left X coordinate
8106          * @param {Boolean} local True to get the local css position instead of page coordinate
8107          * @return {Number}
8108          */
8109         getLeft : function(local){
8110             if(!local){
8111                 return this.getX();
8112             }else{
8113                 return parseInt(this.getStyle("left"), 10) || 0;
8114             }
8115         },
8116
8117         /**
8118          * Gets the right X coordinate of the element (element X position + element width)
8119          * @param {Boolean} local True to get the local css position instead of page coordinate
8120          * @return {Number}
8121          */
8122         getRight : function(local){
8123             if(!local){
8124                 return this.getX() + this.getWidth();
8125             }else{
8126                 return (this.getLeft(true) + this.getWidth()) || 0;
8127             }
8128         },
8129
8130         /**
8131          * Gets the top Y coordinate
8132          * @param {Boolean} local True to get the local css position instead of page coordinate
8133          * @return {Number}
8134          */
8135         getTop : function(local) {
8136             if(!local){
8137                 return this.getY();
8138             }else{
8139                 return parseInt(this.getStyle("top"), 10) || 0;
8140             }
8141         },
8142
8143         /**
8144          * Gets the bottom Y coordinate of the element (element Y position + element height)
8145          * @param {Boolean} local True to get the local css position instead of page coordinate
8146          * @return {Number}
8147          */
8148         getBottom : function(local){
8149             if(!local){
8150                 return this.getY() + this.getHeight();
8151             }else{
8152                 return (this.getTop(true) + this.getHeight()) || 0;
8153             }
8154         },
8155
8156         /**
8157         * Initializes positioning on this element. If a desired position is not passed, it will make the
8158         * the element positioned relative IF it is not already positioned.
8159         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8160         * @param {Number} zIndex (optional) The zIndex to apply
8161         * @param {Number} x (optional) Set the page X position
8162         * @param {Number} y (optional) Set the page Y position
8163         */
8164         position : function(pos, zIndex, x, y){
8165             if(!pos){
8166                if(this.getStyle('position') == 'static'){
8167                    this.setStyle('position', 'relative');
8168                }
8169             }else{
8170                 this.setStyle("position", pos);
8171             }
8172             if(zIndex){
8173                 this.setStyle("z-index", zIndex);
8174             }
8175             if(x !== undefined && y !== undefined){
8176                 this.setXY([x, y]);
8177             }else if(x !== undefined){
8178                 this.setX(x);
8179             }else if(y !== undefined){
8180                 this.setY(y);
8181             }
8182         },
8183
8184         /**
8185         * Clear positioning back to the default when the document was loaded
8186         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8187         * @return {Roo.Element} this
8188          */
8189         clearPositioning : function(value){
8190             value = value ||'';
8191             this.setStyle({
8192                 "left": value,
8193                 "right": value,
8194                 "top": value,
8195                 "bottom": value,
8196                 "z-index": "",
8197                 "position" : "static"
8198             });
8199             return this;
8200         },
8201
8202         /**
8203         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8204         * snapshot before performing an update and then restoring the element.
8205         * @return {Object}
8206         */
8207         getPositioning : function(){
8208             var l = this.getStyle("left");
8209             var t = this.getStyle("top");
8210             return {
8211                 "position" : this.getStyle("position"),
8212                 "left" : l,
8213                 "right" : l ? "" : this.getStyle("right"),
8214                 "top" : t,
8215                 "bottom" : t ? "" : this.getStyle("bottom"),
8216                 "z-index" : this.getStyle("z-index")
8217             };
8218         },
8219
8220         /**
8221          * Gets the width of the border(s) for the specified side(s)
8222          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8223          * passing lr would get the border (l)eft width + the border (r)ight width.
8224          * @return {Number} The width of the sides passed added together
8225          */
8226         getBorderWidth : function(side){
8227             return this.addStyles(side, El.borders);
8228         },
8229
8230         /**
8231          * Gets the width of the padding(s) for the specified side(s)
8232          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8233          * passing lr would get the padding (l)eft + the padding (r)ight.
8234          * @return {Number} The padding of the sides passed added together
8235          */
8236         getPadding : function(side){
8237             return this.addStyles(side, El.paddings);
8238         },
8239
8240         /**
8241         * Set positioning with an object returned by getPositioning().
8242         * @param {Object} posCfg
8243         * @return {Roo.Element} this
8244          */
8245         setPositioning : function(pc){
8246             this.applyStyles(pc);
8247             if(pc.right == "auto"){
8248                 this.dom.style.right = "";
8249             }
8250             if(pc.bottom == "auto"){
8251                 this.dom.style.bottom = "";
8252             }
8253             return this;
8254         },
8255
8256         // private
8257         fixDisplay : function(){
8258             if(this.getStyle("display") == "none"){
8259                 this.setStyle("visibility", "hidden");
8260                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8261                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8262                     this.setStyle("display", "block");
8263                 }
8264             }
8265         },
8266
8267         /**
8268          * Quick set left and top adding default units
8269          * @param {String} left The left CSS property value
8270          * @param {String} top The top CSS property value
8271          * @return {Roo.Element} this
8272          */
8273          setLeftTop : function(left, top){
8274             this.dom.style.left = this.addUnits(left);
8275             this.dom.style.top = this.addUnits(top);
8276             return this;
8277         },
8278
8279         /**
8280          * Move this element relative to its current position.
8281          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8282          * @param {Number} distance How far to move the element in pixels
8283          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8284          * @return {Roo.Element} this
8285          */
8286          move : function(direction, distance, animate){
8287             var xy = this.getXY();
8288             direction = direction.toLowerCase();
8289             switch(direction){
8290                 case "l":
8291                 case "left":
8292                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8293                     break;
8294                case "r":
8295                case "right":
8296                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8297                     break;
8298                case "t":
8299                case "top":
8300                case "up":
8301                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8302                     break;
8303                case "b":
8304                case "bottom":
8305                case "down":
8306                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8307                     break;
8308             }
8309             return this;
8310         },
8311
8312         /**
8313          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8314          * @return {Roo.Element} this
8315          */
8316         clip : function(){
8317             if(!this.isClipped){
8318                this.isClipped = true;
8319                this.originalClip = {
8320                    "o": this.getStyle("overflow"),
8321                    "x": this.getStyle("overflow-x"),
8322                    "y": this.getStyle("overflow-y")
8323                };
8324                this.setStyle("overflow", "hidden");
8325                this.setStyle("overflow-x", "hidden");
8326                this.setStyle("overflow-y", "hidden");
8327             }
8328             return this;
8329         },
8330
8331         /**
8332          *  Return clipping (overflow) to original clipping before clip() was called
8333          * @return {Roo.Element} this
8334          */
8335         unclip : function(){
8336             if(this.isClipped){
8337                 this.isClipped = false;
8338                 var o = this.originalClip;
8339                 if(o.o){this.setStyle("overflow", o.o);}
8340                 if(o.x){this.setStyle("overflow-x", o.x);}
8341                 if(o.y){this.setStyle("overflow-y", o.y);}
8342             }
8343             return this;
8344         },
8345
8346
8347         /**
8348          * Gets the x,y coordinates specified by the anchor position on the element.
8349          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8350          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8351          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8352          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8353          * @return {Array} [x, y] An array containing the element's x and y coordinates
8354          */
8355         getAnchorXY : function(anchor, local, s){
8356             //Passing a different size is useful for pre-calculating anchors,
8357             //especially for anchored animations that change the el size.
8358
8359             var w, h, vp = false;
8360             if(!s){
8361                 var d = this.dom;
8362                 if(d == document.body || d == document){
8363                     vp = true;
8364                     w = D.getViewWidth(); h = D.getViewHeight();
8365                 }else{
8366                     w = this.getWidth(); h = this.getHeight();
8367                 }
8368             }else{
8369                 w = s.width;  h = s.height;
8370             }
8371             var x = 0, y = 0, r = Math.round;
8372             switch((anchor || "tl").toLowerCase()){
8373                 case "c":
8374                     x = r(w*.5);
8375                     y = r(h*.5);
8376                 break;
8377                 case "t":
8378                     x = r(w*.5);
8379                     y = 0;
8380                 break;
8381                 case "l":
8382                     x = 0;
8383                     y = r(h*.5);
8384                 break;
8385                 case "r":
8386                     x = w;
8387                     y = r(h*.5);
8388                 break;
8389                 case "b":
8390                     x = r(w*.5);
8391                     y = h;
8392                 break;
8393                 case "tl":
8394                     x = 0;
8395                     y = 0;
8396                 break;
8397                 case "bl":
8398                     x = 0;
8399                     y = h;
8400                 break;
8401                 case "br":
8402                     x = w;
8403                     y = h;
8404                 break;
8405                 case "tr":
8406                     x = w;
8407                     y = 0;
8408                 break;
8409             }
8410             if(local === true){
8411                 return [x, y];
8412             }
8413             if(vp){
8414                 var sc = this.getScroll();
8415                 return [x + sc.left, y + sc.top];
8416             }
8417             //Add the element's offset xy
8418             var o = this.getXY();
8419             return [x+o[0], y+o[1]];
8420         },
8421
8422         /**
8423          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8424          * supported position values.
8425          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8426          * @param {String} position The position to align to.
8427          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8428          * @return {Array} [x, y]
8429          */
8430         getAlignToXY : function(el, p, o){
8431             el = Roo.get(el);
8432             var d = this.dom;
8433             if(!el.dom){
8434                 throw "Element.alignTo with an element that doesn't exist";
8435             }
8436             var c = false; //constrain to viewport
8437             var p1 = "", p2 = "";
8438             o = o || [0,0];
8439
8440             if(!p){
8441                 p = "tl-bl";
8442             }else if(p == "?"){
8443                 p = "tl-bl?";
8444             }else if(p.indexOf("-") == -1){
8445                 p = "tl-" + p;
8446             }
8447             p = p.toLowerCase();
8448             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8449             if(!m){
8450                throw "Element.alignTo with an invalid alignment " + p;
8451             }
8452             p1 = m[1]; p2 = m[2]; c = !!m[3];
8453
8454             //Subtract the aligned el's internal xy from the target's offset xy
8455             //plus custom offset to get the aligned el's new offset xy
8456             var a1 = this.getAnchorXY(p1, true);
8457             var a2 = el.getAnchorXY(p2, false);
8458             var x = a2[0] - a1[0] + o[0];
8459             var y = a2[1] - a1[1] + o[1];
8460             if(c){
8461                 //constrain the aligned el to viewport if necessary
8462                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8463                 // 5px of margin for ie
8464                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8465
8466                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8467                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8468                 //otherwise swap the aligned el to the opposite border of the target.
8469                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8470                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8471                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8472                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8473
8474                var doc = document;
8475                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8476                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8477
8478                if((x+w) > dw + scrollX){
8479                     x = swapX ? r.left-w : dw+scrollX-w;
8480                 }
8481                if(x < scrollX){
8482                    x = swapX ? r.right : scrollX;
8483                }
8484                if((y+h) > dh + scrollY){
8485                     y = swapY ? r.top-h : dh+scrollY-h;
8486                 }
8487                if (y < scrollY){
8488                    y = swapY ? r.bottom : scrollY;
8489                }
8490             }
8491             return [x,y];
8492         },
8493
8494         // private
8495         getConstrainToXY : function(){
8496             var os = {top:0, left:0, bottom:0, right: 0};
8497
8498             return function(el, local, offsets, proposedXY){
8499                 el = Roo.get(el);
8500                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8501
8502                 var vw, vh, vx = 0, vy = 0;
8503                 if(el.dom == document.body || el.dom == document){
8504                     vw = Roo.lib.Dom.getViewWidth();
8505                     vh = Roo.lib.Dom.getViewHeight();
8506                 }else{
8507                     vw = el.dom.clientWidth;
8508                     vh = el.dom.clientHeight;
8509                     if(!local){
8510                         var vxy = el.getXY();
8511                         vx = vxy[0];
8512                         vy = vxy[1];
8513                     }
8514                 }
8515
8516                 var s = el.getScroll();
8517
8518                 vx += offsets.left + s.left;
8519                 vy += offsets.top + s.top;
8520
8521                 vw -= offsets.right;
8522                 vh -= offsets.bottom;
8523
8524                 var vr = vx+vw;
8525                 var vb = vy+vh;
8526
8527                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8528                 var x = xy[0], y = xy[1];
8529                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8530
8531                 // only move it if it needs it
8532                 var moved = false;
8533
8534                 // first validate right/bottom
8535                 if((x + w) > vr){
8536                     x = vr - w;
8537                     moved = true;
8538                 }
8539                 if((y + h) > vb){
8540                     y = vb - h;
8541                     moved = true;
8542                 }
8543                 // then make sure top/left isn't negative
8544                 if(x < vx){
8545                     x = vx;
8546                     moved = true;
8547                 }
8548                 if(y < vy){
8549                     y = vy;
8550                     moved = true;
8551                 }
8552                 return moved ? [x, y] : false;
8553             };
8554         }(),
8555
8556         // private
8557         adjustForConstraints : function(xy, parent, offsets){
8558             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8559         },
8560
8561         /**
8562          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8563          * document it aligns it to the viewport.
8564          * The position parameter is optional, and can be specified in any one of the following formats:
8565          * <ul>
8566          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8567          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8568          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8569          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8570          *   <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
8571          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8572          * </ul>
8573          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8574          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8575          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8576          * that specified in order to enforce the viewport constraints.
8577          * Following are all of the supported anchor positions:
8578     <pre>
8579     Value  Description
8580     -----  -----------------------------
8581     tl     The top left corner (default)
8582     t      The center of the top edge
8583     tr     The top right corner
8584     l      The center of the left edge
8585     c      In the center of the element
8586     r      The center of the right edge
8587     bl     The bottom left corner
8588     b      The center of the bottom edge
8589     br     The bottom right corner
8590     </pre>
8591     Example Usage:
8592     <pre><code>
8593     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8594     el.alignTo("other-el");
8595
8596     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8597     el.alignTo("other-el", "tr?");
8598
8599     // align the bottom right corner of el with the center left edge of other-el
8600     el.alignTo("other-el", "br-l?");
8601
8602     // align the center of el with the bottom left corner of other-el and
8603     // adjust the x position by -6 pixels (and the y position by 0)
8604     el.alignTo("other-el", "c-bl", [-6, 0]);
8605     </code></pre>
8606          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8607          * @param {String} position The position to align to.
8608          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8609          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8610          * @return {Roo.Element} this
8611          */
8612         alignTo : function(element, position, offsets, animate){
8613             var xy = this.getAlignToXY(element, position, offsets);
8614             this.setXY(xy, this.preanim(arguments, 3));
8615             return this;
8616         },
8617
8618         /**
8619          * Anchors an element to another element and realigns it when the window is resized.
8620          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8621          * @param {String} position The position to align to.
8622          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8623          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8624          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8625          * is a number, it is used as the buffer delay (defaults to 50ms).
8626          * @param {Function} callback The function to call after the animation finishes
8627          * @return {Roo.Element} this
8628          */
8629         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8630             var action = function(){
8631                 this.alignTo(el, alignment, offsets, animate);
8632                 Roo.callback(callback, this);
8633             };
8634             Roo.EventManager.onWindowResize(action, this);
8635             var tm = typeof monitorScroll;
8636             if(tm != 'undefined'){
8637                 Roo.EventManager.on(window, 'scroll', action, this,
8638                     {buffer: tm == 'number' ? monitorScroll : 50});
8639             }
8640             action.call(this); // align immediately
8641             return this;
8642         },
8643         /**
8644          * Clears any opacity settings from this element. Required in some cases for IE.
8645          * @return {Roo.Element} this
8646          */
8647         clearOpacity : function(){
8648             if (window.ActiveXObject) {
8649                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8650                     this.dom.style.filter = "";
8651                 }
8652             } else {
8653                 this.dom.style.opacity = "";
8654                 this.dom.style["-moz-opacity"] = "";
8655                 this.dom.style["-khtml-opacity"] = "";
8656             }
8657             return this;
8658         },
8659
8660         /**
8661          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8662          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8663          * @return {Roo.Element} this
8664          */
8665         hide : function(animate){
8666             this.setVisible(false, this.preanim(arguments, 0));
8667             return this;
8668         },
8669
8670         /**
8671         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8672         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8673          * @return {Roo.Element} this
8674          */
8675         show : function(animate){
8676             this.setVisible(true, this.preanim(arguments, 0));
8677             return this;
8678         },
8679
8680         /**
8681          * @private Test if size has a unit, otherwise appends the default
8682          */
8683         addUnits : function(size){
8684             return Roo.Element.addUnits(size, this.defaultUnit);
8685         },
8686
8687         /**
8688          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8689          * @return {Roo.Element} this
8690          */
8691         beginMeasure : function(){
8692             var el = this.dom;
8693             if(el.offsetWidth || el.offsetHeight){
8694                 return this; // offsets work already
8695             }
8696             var changed = [];
8697             var p = this.dom, b = document.body; // start with this element
8698             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8699                 var pe = Roo.get(p);
8700                 if(pe.getStyle('display') == 'none'){
8701                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8702                     p.style.visibility = "hidden";
8703                     p.style.display = "block";
8704                 }
8705                 p = p.parentNode;
8706             }
8707             this._measureChanged = changed;
8708             return this;
8709
8710         },
8711
8712         /**
8713          * Restores displays to before beginMeasure was called
8714          * @return {Roo.Element} this
8715          */
8716         endMeasure : function(){
8717             var changed = this._measureChanged;
8718             if(changed){
8719                 for(var i = 0, len = changed.length; i < len; i++) {
8720                     var r = changed[i];
8721                     r.el.style.visibility = r.visibility;
8722                     r.el.style.display = "none";
8723                 }
8724                 this._measureChanged = null;
8725             }
8726             return this;
8727         },
8728
8729         /**
8730         * Update the innerHTML of this element, optionally searching for and processing scripts
8731         * @param {String} html The new HTML
8732         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8733         * @param {Function} callback For async script loading you can be noticed when the update completes
8734         * @return {Roo.Element} this
8735          */
8736         update : function(html, loadScripts, callback){
8737             if(typeof html == "undefined"){
8738                 html = "";
8739             }
8740             if(loadScripts !== true){
8741                 this.dom.innerHTML = html;
8742                 if(typeof callback == "function"){
8743                     callback();
8744                 }
8745                 return this;
8746             }
8747             var id = Roo.id();
8748             var dom = this.dom;
8749
8750             html += '<span id="' + id + '"></span>';
8751
8752             E.onAvailable(id, function(){
8753                 var hd = document.getElementsByTagName("head")[0];
8754                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8755                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8756                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8757
8758                 var match;
8759                 while(match = re.exec(html)){
8760                     var attrs = match[1];
8761                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8762                     if(srcMatch && srcMatch[2]){
8763                        var s = document.createElement("script");
8764                        s.src = srcMatch[2];
8765                        var typeMatch = attrs.match(typeRe);
8766                        if(typeMatch && typeMatch[2]){
8767                            s.type = typeMatch[2];
8768                        }
8769                        hd.appendChild(s);
8770                     }else if(match[2] && match[2].length > 0){
8771                         if(window.execScript) {
8772                            window.execScript(match[2]);
8773                         } else {
8774                             /**
8775                              * eval:var:id
8776                              * eval:var:dom
8777                              * eval:var:html
8778                              * 
8779                              */
8780                            window.eval(match[2]);
8781                         }
8782                     }
8783                 }
8784                 var el = document.getElementById(id);
8785                 if(el){el.parentNode.removeChild(el);}
8786                 if(typeof callback == "function"){
8787                     callback();
8788                 }
8789             });
8790             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8791             return this;
8792         },
8793
8794         /**
8795          * Direct access to the UpdateManager update() method (takes the same parameters).
8796          * @param {String/Function} url The url for this request or a function to call to get the url
8797          * @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}
8798          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8799          * @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.
8800          * @return {Roo.Element} this
8801          */
8802         load : function(){
8803             var um = this.getUpdateManager();
8804             um.update.apply(um, arguments);
8805             return this;
8806         },
8807
8808         /**
8809         * Gets this element's UpdateManager
8810         * @return {Roo.UpdateManager} The UpdateManager
8811         */
8812         getUpdateManager : function(){
8813             if(!this.updateManager){
8814                 this.updateManager = new Roo.UpdateManager(this);
8815             }
8816             return this.updateManager;
8817         },
8818
8819         /**
8820          * Disables text selection for this element (normalized across browsers)
8821          * @return {Roo.Element} this
8822          */
8823         unselectable : function(){
8824             this.dom.unselectable = "on";
8825             this.swallowEvent("selectstart", true);
8826             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8827             this.addClass("x-unselectable");
8828             return this;
8829         },
8830
8831         /**
8832         * Calculates the x, y to center this element on the screen
8833         * @return {Array} The x, y values [x, y]
8834         */
8835         getCenterXY : function(){
8836             return this.getAlignToXY(document, 'c-c');
8837         },
8838
8839         /**
8840         * Centers the Element in either the viewport, or another Element.
8841         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8842         */
8843         center : function(centerIn){
8844             this.alignTo(centerIn || document, 'c-c');
8845             return this;
8846         },
8847
8848         /**
8849          * Tests various css rules/browsers to determine if this element uses a border box
8850          * @return {Boolean}
8851          */
8852         isBorderBox : function(){
8853             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8854         },
8855
8856         /**
8857          * Return a box {x, y, width, height} that can be used to set another elements
8858          * size/location to match this element.
8859          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8860          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8861          * @return {Object} box An object in the format {x, y, width, height}
8862          */
8863         getBox : function(contentBox, local){
8864             var xy;
8865             if(!local){
8866                 xy = this.getXY();
8867             }else{
8868                 var left = parseInt(this.getStyle("left"), 10) || 0;
8869                 var top = parseInt(this.getStyle("top"), 10) || 0;
8870                 xy = [left, top];
8871             }
8872             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8873             if(!contentBox){
8874                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8875             }else{
8876                 var l = this.getBorderWidth("l")+this.getPadding("l");
8877                 var r = this.getBorderWidth("r")+this.getPadding("r");
8878                 var t = this.getBorderWidth("t")+this.getPadding("t");
8879                 var b = this.getBorderWidth("b")+this.getPadding("b");
8880                 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)};
8881             }
8882             bx.right = bx.x + bx.width;
8883             bx.bottom = bx.y + bx.height;
8884             return bx;
8885         },
8886
8887         /**
8888          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8889          for more information about the sides.
8890          * @param {String} sides
8891          * @return {Number}
8892          */
8893         getFrameWidth : function(sides, onlyContentBox){
8894             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8895         },
8896
8897         /**
8898          * 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.
8899          * @param {Object} box The box to fill {x, y, width, height}
8900          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8901          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8902          * @return {Roo.Element} this
8903          */
8904         setBox : function(box, adjust, animate){
8905             var w = box.width, h = box.height;
8906             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8907                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8908                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8909             }
8910             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8911             return this;
8912         },
8913
8914         /**
8915          * Forces the browser to repaint this element
8916          * @return {Roo.Element} this
8917          */
8918          repaint : function(){
8919             var dom = this.dom;
8920             this.addClass("x-repaint");
8921             setTimeout(function(){
8922                 Roo.get(dom).removeClass("x-repaint");
8923             }, 1);
8924             return this;
8925         },
8926
8927         /**
8928          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8929          * then it returns the calculated width of the sides (see getPadding)
8930          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8931          * @return {Object/Number}
8932          */
8933         getMargins : function(side){
8934             if(!side){
8935                 return {
8936                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8937                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8938                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8939                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8940                 };
8941             }else{
8942                 return this.addStyles(side, El.margins);
8943              }
8944         },
8945
8946         // private
8947         addStyles : function(sides, styles){
8948             var val = 0, v, w;
8949             for(var i = 0, len = sides.length; i < len; i++){
8950                 v = this.getStyle(styles[sides.charAt(i)]);
8951                 if(v){
8952                      w = parseInt(v, 10);
8953                      if(w){ val += w; }
8954                 }
8955             }
8956             return val;
8957         },
8958
8959         /**
8960          * Creates a proxy element of this element
8961          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8962          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8963          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8964          * @return {Roo.Element} The new proxy element
8965          */
8966         createProxy : function(config, renderTo, matchBox){
8967             if(renderTo){
8968                 renderTo = Roo.getDom(renderTo);
8969             }else{
8970                 renderTo = document.body;
8971             }
8972             config = typeof config == "object" ?
8973                 config : {tag : "div", cls: config};
8974             var proxy = Roo.DomHelper.append(renderTo, config, true);
8975             if(matchBox){
8976                proxy.setBox(this.getBox());
8977             }
8978             return proxy;
8979         },
8980
8981         /**
8982          * Puts a mask over this element to disable user interaction. Requires core.css.
8983          * This method can only be applied to elements which accept child nodes.
8984          * @param {String} msg (optional) A message to display in the mask
8985          * @param {String} msgCls (optional) A css class to apply to the msg element
8986          * @return {Element} The mask  element
8987          */
8988         mask : function(msg, msgCls)
8989         {
8990             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
8991                 this.setStyle("position", "relative");
8992             }
8993             if(!this._mask){
8994                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8995             }
8996             this.addClass("x-masked");
8997             this._mask.setDisplayed(true);
8998             
8999             // we wander
9000             var z = 0;
9001             var dom = this.dom
9002             while (dom && dom.style) {
9003                 if (!isNaN(parseInt(dom.style.zIndex))) {
9004                     z = Math.max(z, parseInt(dom.style.zIndex));
9005                 }
9006                 dom = dom.parentNode;
9007             }
9008             // if we are masking the body - then it hides everything..
9009             if (this.dom == document.body) {
9010                 z = 1000000;
9011                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9012                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9013             }
9014            
9015             if(typeof msg == 'string'){
9016                 if(!this._maskMsg){
9017                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9018                 }
9019                 var mm = this._maskMsg;
9020                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9021                 if (mm.dom.firstChild) { // weird IE issue?
9022                     mm.dom.firstChild.innerHTML = msg;
9023                 }
9024                 mm.setDisplayed(true);
9025                 mm.center(this);
9026                 mm.setStyle('z-index', z + 102);
9027             }
9028             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9029                 this._mask.setHeight(this.getHeight());
9030             }
9031             this._mask.setStyle('z-index', z + 100);
9032             
9033             return this._mask;
9034         },
9035
9036         /**
9037          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9038          * it is cached for reuse.
9039          */
9040         unmask : function(removeEl){
9041             if(this._mask){
9042                 if(removeEl === true){
9043                     this._mask.remove();
9044                     delete this._mask;
9045                     if(this._maskMsg){
9046                         this._maskMsg.remove();
9047                         delete this._maskMsg;
9048                     }
9049                 }else{
9050                     this._mask.setDisplayed(false);
9051                     if(this._maskMsg){
9052                         this._maskMsg.setDisplayed(false);
9053                     }
9054                 }
9055             }
9056             this.removeClass("x-masked");
9057         },
9058
9059         /**
9060          * Returns true if this element is masked
9061          * @return {Boolean}
9062          */
9063         isMasked : function(){
9064             return this._mask && this._mask.isVisible();
9065         },
9066
9067         /**
9068          * Creates an iframe shim for this element to keep selects and other windowed objects from
9069          * showing through.
9070          * @return {Roo.Element} The new shim element
9071          */
9072         createShim : function(){
9073             var el = document.createElement('iframe');
9074             el.frameBorder = 'no';
9075             el.className = 'roo-shim';
9076             if(Roo.isIE && Roo.isSecure){
9077                 el.src = Roo.SSL_SECURE_URL;
9078             }
9079             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9080             shim.autoBoxAdjust = false;
9081             return shim;
9082         },
9083
9084         /**
9085          * Removes this element from the DOM and deletes it from the cache
9086          */
9087         remove : function(){
9088             if(this.dom.parentNode){
9089                 this.dom.parentNode.removeChild(this.dom);
9090             }
9091             delete El.cache[this.dom.id];
9092         },
9093
9094         /**
9095          * Sets up event handlers to add and remove a css class when the mouse is over this element
9096          * @param {String} className
9097          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9098          * mouseout events for children elements
9099          * @return {Roo.Element} this
9100          */
9101         addClassOnOver : function(className, preventFlicker){
9102             this.on("mouseover", function(){
9103                 Roo.fly(this, '_internal').addClass(className);
9104             }, this.dom);
9105             var removeFn = function(e){
9106                 if(preventFlicker !== true || !e.within(this, true)){
9107                     Roo.fly(this, '_internal').removeClass(className);
9108                 }
9109             };
9110             this.on("mouseout", removeFn, this.dom);
9111             return this;
9112         },
9113
9114         /**
9115          * Sets up event handlers to add and remove a css class when this element has the focus
9116          * @param {String} className
9117          * @return {Roo.Element} this
9118          */
9119         addClassOnFocus : function(className){
9120             this.on("focus", function(){
9121                 Roo.fly(this, '_internal').addClass(className);
9122             }, this.dom);
9123             this.on("blur", function(){
9124                 Roo.fly(this, '_internal').removeClass(className);
9125             }, this.dom);
9126             return this;
9127         },
9128         /**
9129          * 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)
9130          * @param {String} className
9131          * @return {Roo.Element} this
9132          */
9133         addClassOnClick : function(className){
9134             var dom = this.dom;
9135             this.on("mousedown", function(){
9136                 Roo.fly(dom, '_internal').addClass(className);
9137                 var d = Roo.get(document);
9138                 var fn = function(){
9139                     Roo.fly(dom, '_internal').removeClass(className);
9140                     d.removeListener("mouseup", fn);
9141                 };
9142                 d.on("mouseup", fn);
9143             });
9144             return this;
9145         },
9146
9147         /**
9148          * Stops the specified event from bubbling and optionally prevents the default action
9149          * @param {String} eventName
9150          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9151          * @return {Roo.Element} this
9152          */
9153         swallowEvent : function(eventName, preventDefault){
9154             var fn = function(e){
9155                 e.stopPropagation();
9156                 if(preventDefault){
9157                     e.preventDefault();
9158                 }
9159             };
9160             if(eventName instanceof Array){
9161                 for(var i = 0, len = eventName.length; i < len; i++){
9162                      this.on(eventName[i], fn);
9163                 }
9164                 return this;
9165             }
9166             this.on(eventName, fn);
9167             return this;
9168         },
9169
9170         /**
9171          * @private
9172          */
9173       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9174
9175         /**
9176          * Sizes this element to its parent element's dimensions performing
9177          * neccessary box adjustments.
9178          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9179          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9180          * @return {Roo.Element} this
9181          */
9182         fitToParent : function(monitorResize, targetParent) {
9183           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9184           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9185           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9186             return;
9187           }
9188           var p = Roo.get(targetParent || this.dom.parentNode);
9189           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9190           if (monitorResize === true) {
9191             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9192             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9193           }
9194           return this;
9195         },
9196
9197         /**
9198          * Gets the next sibling, skipping text nodes
9199          * @return {HTMLElement} The next sibling or null
9200          */
9201         getNextSibling : function(){
9202             var n = this.dom.nextSibling;
9203             while(n && n.nodeType != 1){
9204                 n = n.nextSibling;
9205             }
9206             return n;
9207         },
9208
9209         /**
9210          * Gets the previous sibling, skipping text nodes
9211          * @return {HTMLElement} The previous sibling or null
9212          */
9213         getPrevSibling : function(){
9214             var n = this.dom.previousSibling;
9215             while(n && n.nodeType != 1){
9216                 n = n.previousSibling;
9217             }
9218             return n;
9219         },
9220
9221
9222         /**
9223          * Appends the passed element(s) to this element
9224          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9225          * @return {Roo.Element} this
9226          */
9227         appendChild: function(el){
9228             el = Roo.get(el);
9229             el.appendTo(this);
9230             return this;
9231         },
9232
9233         /**
9234          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9235          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9236          * automatically generated with the specified attributes.
9237          * @param {HTMLElement} insertBefore (optional) a child element of this element
9238          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9239          * @return {Roo.Element} The new child element
9240          */
9241         createChild: function(config, insertBefore, returnDom){
9242             config = config || {tag:'div'};
9243             if(insertBefore){
9244                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9245             }
9246             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9247         },
9248
9249         /**
9250          * Appends this element to the passed element
9251          * @param {String/HTMLElement/Element} el The new parent element
9252          * @return {Roo.Element} this
9253          */
9254         appendTo: function(el){
9255             el = Roo.getDom(el);
9256             el.appendChild(this.dom);
9257             return this;
9258         },
9259
9260         /**
9261          * Inserts this element before the passed element in the DOM
9262          * @param {String/HTMLElement/Element} el The element to insert before
9263          * @return {Roo.Element} this
9264          */
9265         insertBefore: function(el){
9266             el = Roo.getDom(el);
9267             el.parentNode.insertBefore(this.dom, el);
9268             return this;
9269         },
9270
9271         /**
9272          * Inserts this element after the passed element in the DOM
9273          * @param {String/HTMLElement/Element} el The element to insert after
9274          * @return {Roo.Element} this
9275          */
9276         insertAfter: function(el){
9277             el = Roo.getDom(el);
9278             el.parentNode.insertBefore(this.dom, el.nextSibling);
9279             return this;
9280         },
9281
9282         /**
9283          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9284          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9285          * @return {Roo.Element} The new child
9286          */
9287         insertFirst: function(el, returnDom){
9288             el = el || {};
9289             if(typeof el == 'object' && !el.nodeType){ // dh config
9290                 return this.createChild(el, this.dom.firstChild, returnDom);
9291             }else{
9292                 el = Roo.getDom(el);
9293                 this.dom.insertBefore(el, this.dom.firstChild);
9294                 return !returnDom ? Roo.get(el) : el;
9295             }
9296         },
9297
9298         /**
9299          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9300          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9301          * @param {String} where (optional) 'before' or 'after' defaults to before
9302          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9303          * @return {Roo.Element} the inserted Element
9304          */
9305         insertSibling: function(el, where, returnDom){
9306             where = where ? where.toLowerCase() : 'before';
9307             el = el || {};
9308             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9309
9310             if(typeof el == 'object' && !el.nodeType){ // dh config
9311                 if(where == 'after' && !this.dom.nextSibling){
9312                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9313                 }else{
9314                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9315                 }
9316
9317             }else{
9318                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9319                             where == 'before' ? this.dom : this.dom.nextSibling);
9320                 if(!returnDom){
9321                     rt = Roo.get(rt);
9322                 }
9323             }
9324             return rt;
9325         },
9326
9327         /**
9328          * Creates and wraps this element with another element
9329          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9330          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9331          * @return {HTMLElement/Element} The newly created wrapper element
9332          */
9333         wrap: function(config, returnDom){
9334             if(!config){
9335                 config = {tag: "div"};
9336             }
9337             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9338             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9339             return newEl;
9340         },
9341
9342         /**
9343          * Replaces the passed element with this element
9344          * @param {String/HTMLElement/Element} el The element to replace
9345          * @return {Roo.Element} this
9346          */
9347         replace: function(el){
9348             el = Roo.get(el);
9349             this.insertBefore(el);
9350             el.remove();
9351             return this;
9352         },
9353
9354         /**
9355          * Inserts an html fragment into this element
9356          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9357          * @param {String} html The HTML fragment
9358          * @param {Boolean} returnEl True to return an Roo.Element
9359          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9360          */
9361         insertHtml : function(where, html, returnEl){
9362             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9363             return returnEl ? Roo.get(el) : el;
9364         },
9365
9366         /**
9367          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9368          * @param {Object} o The object with the attributes
9369          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9370          * @return {Roo.Element} this
9371          */
9372         set : function(o, useSet){
9373             var el = this.dom;
9374             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9375             for(var attr in o){
9376                 if(attr == "style" || typeof o[attr] == "function") continue;
9377                 if(attr=="cls"){
9378                     el.className = o["cls"];
9379                 }else{
9380                     if(useSet) el.setAttribute(attr, o[attr]);
9381                     else el[attr] = o[attr];
9382                 }
9383             }
9384             if(o.style){
9385                 Roo.DomHelper.applyStyles(el, o.style);
9386             }
9387             return this;
9388         },
9389
9390         /**
9391          * Convenience method for constructing a KeyMap
9392          * @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:
9393          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9394          * @param {Function} fn The function to call
9395          * @param {Object} scope (optional) The scope of the function
9396          * @return {Roo.KeyMap} The KeyMap created
9397          */
9398         addKeyListener : function(key, fn, scope){
9399             var config;
9400             if(typeof key != "object" || key instanceof Array){
9401                 config = {
9402                     key: key,
9403                     fn: fn,
9404                     scope: scope
9405                 };
9406             }else{
9407                 config = {
9408                     key : key.key,
9409                     shift : key.shift,
9410                     ctrl : key.ctrl,
9411                     alt : key.alt,
9412                     fn: fn,
9413                     scope: scope
9414                 };
9415             }
9416             return new Roo.KeyMap(this, config);
9417         },
9418
9419         /**
9420          * Creates a KeyMap for this element
9421          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9422          * @return {Roo.KeyMap} The KeyMap created
9423          */
9424         addKeyMap : function(config){
9425             return new Roo.KeyMap(this, config);
9426         },
9427
9428         /**
9429          * Returns true if this element is scrollable.
9430          * @return {Boolean}
9431          */
9432          isScrollable : function(){
9433             var dom = this.dom;
9434             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9435         },
9436
9437         /**
9438          * 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().
9439          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9440          * @param {Number} value The new scroll value
9441          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9442          * @return {Element} this
9443          */
9444
9445         scrollTo : function(side, value, animate){
9446             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9447             if(!animate || !A){
9448                 this.dom[prop] = value;
9449             }else{
9450                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9451                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9452             }
9453             return this;
9454         },
9455
9456         /**
9457          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9458          * within this element's scrollable range.
9459          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9460          * @param {Number} distance How far to scroll the element in pixels
9461          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9462          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9463          * was scrolled as far as it could go.
9464          */
9465          scroll : function(direction, distance, animate){
9466              if(!this.isScrollable()){
9467                  return;
9468              }
9469              var el = this.dom;
9470              var l = el.scrollLeft, t = el.scrollTop;
9471              var w = el.scrollWidth, h = el.scrollHeight;
9472              var cw = el.clientWidth, ch = el.clientHeight;
9473              direction = direction.toLowerCase();
9474              var scrolled = false;
9475              var a = this.preanim(arguments, 2);
9476              switch(direction){
9477                  case "l":
9478                  case "left":
9479                      if(w - l > cw){
9480                          var v = Math.min(l + distance, w-cw);
9481                          this.scrollTo("left", v, a);
9482                          scrolled = true;
9483                      }
9484                      break;
9485                 case "r":
9486                 case "right":
9487                      if(l > 0){
9488                          var v = Math.max(l - distance, 0);
9489                          this.scrollTo("left", v, a);
9490                          scrolled = true;
9491                      }
9492                      break;
9493                 case "t":
9494                 case "top":
9495                 case "up":
9496                      if(t > 0){
9497                          var v = Math.max(t - distance, 0);
9498                          this.scrollTo("top", v, a);
9499                          scrolled = true;
9500                      }
9501                      break;
9502                 case "b":
9503                 case "bottom":
9504                 case "down":
9505                      if(h - t > ch){
9506                          var v = Math.min(t + distance, h-ch);
9507                          this.scrollTo("top", v, a);
9508                          scrolled = true;
9509                      }
9510                      break;
9511              }
9512              return scrolled;
9513         },
9514
9515         /**
9516          * Translates the passed page coordinates into left/top css values for this element
9517          * @param {Number/Array} x The page x or an array containing [x, y]
9518          * @param {Number} y The page y
9519          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9520          */
9521         translatePoints : function(x, y){
9522             if(typeof x == 'object' || x instanceof Array){
9523                 y = x[1]; x = x[0];
9524             }
9525             var p = this.getStyle('position');
9526             var o = this.getXY();
9527
9528             var l = parseInt(this.getStyle('left'), 10);
9529             var t = parseInt(this.getStyle('top'), 10);
9530
9531             if(isNaN(l)){
9532                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9533             }
9534             if(isNaN(t)){
9535                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9536             }
9537
9538             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9539         },
9540
9541         /**
9542          * Returns the current scroll position of the element.
9543          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9544          */
9545         getScroll : function(){
9546             var d = this.dom, doc = document;
9547             if(d == doc || d == doc.body){
9548                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9549                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9550                 return {left: l, top: t};
9551             }else{
9552                 return {left: d.scrollLeft, top: d.scrollTop};
9553             }
9554         },
9555
9556         /**
9557          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9558          * are convert to standard 6 digit hex color.
9559          * @param {String} attr The css attribute
9560          * @param {String} defaultValue The default value to use when a valid color isn't found
9561          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9562          * YUI color anims.
9563          */
9564         getColor : function(attr, defaultValue, prefix){
9565             var v = this.getStyle(attr);
9566             if(!v || v == "transparent" || v == "inherit") {
9567                 return defaultValue;
9568             }
9569             var color = typeof prefix == "undefined" ? "#" : prefix;
9570             if(v.substr(0, 4) == "rgb("){
9571                 var rvs = v.slice(4, v.length -1).split(",");
9572                 for(var i = 0; i < 3; i++){
9573                     var h = parseInt(rvs[i]).toString(16);
9574                     if(h < 16){
9575                         h = "0" + h;
9576                     }
9577                     color += h;
9578                 }
9579             } else {
9580                 if(v.substr(0, 1) == "#"){
9581                     if(v.length == 4) {
9582                         for(var i = 1; i < 4; i++){
9583                             var c = v.charAt(i);
9584                             color +=  c + c;
9585                         }
9586                     }else if(v.length == 7){
9587                         color += v.substr(1);
9588                     }
9589                 }
9590             }
9591             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9592         },
9593
9594         /**
9595          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9596          * gradient background, rounded corners and a 4-way shadow.
9597          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9598          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9599          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9600          * @return {Roo.Element} this
9601          */
9602         boxWrap : function(cls){
9603             cls = cls || 'x-box';
9604             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9605             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9606             return el;
9607         },
9608
9609         /**
9610          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9611          * @param {String} namespace The namespace in which to look for the attribute
9612          * @param {String} name The attribute name
9613          * @return {String} The attribute value
9614          */
9615         getAttributeNS : Roo.isIE ? function(ns, name){
9616             var d = this.dom;
9617             var type = typeof d[ns+":"+name];
9618             if(type != 'undefined' && type != 'unknown'){
9619                 return d[ns+":"+name];
9620             }
9621             return d[name];
9622         } : function(ns, name){
9623             var d = this.dom;
9624             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9625         },
9626         
9627         
9628         /**
9629          * Sets or Returns the value the dom attribute value
9630          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9631          * @param {String} value (optional) The value to set the attribute to
9632          * @return {String} The attribute value
9633          */
9634         attr : function(name){
9635             if (arguments.length > 1) {
9636                 this.dom.setAttribute(name, arguments[1]);
9637                 return arguments[1];
9638             }
9639             if (typeof(name) == 'object') {
9640                 for(var i in name) {
9641                     this.attr(i, name[i]);
9642                 }
9643                 return name;
9644             }
9645             
9646             
9647             if (!this.dom.hasAttribute(name)) {
9648                 return undefined;
9649             }
9650             return this.dom.getAttribute(name);
9651         }
9652         
9653         
9654         
9655     };
9656
9657     var ep = El.prototype;
9658
9659     /**
9660      * Appends an event handler (Shorthand for addListener)
9661      * @param {String}   eventName     The type of event to append
9662      * @param {Function} fn        The method the event invokes
9663      * @param {Object} scope       (optional) The scope (this object) of the fn
9664      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9665      * @method
9666      */
9667     ep.on = ep.addListener;
9668         // backwards compat
9669     ep.mon = ep.addListener;
9670
9671     /**
9672      * Removes an event handler from this element (shorthand for removeListener)
9673      * @param {String} eventName the type of event to remove
9674      * @param {Function} fn the method the event invokes
9675      * @return {Roo.Element} this
9676      * @method
9677      */
9678     ep.un = ep.removeListener;
9679
9680     /**
9681      * true to automatically adjust width and height settings for box-model issues (default to true)
9682      */
9683     ep.autoBoxAdjust = true;
9684
9685     // private
9686     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9687
9688     // private
9689     El.addUnits = function(v, defaultUnit){
9690         if(v === "" || v == "auto"){
9691             return v;
9692         }
9693         if(v === undefined){
9694             return '';
9695         }
9696         if(typeof v == "number" || !El.unitPattern.test(v)){
9697             return v + (defaultUnit || 'px');
9698         }
9699         return v;
9700     };
9701
9702     // special markup used throughout Roo when box wrapping elements
9703     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>';
9704     /**
9705      * Visibility mode constant - Use visibility to hide element
9706      * @static
9707      * @type Number
9708      */
9709     El.VISIBILITY = 1;
9710     /**
9711      * Visibility mode constant - Use display to hide element
9712      * @static
9713      * @type Number
9714      */
9715     El.DISPLAY = 2;
9716
9717     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9718     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9719     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9720
9721
9722
9723     /**
9724      * @private
9725      */
9726     El.cache = {};
9727
9728     var docEl;
9729
9730     /**
9731      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9732      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9733      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9734      * @return {Element} The Element object
9735      * @static
9736      */
9737     El.get = function(el){
9738         var ex, elm, id;
9739         if(!el){ return null; }
9740         if(typeof el == "string"){ // element id
9741             if(!(elm = document.getElementById(el))){
9742                 return null;
9743             }
9744             if(ex = El.cache[el]){
9745                 ex.dom = elm;
9746             }else{
9747                 ex = El.cache[el] = new El(elm);
9748             }
9749             return ex;
9750         }else if(el.tagName){ // dom element
9751             if(!(id = el.id)){
9752                 id = Roo.id(el);
9753             }
9754             if(ex = El.cache[id]){
9755                 ex.dom = el;
9756             }else{
9757                 ex = El.cache[id] = new El(el);
9758             }
9759             return ex;
9760         }else if(el instanceof El){
9761             if(el != docEl){
9762                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9763                                                               // catch case where it hasn't been appended
9764                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9765             }
9766             return el;
9767         }else if(el.isComposite){
9768             return el;
9769         }else if(el instanceof Array){
9770             return El.select(el);
9771         }else if(el == document){
9772             // create a bogus element object representing the document object
9773             if(!docEl){
9774                 var f = function(){};
9775                 f.prototype = El.prototype;
9776                 docEl = new f();
9777                 docEl.dom = document;
9778             }
9779             return docEl;
9780         }
9781         return null;
9782     };
9783
9784     // private
9785     El.uncache = function(el){
9786         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9787             if(a[i]){
9788                 delete El.cache[a[i].id || a[i]];
9789             }
9790         }
9791     };
9792
9793     // private
9794     // Garbage collection - uncache elements/purge listeners on orphaned elements
9795     // so we don't hold a reference and cause the browser to retain them
9796     El.garbageCollect = function(){
9797         if(!Roo.enableGarbageCollector){
9798             clearInterval(El.collectorThread);
9799             return;
9800         }
9801         for(var eid in El.cache){
9802             var el = El.cache[eid], d = el.dom;
9803             // -------------------------------------------------------
9804             // Determining what is garbage:
9805             // -------------------------------------------------------
9806             // !d
9807             // dom node is null, definitely garbage
9808             // -------------------------------------------------------
9809             // !d.parentNode
9810             // no parentNode == direct orphan, definitely garbage
9811             // -------------------------------------------------------
9812             // !d.offsetParent && !document.getElementById(eid)
9813             // display none elements have no offsetParent so we will
9814             // also try to look it up by it's id. However, check
9815             // offsetParent first so we don't do unneeded lookups.
9816             // This enables collection of elements that are not orphans
9817             // directly, but somewhere up the line they have an orphan
9818             // parent.
9819             // -------------------------------------------------------
9820             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9821                 delete El.cache[eid];
9822                 if(d && Roo.enableListenerCollection){
9823                     E.purgeElement(d);
9824                 }
9825             }
9826         }
9827     }
9828     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9829
9830
9831     // dom is optional
9832     El.Flyweight = function(dom){
9833         this.dom = dom;
9834     };
9835     El.Flyweight.prototype = El.prototype;
9836
9837     El._flyweights = {};
9838     /**
9839      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9840      * the dom node can be overwritten by other code.
9841      * @param {String/HTMLElement} el The dom node or id
9842      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9843      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9844      * @static
9845      * @return {Element} The shared Element object
9846      */
9847     El.fly = function(el, named){
9848         named = named || '_global';
9849         el = Roo.getDom(el);
9850         if(!el){
9851             return null;
9852         }
9853         if(!El._flyweights[named]){
9854             El._flyweights[named] = new El.Flyweight();
9855         }
9856         El._flyweights[named].dom = el;
9857         return El._flyweights[named];
9858     };
9859
9860     /**
9861      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9862      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9863      * Shorthand of {@link Roo.Element#get}
9864      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9865      * @return {Element} The Element object
9866      * @member Roo
9867      * @method get
9868      */
9869     Roo.get = El.get;
9870     /**
9871      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9872      * the dom node can be overwritten by other code.
9873      * Shorthand of {@link Roo.Element#fly}
9874      * @param {String/HTMLElement} el The dom node or id
9875      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9876      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9877      * @static
9878      * @return {Element} The shared Element object
9879      * @member Roo
9880      * @method fly
9881      */
9882     Roo.fly = El.fly;
9883
9884     // speedy lookup for elements never to box adjust
9885     var noBoxAdjust = Roo.isStrict ? {
9886         select:1
9887     } : {
9888         input:1, select:1, textarea:1
9889     };
9890     if(Roo.isIE || Roo.isGecko){
9891         noBoxAdjust['button'] = 1;
9892     }
9893
9894
9895     Roo.EventManager.on(window, 'unload', function(){
9896         delete El.cache;
9897         delete El._flyweights;
9898     });
9899 })();
9900
9901
9902
9903
9904 if(Roo.DomQuery){
9905     Roo.Element.selectorFunction = Roo.DomQuery.select;
9906 }
9907
9908 Roo.Element.select = function(selector, unique, root){
9909     var els;
9910     if(typeof selector == "string"){
9911         els = Roo.Element.selectorFunction(selector, root);
9912     }else if(selector.length !== undefined){
9913         els = selector;
9914     }else{
9915         throw "Invalid selector";
9916     }
9917     if(unique === true){
9918         return new Roo.CompositeElement(els);
9919     }else{
9920         return new Roo.CompositeElementLite(els);
9921     }
9922 };
9923 /**
9924  * Selects elements based on the passed CSS selector to enable working on them as 1.
9925  * @param {String/Array} selector The CSS selector or an array of elements
9926  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9927  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9928  * @return {CompositeElementLite/CompositeElement}
9929  * @member Roo
9930  * @method select
9931  */
9932 Roo.select = Roo.Element.select;
9933
9934
9935
9936
9937
9938
9939
9940
9941
9942
9943
9944
9945
9946
9947 /*
9948  * Based on:
9949  * Ext JS Library 1.1.1
9950  * Copyright(c) 2006-2007, Ext JS, LLC.
9951  *
9952  * Originally Released Under LGPL - original licence link has changed is not relivant.
9953  *
9954  * Fork - LGPL
9955  * <script type="text/javascript">
9956  */
9957
9958
9959
9960 //Notifies Element that fx methods are available
9961 Roo.enableFx = true;
9962
9963 /**
9964  * @class Roo.Fx
9965  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9966  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9967  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9968  * Element effects to work.</p><br/>
9969  *
9970  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9971  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9972  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9973  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9974  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9975  * expected results and should be done with care.</p><br/>
9976  *
9977  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9978  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9979 <pre>
9980 Value  Description
9981 -----  -----------------------------
9982 tl     The top left corner
9983 t      The center of the top edge
9984 tr     The top right corner
9985 l      The center of the left edge
9986 r      The center of the right edge
9987 bl     The bottom left corner
9988 b      The center of the bottom edge
9989 br     The bottom right corner
9990 </pre>
9991  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9992  * below are common options that can be passed to any Fx method.</b>
9993  * @cfg {Function} callback A function called when the effect is finished
9994  * @cfg {Object} scope The scope of the effect function
9995  * @cfg {String} easing A valid Easing value for the effect
9996  * @cfg {String} afterCls A css class to apply after the effect
9997  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9998  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9999  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10000  * effects that end with the element being visually hidden, ignored otherwise)
10001  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10002  * a function which returns such a specification that will be applied to the Element after the effect finishes
10003  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10004  * @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
10005  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10006  */
10007 Roo.Fx = {
10008         /**
10009          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10010          * origin for the slide effect.  This function automatically handles wrapping the element with
10011          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10012          * Usage:
10013          *<pre><code>
10014 // default: slide the element in from the top
10015 el.slideIn();
10016
10017 // custom: slide the element in from the right with a 2-second duration
10018 el.slideIn('r', { duration: 2 });
10019
10020 // common config options shown with default values
10021 el.slideIn('t', {
10022     easing: 'easeOut',
10023     duration: .5
10024 });
10025 </code></pre>
10026          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10027          * @param {Object} options (optional) Object literal with any of the Fx config options
10028          * @return {Roo.Element} The Element
10029          */
10030     slideIn : function(anchor, o){
10031         var el = this.getFxEl();
10032         o = o || {};
10033
10034         el.queueFx(o, function(){
10035
10036             anchor = anchor || "t";
10037
10038             // fix display to visibility
10039             this.fixDisplay();
10040
10041             // restore values after effect
10042             var r = this.getFxRestore();
10043             var b = this.getBox();
10044             // fixed size for slide
10045             this.setSize(b);
10046
10047             // wrap if needed
10048             var wrap = this.fxWrap(r.pos, o, "hidden");
10049
10050             var st = this.dom.style;
10051             st.visibility = "visible";
10052             st.position = "absolute";
10053
10054             // clear out temp styles after slide and unwrap
10055             var after = function(){
10056                 el.fxUnwrap(wrap, r.pos, o);
10057                 st.width = r.width;
10058                 st.height = r.height;
10059                 el.afterFx(o);
10060             };
10061             // time to calc the positions
10062             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10063
10064             switch(anchor.toLowerCase()){
10065                 case "t":
10066                     wrap.setSize(b.width, 0);
10067                     st.left = st.bottom = "0";
10068                     a = {height: bh};
10069                 break;
10070                 case "l":
10071                     wrap.setSize(0, b.height);
10072                     st.right = st.top = "0";
10073                     a = {width: bw};
10074                 break;
10075                 case "r":
10076                     wrap.setSize(0, b.height);
10077                     wrap.setX(b.right);
10078                     st.left = st.top = "0";
10079                     a = {width: bw, points: pt};
10080                 break;
10081                 case "b":
10082                     wrap.setSize(b.width, 0);
10083                     wrap.setY(b.bottom);
10084                     st.left = st.top = "0";
10085                     a = {height: bh, points: pt};
10086                 break;
10087                 case "tl":
10088                     wrap.setSize(0, 0);
10089                     st.right = st.bottom = "0";
10090                     a = {width: bw, height: bh};
10091                 break;
10092                 case "bl":
10093                     wrap.setSize(0, 0);
10094                     wrap.setY(b.y+b.height);
10095                     st.right = st.top = "0";
10096                     a = {width: bw, height: bh, points: pt};
10097                 break;
10098                 case "br":
10099                     wrap.setSize(0, 0);
10100                     wrap.setXY([b.right, b.bottom]);
10101                     st.left = st.top = "0";
10102                     a = {width: bw, height: bh, points: pt};
10103                 break;
10104                 case "tr":
10105                     wrap.setSize(0, 0);
10106                     wrap.setX(b.x+b.width);
10107                     st.left = st.bottom = "0";
10108                     a = {width: bw, height: bh, points: pt};
10109                 break;
10110             }
10111             this.dom.style.visibility = "visible";
10112             wrap.show();
10113
10114             arguments.callee.anim = wrap.fxanim(a,
10115                 o,
10116                 'motion',
10117                 .5,
10118                 'easeOut', after);
10119         });
10120         return this;
10121     },
10122     
10123         /**
10124          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10125          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10126          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10127          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10128          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10129          * Usage:
10130          *<pre><code>
10131 // default: slide the element out to the top
10132 el.slideOut();
10133
10134 // custom: slide the element out to the right with a 2-second duration
10135 el.slideOut('r', { duration: 2 });
10136
10137 // common config options shown with default values
10138 el.slideOut('t', {
10139     easing: 'easeOut',
10140     duration: .5,
10141     remove: false,
10142     useDisplay: false
10143 });
10144 </code></pre>
10145          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10146          * @param {Object} options (optional) Object literal with any of the Fx config options
10147          * @return {Roo.Element} The Element
10148          */
10149     slideOut : function(anchor, o){
10150         var el = this.getFxEl();
10151         o = o || {};
10152
10153         el.queueFx(o, function(){
10154
10155             anchor = anchor || "t";
10156
10157             // restore values after effect
10158             var r = this.getFxRestore();
10159             
10160             var b = this.getBox();
10161             // fixed size for slide
10162             this.setSize(b);
10163
10164             // wrap if needed
10165             var wrap = this.fxWrap(r.pos, o, "visible");
10166
10167             var st = this.dom.style;
10168             st.visibility = "visible";
10169             st.position = "absolute";
10170
10171             wrap.setSize(b);
10172
10173             var after = function(){
10174                 if(o.useDisplay){
10175                     el.setDisplayed(false);
10176                 }else{
10177                     el.hide();
10178                 }
10179
10180                 el.fxUnwrap(wrap, r.pos, o);
10181
10182                 st.width = r.width;
10183                 st.height = r.height;
10184
10185                 el.afterFx(o);
10186             };
10187
10188             var a, zero = {to: 0};
10189             switch(anchor.toLowerCase()){
10190                 case "t":
10191                     st.left = st.bottom = "0";
10192                     a = {height: zero};
10193                 break;
10194                 case "l":
10195                     st.right = st.top = "0";
10196                     a = {width: zero};
10197                 break;
10198                 case "r":
10199                     st.left = st.top = "0";
10200                     a = {width: zero, points: {to:[b.right, b.y]}};
10201                 break;
10202                 case "b":
10203                     st.left = st.top = "0";
10204                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10205                 break;
10206                 case "tl":
10207                     st.right = st.bottom = "0";
10208                     a = {width: zero, height: zero};
10209                 break;
10210                 case "bl":
10211                     st.right = st.top = "0";
10212                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10213                 break;
10214                 case "br":
10215                     st.left = st.top = "0";
10216                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10217                 break;
10218                 case "tr":
10219                     st.left = st.bottom = "0";
10220                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10221                 break;
10222             }
10223
10224             arguments.callee.anim = wrap.fxanim(a,
10225                 o,
10226                 'motion',
10227                 .5,
10228                 "easeOut", after);
10229         });
10230         return this;
10231     },
10232
10233         /**
10234          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10235          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10236          * The element must be removed from the DOM using the 'remove' config option if desired.
10237          * Usage:
10238          *<pre><code>
10239 // default
10240 el.puff();
10241
10242 // common config options shown with default values
10243 el.puff({
10244     easing: 'easeOut',
10245     duration: .5,
10246     remove: false,
10247     useDisplay: false
10248 });
10249 </code></pre>
10250          * @param {Object} options (optional) Object literal with any of the Fx config options
10251          * @return {Roo.Element} The Element
10252          */
10253     puff : function(o){
10254         var el = this.getFxEl();
10255         o = o || {};
10256
10257         el.queueFx(o, function(){
10258             this.clearOpacity();
10259             this.show();
10260
10261             // restore values after effect
10262             var r = this.getFxRestore();
10263             var st = this.dom.style;
10264
10265             var after = function(){
10266                 if(o.useDisplay){
10267                     el.setDisplayed(false);
10268                 }else{
10269                     el.hide();
10270                 }
10271
10272                 el.clearOpacity();
10273
10274                 el.setPositioning(r.pos);
10275                 st.width = r.width;
10276                 st.height = r.height;
10277                 st.fontSize = '';
10278                 el.afterFx(o);
10279             };
10280
10281             var width = this.getWidth();
10282             var height = this.getHeight();
10283
10284             arguments.callee.anim = this.fxanim({
10285                     width : {to: this.adjustWidth(width * 2)},
10286                     height : {to: this.adjustHeight(height * 2)},
10287                     points : {by: [-(width * .5), -(height * .5)]},
10288                     opacity : {to: 0},
10289                     fontSize: {to:200, unit: "%"}
10290                 },
10291                 o,
10292                 'motion',
10293                 .5,
10294                 "easeOut", after);
10295         });
10296         return this;
10297     },
10298
10299         /**
10300          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10301          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10302          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10303          * Usage:
10304          *<pre><code>
10305 // default
10306 el.switchOff();
10307
10308 // all config options shown with default values
10309 el.switchOff({
10310     easing: 'easeIn',
10311     duration: .3,
10312     remove: false,
10313     useDisplay: false
10314 });
10315 </code></pre>
10316          * @param {Object} options (optional) Object literal with any of the Fx config options
10317          * @return {Roo.Element} The Element
10318          */
10319     switchOff : function(o){
10320         var el = this.getFxEl();
10321         o = o || {};
10322
10323         el.queueFx(o, function(){
10324             this.clearOpacity();
10325             this.clip();
10326
10327             // restore values after effect
10328             var r = this.getFxRestore();
10329             var st = this.dom.style;
10330
10331             var after = function(){
10332                 if(o.useDisplay){
10333                     el.setDisplayed(false);
10334                 }else{
10335                     el.hide();
10336                 }
10337
10338                 el.clearOpacity();
10339                 el.setPositioning(r.pos);
10340                 st.width = r.width;
10341                 st.height = r.height;
10342
10343                 el.afterFx(o);
10344             };
10345
10346             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10347                 this.clearOpacity();
10348                 (function(){
10349                     this.fxanim({
10350                         height:{to:1},
10351                         points:{by:[0, this.getHeight() * .5]}
10352                     }, o, 'motion', 0.3, 'easeIn', after);
10353                 }).defer(100, this);
10354             });
10355         });
10356         return this;
10357     },
10358
10359     /**
10360      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10361      * changed using the "attr" config option) and then fading back to the original color. If no original
10362      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10363      * Usage:
10364 <pre><code>
10365 // default: highlight background to yellow
10366 el.highlight();
10367
10368 // custom: highlight foreground text to blue for 2 seconds
10369 el.highlight("0000ff", { attr: 'color', duration: 2 });
10370
10371 // common config options shown with default values
10372 el.highlight("ffff9c", {
10373     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10374     endColor: (current color) or "ffffff",
10375     easing: 'easeIn',
10376     duration: 1
10377 });
10378 </code></pre>
10379      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10380      * @param {Object} options (optional) Object literal with any of the Fx config options
10381      * @return {Roo.Element} The Element
10382      */ 
10383     highlight : function(color, o){
10384         var el = this.getFxEl();
10385         o = o || {};
10386
10387         el.queueFx(o, function(){
10388             color = color || "ffff9c";
10389             attr = o.attr || "backgroundColor";
10390
10391             this.clearOpacity();
10392             this.show();
10393
10394             var origColor = this.getColor(attr);
10395             var restoreColor = this.dom.style[attr];
10396             endColor = (o.endColor || origColor) || "ffffff";
10397
10398             var after = function(){
10399                 el.dom.style[attr] = restoreColor;
10400                 el.afterFx(o);
10401             };
10402
10403             var a = {};
10404             a[attr] = {from: color, to: endColor};
10405             arguments.callee.anim = this.fxanim(a,
10406                 o,
10407                 'color',
10408                 1,
10409                 'easeIn', after);
10410         });
10411         return this;
10412     },
10413
10414    /**
10415     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10416     * Usage:
10417 <pre><code>
10418 // default: a single light blue ripple
10419 el.frame();
10420
10421 // custom: 3 red ripples lasting 3 seconds total
10422 el.frame("ff0000", 3, { duration: 3 });
10423
10424 // common config options shown with default values
10425 el.frame("C3DAF9", 1, {
10426     duration: 1 //duration of entire animation (not each individual ripple)
10427     // Note: Easing is not configurable and will be ignored if included
10428 });
10429 </code></pre>
10430     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10431     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10432     * @param {Object} options (optional) Object literal with any of the Fx config options
10433     * @return {Roo.Element} The Element
10434     */
10435     frame : function(color, count, o){
10436         var el = this.getFxEl();
10437         o = o || {};
10438
10439         el.queueFx(o, function(){
10440             color = color || "#C3DAF9";
10441             if(color.length == 6){
10442                 color = "#" + color;
10443             }
10444             count = count || 1;
10445             duration = o.duration || 1;
10446             this.show();
10447
10448             var b = this.getBox();
10449             var animFn = function(){
10450                 var proxy = this.createProxy({
10451
10452                      style:{
10453                         visbility:"hidden",
10454                         position:"absolute",
10455                         "z-index":"35000", // yee haw
10456                         border:"0px solid " + color
10457                      }
10458                   });
10459                 var scale = Roo.isBorderBox ? 2 : 1;
10460                 proxy.animate({
10461                     top:{from:b.y, to:b.y - 20},
10462                     left:{from:b.x, to:b.x - 20},
10463                     borderWidth:{from:0, to:10},
10464                     opacity:{from:1, to:0},
10465                     height:{from:b.height, to:(b.height + (20*scale))},
10466                     width:{from:b.width, to:(b.width + (20*scale))}
10467                 }, duration, function(){
10468                     proxy.remove();
10469                 });
10470                 if(--count > 0){
10471                      animFn.defer((duration/2)*1000, this);
10472                 }else{
10473                     el.afterFx(o);
10474                 }
10475             };
10476             animFn.call(this);
10477         });
10478         return this;
10479     },
10480
10481    /**
10482     * Creates a pause before any subsequent queued effects begin.  If there are
10483     * no effects queued after the pause it will have no effect.
10484     * Usage:
10485 <pre><code>
10486 el.pause(1);
10487 </code></pre>
10488     * @param {Number} seconds The length of time to pause (in seconds)
10489     * @return {Roo.Element} The Element
10490     */
10491     pause : function(seconds){
10492         var el = this.getFxEl();
10493         var o = {};
10494
10495         el.queueFx(o, function(){
10496             setTimeout(function(){
10497                 el.afterFx(o);
10498             }, seconds * 1000);
10499         });
10500         return this;
10501     },
10502
10503    /**
10504     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10505     * using the "endOpacity" config option.
10506     * Usage:
10507 <pre><code>
10508 // default: fade in from opacity 0 to 100%
10509 el.fadeIn();
10510
10511 // custom: fade in from opacity 0 to 75% over 2 seconds
10512 el.fadeIn({ endOpacity: .75, duration: 2});
10513
10514 // common config options shown with default values
10515 el.fadeIn({
10516     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10517     easing: 'easeOut',
10518     duration: .5
10519 });
10520 </code></pre>
10521     * @param {Object} options (optional) Object literal with any of the Fx config options
10522     * @return {Roo.Element} The Element
10523     */
10524     fadeIn : function(o){
10525         var el = this.getFxEl();
10526         o = o || {};
10527         el.queueFx(o, function(){
10528             this.setOpacity(0);
10529             this.fixDisplay();
10530             this.dom.style.visibility = 'visible';
10531             var to = o.endOpacity || 1;
10532             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10533                 o, null, .5, "easeOut", function(){
10534                 if(to == 1){
10535                     this.clearOpacity();
10536                 }
10537                 el.afterFx(o);
10538             });
10539         });
10540         return this;
10541     },
10542
10543    /**
10544     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10545     * using the "endOpacity" config option.
10546     * Usage:
10547 <pre><code>
10548 // default: fade out from the element's current opacity to 0
10549 el.fadeOut();
10550
10551 // custom: fade out from the element's current opacity to 25% over 2 seconds
10552 el.fadeOut({ endOpacity: .25, duration: 2});
10553
10554 // common config options shown with default values
10555 el.fadeOut({
10556     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10557     easing: 'easeOut',
10558     duration: .5
10559     remove: false,
10560     useDisplay: false
10561 });
10562 </code></pre>
10563     * @param {Object} options (optional) Object literal with any of the Fx config options
10564     * @return {Roo.Element} The Element
10565     */
10566     fadeOut : function(o){
10567         var el = this.getFxEl();
10568         o = o || {};
10569         el.queueFx(o, function(){
10570             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10571                 o, null, .5, "easeOut", function(){
10572                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10573                      this.dom.style.display = "none";
10574                 }else{
10575                      this.dom.style.visibility = "hidden";
10576                 }
10577                 this.clearOpacity();
10578                 el.afterFx(o);
10579             });
10580         });
10581         return this;
10582     },
10583
10584    /**
10585     * Animates the transition of an element's dimensions from a starting height/width
10586     * to an ending height/width.
10587     * Usage:
10588 <pre><code>
10589 // change height and width to 100x100 pixels
10590 el.scale(100, 100);
10591
10592 // common config options shown with default values.  The height and width will default to
10593 // the element's existing values if passed as null.
10594 el.scale(
10595     [element's width],
10596     [element's height], {
10597     easing: 'easeOut',
10598     duration: .35
10599 });
10600 </code></pre>
10601     * @param {Number} width  The new width (pass undefined to keep the original width)
10602     * @param {Number} height  The new height (pass undefined to keep the original height)
10603     * @param {Object} options (optional) Object literal with any of the Fx config options
10604     * @return {Roo.Element} The Element
10605     */
10606     scale : function(w, h, o){
10607         this.shift(Roo.apply({}, o, {
10608             width: w,
10609             height: h
10610         }));
10611         return this;
10612     },
10613
10614    /**
10615     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10616     * Any of these properties not specified in the config object will not be changed.  This effect 
10617     * requires that at least one new dimension, position or opacity setting must be passed in on
10618     * the config object in order for the function to have any effect.
10619     * Usage:
10620 <pre><code>
10621 // slide the element horizontally to x position 200 while changing the height and opacity
10622 el.shift({ x: 200, height: 50, opacity: .8 });
10623
10624 // common config options shown with default values.
10625 el.shift({
10626     width: [element's width],
10627     height: [element's height],
10628     x: [element's x position],
10629     y: [element's y position],
10630     opacity: [element's opacity],
10631     easing: 'easeOut',
10632     duration: .35
10633 });
10634 </code></pre>
10635     * @param {Object} options  Object literal with any of the Fx config options
10636     * @return {Roo.Element} The Element
10637     */
10638     shift : function(o){
10639         var el = this.getFxEl();
10640         o = o || {};
10641         el.queueFx(o, function(){
10642             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10643             if(w !== undefined){
10644                 a.width = {to: this.adjustWidth(w)};
10645             }
10646             if(h !== undefined){
10647                 a.height = {to: this.adjustHeight(h)};
10648             }
10649             if(x !== undefined || y !== undefined){
10650                 a.points = {to: [
10651                     x !== undefined ? x : this.getX(),
10652                     y !== undefined ? y : this.getY()
10653                 ]};
10654             }
10655             if(op !== undefined){
10656                 a.opacity = {to: op};
10657             }
10658             if(o.xy !== undefined){
10659                 a.points = {to: o.xy};
10660             }
10661             arguments.callee.anim = this.fxanim(a,
10662                 o, 'motion', .35, "easeOut", function(){
10663                 el.afterFx(o);
10664             });
10665         });
10666         return this;
10667     },
10668
10669         /**
10670          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10671          * ending point of the effect.
10672          * Usage:
10673          *<pre><code>
10674 // default: slide the element downward while fading out
10675 el.ghost();
10676
10677 // custom: slide the element out to the right with a 2-second duration
10678 el.ghost('r', { duration: 2 });
10679
10680 // common config options shown with default values
10681 el.ghost('b', {
10682     easing: 'easeOut',
10683     duration: .5
10684     remove: false,
10685     useDisplay: false
10686 });
10687 </code></pre>
10688          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10689          * @param {Object} options (optional) Object literal with any of the Fx config options
10690          * @return {Roo.Element} The Element
10691          */
10692     ghost : function(anchor, o){
10693         var el = this.getFxEl();
10694         o = o || {};
10695
10696         el.queueFx(o, function(){
10697             anchor = anchor || "b";
10698
10699             // restore values after effect
10700             var r = this.getFxRestore();
10701             var w = this.getWidth(),
10702                 h = this.getHeight();
10703
10704             var st = this.dom.style;
10705
10706             var after = function(){
10707                 if(o.useDisplay){
10708                     el.setDisplayed(false);
10709                 }else{
10710                     el.hide();
10711                 }
10712
10713                 el.clearOpacity();
10714                 el.setPositioning(r.pos);
10715                 st.width = r.width;
10716                 st.height = r.height;
10717
10718                 el.afterFx(o);
10719             };
10720
10721             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10722             switch(anchor.toLowerCase()){
10723                 case "t":
10724                     pt.by = [0, -h];
10725                 break;
10726                 case "l":
10727                     pt.by = [-w, 0];
10728                 break;
10729                 case "r":
10730                     pt.by = [w, 0];
10731                 break;
10732                 case "b":
10733                     pt.by = [0, h];
10734                 break;
10735                 case "tl":
10736                     pt.by = [-w, -h];
10737                 break;
10738                 case "bl":
10739                     pt.by = [-w, h];
10740                 break;
10741                 case "br":
10742                     pt.by = [w, h];
10743                 break;
10744                 case "tr":
10745                     pt.by = [w, -h];
10746                 break;
10747             }
10748
10749             arguments.callee.anim = this.fxanim(a,
10750                 o,
10751                 'motion',
10752                 .5,
10753                 "easeOut", after);
10754         });
10755         return this;
10756     },
10757
10758         /**
10759          * Ensures that all effects queued after syncFx is called on the element are
10760          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10761          * @return {Roo.Element} The Element
10762          */
10763     syncFx : function(){
10764         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10765             block : false,
10766             concurrent : true,
10767             stopFx : false
10768         });
10769         return this;
10770     },
10771
10772         /**
10773          * Ensures that all effects queued after sequenceFx is called on the element are
10774          * run in sequence.  This is the opposite of {@link #syncFx}.
10775          * @return {Roo.Element} The Element
10776          */
10777     sequenceFx : function(){
10778         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10779             block : false,
10780             concurrent : false,
10781             stopFx : false
10782         });
10783         return this;
10784     },
10785
10786         /* @private */
10787     nextFx : function(){
10788         var ef = this.fxQueue[0];
10789         if(ef){
10790             ef.call(this);
10791         }
10792     },
10793
10794         /**
10795          * Returns true if the element has any effects actively running or queued, else returns false.
10796          * @return {Boolean} True if element has active effects, else false
10797          */
10798     hasActiveFx : function(){
10799         return this.fxQueue && this.fxQueue[0];
10800     },
10801
10802         /**
10803          * Stops any running effects and clears the element's internal effects queue if it contains
10804          * any additional effects that haven't started yet.
10805          * @return {Roo.Element} The Element
10806          */
10807     stopFx : function(){
10808         if(this.hasActiveFx()){
10809             var cur = this.fxQueue[0];
10810             if(cur && cur.anim && cur.anim.isAnimated()){
10811                 this.fxQueue = [cur]; // clear out others
10812                 cur.anim.stop(true);
10813             }
10814         }
10815         return this;
10816     },
10817
10818         /* @private */
10819     beforeFx : function(o){
10820         if(this.hasActiveFx() && !o.concurrent){
10821            if(o.stopFx){
10822                this.stopFx();
10823                return true;
10824            }
10825            return false;
10826         }
10827         return true;
10828     },
10829
10830         /**
10831          * Returns true if the element is currently blocking so that no other effect can be queued
10832          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10833          * used to ensure that an effect initiated by a user action runs to completion prior to the
10834          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10835          * @return {Boolean} True if blocking, else false
10836          */
10837     hasFxBlock : function(){
10838         var q = this.fxQueue;
10839         return q && q[0] && q[0].block;
10840     },
10841
10842         /* @private */
10843     queueFx : function(o, fn){
10844         if(!this.fxQueue){
10845             this.fxQueue = [];
10846         }
10847         if(!this.hasFxBlock()){
10848             Roo.applyIf(o, this.fxDefaults);
10849             if(!o.concurrent){
10850                 var run = this.beforeFx(o);
10851                 fn.block = o.block;
10852                 this.fxQueue.push(fn);
10853                 if(run){
10854                     this.nextFx();
10855                 }
10856             }else{
10857                 fn.call(this);
10858             }
10859         }
10860         return this;
10861     },
10862
10863         /* @private */
10864     fxWrap : function(pos, o, vis){
10865         var wrap;
10866         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10867             var wrapXY;
10868             if(o.fixPosition){
10869                 wrapXY = this.getXY();
10870             }
10871             var div = document.createElement("div");
10872             div.style.visibility = vis;
10873             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10874             wrap.setPositioning(pos);
10875             if(wrap.getStyle("position") == "static"){
10876                 wrap.position("relative");
10877             }
10878             this.clearPositioning('auto');
10879             wrap.clip();
10880             wrap.dom.appendChild(this.dom);
10881             if(wrapXY){
10882                 wrap.setXY(wrapXY);
10883             }
10884         }
10885         return wrap;
10886     },
10887
10888         /* @private */
10889     fxUnwrap : function(wrap, pos, o){
10890         this.clearPositioning();
10891         this.setPositioning(pos);
10892         if(!o.wrap){
10893             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10894             wrap.remove();
10895         }
10896     },
10897
10898         /* @private */
10899     getFxRestore : function(){
10900         var st = this.dom.style;
10901         return {pos: this.getPositioning(), width: st.width, height : st.height};
10902     },
10903
10904         /* @private */
10905     afterFx : function(o){
10906         if(o.afterStyle){
10907             this.applyStyles(o.afterStyle);
10908         }
10909         if(o.afterCls){
10910             this.addClass(o.afterCls);
10911         }
10912         if(o.remove === true){
10913             this.remove();
10914         }
10915         Roo.callback(o.callback, o.scope, [this]);
10916         if(!o.concurrent){
10917             this.fxQueue.shift();
10918             this.nextFx();
10919         }
10920     },
10921
10922         /* @private */
10923     getFxEl : function(){ // support for composite element fx
10924         return Roo.get(this.dom);
10925     },
10926
10927         /* @private */
10928     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10929         animType = animType || 'run';
10930         opt = opt || {};
10931         var anim = Roo.lib.Anim[animType](
10932             this.dom, args,
10933             (opt.duration || defaultDur) || .35,
10934             (opt.easing || defaultEase) || 'easeOut',
10935             function(){
10936                 Roo.callback(cb, this);
10937             },
10938             this
10939         );
10940         opt.anim = anim;
10941         return anim;
10942     }
10943 };
10944
10945 // backwords compat
10946 Roo.Fx.resize = Roo.Fx.scale;
10947
10948 //When included, Roo.Fx is automatically applied to Element so that all basic
10949 //effects are available directly via the Element API
10950 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10951  * Based on:
10952  * Ext JS Library 1.1.1
10953  * Copyright(c) 2006-2007, Ext JS, LLC.
10954  *
10955  * Originally Released Under LGPL - original licence link has changed is not relivant.
10956  *
10957  * Fork - LGPL
10958  * <script type="text/javascript">
10959  */
10960
10961
10962 /**
10963  * @class Roo.CompositeElement
10964  * Standard composite class. Creates a Roo.Element for every element in the collection.
10965  * <br><br>
10966  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10967  * actions will be performed on all the elements in this collection.</b>
10968  * <br><br>
10969  * All methods return <i>this</i> and can be chained.
10970  <pre><code>
10971  var els = Roo.select("#some-el div.some-class", true);
10972  // or select directly from an existing element
10973  var el = Roo.get('some-el');
10974  el.select('div.some-class', true);
10975
10976  els.setWidth(100); // all elements become 100 width
10977  els.hide(true); // all elements fade out and hide
10978  // or
10979  els.setWidth(100).hide(true);
10980  </code></pre>
10981  */
10982 Roo.CompositeElement = function(els){
10983     this.elements = [];
10984     this.addElements(els);
10985 };
10986 Roo.CompositeElement.prototype = {
10987     isComposite: true,
10988     addElements : function(els){
10989         if(!els) return this;
10990         if(typeof els == "string"){
10991             els = Roo.Element.selectorFunction(els);
10992         }
10993         var yels = this.elements;
10994         var index = yels.length-1;
10995         for(var i = 0, len = els.length; i < len; i++) {
10996                 yels[++index] = Roo.get(els[i]);
10997         }
10998         return this;
10999     },
11000
11001     /**
11002     * Clears this composite and adds the elements returned by the passed selector.
11003     * @param {String/Array} els A string CSS selector, an array of elements or an element
11004     * @return {CompositeElement} this
11005     */
11006     fill : function(els){
11007         this.elements = [];
11008         this.add(els);
11009         return this;
11010     },
11011
11012     /**
11013     * Filters this composite to only elements that match the passed selector.
11014     * @param {String} selector A string CSS selector
11015     * @param {Boolean} inverse return inverse filter (not matches)
11016     * @return {CompositeElement} this
11017     */
11018     filter : function(selector, inverse){
11019         var els = [];
11020         inverse = inverse || false;
11021         this.each(function(el){
11022             var match = inverse ? !el.is(selector) : el.is(selector);
11023             if(match){
11024                 els[els.length] = el.dom;
11025             }
11026         });
11027         this.fill(els);
11028         return this;
11029     },
11030
11031     invoke : function(fn, args){
11032         var els = this.elements;
11033         for(var i = 0, len = els.length; i < len; i++) {
11034                 Roo.Element.prototype[fn].apply(els[i], args);
11035         }
11036         return this;
11037     },
11038     /**
11039     * Adds elements to this composite.
11040     * @param {String/Array} els A string CSS selector, an array of elements or an element
11041     * @return {CompositeElement} this
11042     */
11043     add : function(els){
11044         if(typeof els == "string"){
11045             this.addElements(Roo.Element.selectorFunction(els));
11046         }else if(els.length !== undefined){
11047             this.addElements(els);
11048         }else{
11049             this.addElements([els]);
11050         }
11051         return this;
11052     },
11053     /**
11054     * Calls the passed function passing (el, this, index) for each element in this composite.
11055     * @param {Function} fn The function to call
11056     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11057     * @return {CompositeElement} this
11058     */
11059     each : function(fn, scope){
11060         var els = this.elements;
11061         for(var i = 0, len = els.length; i < len; i++){
11062             if(fn.call(scope || els[i], els[i], this, i) === false) {
11063                 break;
11064             }
11065         }
11066         return this;
11067     },
11068
11069     /**
11070      * Returns the Element object at the specified index
11071      * @param {Number} index
11072      * @return {Roo.Element}
11073      */
11074     item : function(index){
11075         return this.elements[index] || null;
11076     },
11077
11078     /**
11079      * Returns the first Element
11080      * @return {Roo.Element}
11081      */
11082     first : function(){
11083         return this.item(0);
11084     },
11085
11086     /**
11087      * Returns the last Element
11088      * @return {Roo.Element}
11089      */
11090     last : function(){
11091         return this.item(this.elements.length-1);
11092     },
11093
11094     /**
11095      * Returns the number of elements in this composite
11096      * @return Number
11097      */
11098     getCount : function(){
11099         return this.elements.length;
11100     },
11101
11102     /**
11103      * Returns true if this composite contains the passed element
11104      * @return Boolean
11105      */
11106     contains : function(el){
11107         return this.indexOf(el) !== -1;
11108     },
11109
11110     /**
11111      * Returns true if this composite contains the passed element
11112      * @return Boolean
11113      */
11114     indexOf : function(el){
11115         return this.elements.indexOf(Roo.get(el));
11116     },
11117
11118
11119     /**
11120     * Removes the specified element(s).
11121     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11122     * or an array of any of those.
11123     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11124     * @return {CompositeElement} this
11125     */
11126     removeElement : function(el, removeDom){
11127         if(el instanceof Array){
11128             for(var i = 0, len = el.length; i < len; i++){
11129                 this.removeElement(el[i]);
11130             }
11131             return this;
11132         }
11133         var index = typeof el == 'number' ? el : this.indexOf(el);
11134         if(index !== -1){
11135             if(removeDom){
11136                 var d = this.elements[index];
11137                 if(d.dom){
11138                     d.remove();
11139                 }else{
11140                     d.parentNode.removeChild(d);
11141                 }
11142             }
11143             this.elements.splice(index, 1);
11144         }
11145         return this;
11146     },
11147
11148     /**
11149     * Replaces the specified element with the passed element.
11150     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11151     * to replace.
11152     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11153     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11154     * @return {CompositeElement} this
11155     */
11156     replaceElement : function(el, replacement, domReplace){
11157         var index = typeof el == 'number' ? el : this.indexOf(el);
11158         if(index !== -1){
11159             if(domReplace){
11160                 this.elements[index].replaceWith(replacement);
11161             }else{
11162                 this.elements.splice(index, 1, Roo.get(replacement))
11163             }
11164         }
11165         return this;
11166     },
11167
11168     /**
11169      * Removes all elements.
11170      */
11171     clear : function(){
11172         this.elements = [];
11173     }
11174 };
11175 (function(){
11176     Roo.CompositeElement.createCall = function(proto, fnName){
11177         if(!proto[fnName]){
11178             proto[fnName] = function(){
11179                 return this.invoke(fnName, arguments);
11180             };
11181         }
11182     };
11183     for(var fnName in Roo.Element.prototype){
11184         if(typeof Roo.Element.prototype[fnName] == "function"){
11185             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11186         }
11187     };
11188 })();
11189 /*
11190  * Based on:
11191  * Ext JS Library 1.1.1
11192  * Copyright(c) 2006-2007, Ext JS, LLC.
11193  *
11194  * Originally Released Under LGPL - original licence link has changed is not relivant.
11195  *
11196  * Fork - LGPL
11197  * <script type="text/javascript">
11198  */
11199
11200 /**
11201  * @class Roo.CompositeElementLite
11202  * @extends Roo.CompositeElement
11203  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11204  <pre><code>
11205  var els = Roo.select("#some-el div.some-class");
11206  // or select directly from an existing element
11207  var el = Roo.get('some-el');
11208  el.select('div.some-class');
11209
11210  els.setWidth(100); // all elements become 100 width
11211  els.hide(true); // all elements fade out and hide
11212  // or
11213  els.setWidth(100).hide(true);
11214  </code></pre><br><br>
11215  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11216  * actions will be performed on all the elements in this collection.</b>
11217  */
11218 Roo.CompositeElementLite = function(els){
11219     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11220     this.el = new Roo.Element.Flyweight();
11221 };
11222 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11223     addElements : function(els){
11224         if(els){
11225             if(els instanceof Array){
11226                 this.elements = this.elements.concat(els);
11227             }else{
11228                 var yels = this.elements;
11229                 var index = yels.length-1;
11230                 for(var i = 0, len = els.length; i < len; i++) {
11231                     yels[++index] = els[i];
11232                 }
11233             }
11234         }
11235         return this;
11236     },
11237     invoke : function(fn, args){
11238         var els = this.elements;
11239         var el = this.el;
11240         for(var i = 0, len = els.length; i < len; i++) {
11241             el.dom = els[i];
11242                 Roo.Element.prototype[fn].apply(el, args);
11243         }
11244         return this;
11245     },
11246     /**
11247      * Returns a flyweight Element of the dom element object at the specified index
11248      * @param {Number} index
11249      * @return {Roo.Element}
11250      */
11251     item : function(index){
11252         if(!this.elements[index]){
11253             return null;
11254         }
11255         this.el.dom = this.elements[index];
11256         return this.el;
11257     },
11258
11259     // fixes scope with flyweight
11260     addListener : function(eventName, handler, scope, opt){
11261         var els = this.elements;
11262         for(var i = 0, len = els.length; i < len; i++) {
11263             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11264         }
11265         return this;
11266     },
11267
11268     /**
11269     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11270     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11271     * a reference to the dom node, use el.dom.</b>
11272     * @param {Function} fn The function to call
11273     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11274     * @return {CompositeElement} this
11275     */
11276     each : function(fn, scope){
11277         var els = this.elements;
11278         var el = this.el;
11279         for(var i = 0, len = els.length; i < len; i++){
11280             el.dom = els[i];
11281                 if(fn.call(scope || el, el, this, i) === false){
11282                 break;
11283             }
11284         }
11285         return this;
11286     },
11287
11288     indexOf : function(el){
11289         return this.elements.indexOf(Roo.getDom(el));
11290     },
11291
11292     replaceElement : function(el, replacement, domReplace){
11293         var index = typeof el == 'number' ? el : this.indexOf(el);
11294         if(index !== -1){
11295             replacement = Roo.getDom(replacement);
11296             if(domReplace){
11297                 var d = this.elements[index];
11298                 d.parentNode.insertBefore(replacement, d);
11299                 d.parentNode.removeChild(d);
11300             }
11301             this.elements.splice(index, 1, replacement);
11302         }
11303         return this;
11304     }
11305 });
11306 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11307
11308 /*
11309  * Based on:
11310  * Ext JS Library 1.1.1
11311  * Copyright(c) 2006-2007, Ext JS, LLC.
11312  *
11313  * Originally Released Under LGPL - original licence link has changed is not relivant.
11314  *
11315  * Fork - LGPL
11316  * <script type="text/javascript">
11317  */
11318
11319  
11320
11321 /**
11322  * @class Roo.data.Connection
11323  * @extends Roo.util.Observable
11324  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11325  * either to a configured URL, or to a URL specified at request time.<br><br>
11326  * <p>
11327  * Requests made by this class are asynchronous, and will return immediately. No data from
11328  * the server will be available to the statement immediately following the {@link #request} call.
11329  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11330  * <p>
11331  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11332  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11333  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11334  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11335  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11336  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11337  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11338  * standard DOM methods.
11339  * @constructor
11340  * @param {Object} config a configuration object.
11341  */
11342 Roo.data.Connection = function(config){
11343     Roo.apply(this, config);
11344     this.addEvents({
11345         /**
11346          * @event beforerequest
11347          * Fires before a network request is made to retrieve a data object.
11348          * @param {Connection} conn This Connection object.
11349          * @param {Object} options The options config object passed to the {@link #request} method.
11350          */
11351         "beforerequest" : true,
11352         /**
11353          * @event requestcomplete
11354          * Fires if the request was successfully completed.
11355          * @param {Connection} conn This Connection object.
11356          * @param {Object} response The XHR object containing the response data.
11357          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11358          * @param {Object} options The options config object passed to the {@link #request} method.
11359          */
11360         "requestcomplete" : true,
11361         /**
11362          * @event requestexception
11363          * Fires if an error HTTP status was returned from the server.
11364          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11365          * @param {Connection} conn This Connection object.
11366          * @param {Object} response The XHR object containing the response data.
11367          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11368          * @param {Object} options The options config object passed to the {@link #request} method.
11369          */
11370         "requestexception" : true
11371     });
11372     Roo.data.Connection.superclass.constructor.call(this);
11373 };
11374
11375 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11376     /**
11377      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11378      */
11379     /**
11380      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11381      * extra parameters to each request made by this object. (defaults to undefined)
11382      */
11383     /**
11384      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11385      *  to each request made by this object. (defaults to undefined)
11386      */
11387     /**
11388      * @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)
11389      */
11390     /**
11391      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11392      */
11393     timeout : 30000,
11394     /**
11395      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11396      * @type Boolean
11397      */
11398     autoAbort:false,
11399
11400     /**
11401      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11402      * @type Boolean
11403      */
11404     disableCaching: true,
11405
11406     /**
11407      * Sends an HTTP request to a remote server.
11408      * @param {Object} options An object which may contain the following properties:<ul>
11409      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11410      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11411      * request, a url encoded string or a function to call to get either.</li>
11412      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11413      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11414      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11415      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11416      * <li>options {Object} The parameter to the request call.</li>
11417      * <li>success {Boolean} True if the request succeeded.</li>
11418      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11419      * </ul></li>
11420      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11421      * The callback is passed the following parameters:<ul>
11422      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11423      * <li>options {Object} The parameter to the request call.</li>
11424      * </ul></li>
11425      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11426      * The callback is passed the following parameters:<ul>
11427      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11428      * <li>options {Object} The parameter to the request call.</li>
11429      * </ul></li>
11430      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11431      * for the callback function. Defaults to the browser window.</li>
11432      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11433      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11434      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11435      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11436      * params for the post data. Any params will be appended to the URL.</li>
11437      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11438      * </ul>
11439      * @return {Number} transactionId
11440      */
11441     request : function(o){
11442         if(this.fireEvent("beforerequest", this, o) !== false){
11443             var p = o.params;
11444
11445             if(typeof p == "function"){
11446                 p = p.call(o.scope||window, o);
11447             }
11448             if(typeof p == "object"){
11449                 p = Roo.urlEncode(o.params);
11450             }
11451             if(this.extraParams){
11452                 var extras = Roo.urlEncode(this.extraParams);
11453                 p = p ? (p + '&' + extras) : extras;
11454             }
11455
11456             var url = o.url || this.url;
11457             if(typeof url == 'function'){
11458                 url = url.call(o.scope||window, o);
11459             }
11460
11461             if(o.form){
11462                 var form = Roo.getDom(o.form);
11463                 url = url || form.action;
11464
11465                 var enctype = form.getAttribute("enctype");
11466                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11467                     return this.doFormUpload(o, p, url);
11468                 }
11469                 var f = Roo.lib.Ajax.serializeForm(form);
11470                 p = p ? (p + '&' + f) : f;
11471             }
11472
11473             var hs = o.headers;
11474             if(this.defaultHeaders){
11475                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11476                 if(!o.headers){
11477                     o.headers = hs;
11478                 }
11479             }
11480
11481             var cb = {
11482                 success: this.handleResponse,
11483                 failure: this.handleFailure,
11484                 scope: this,
11485                 argument: {options: o},
11486                 timeout : o.timeout || this.timeout
11487             };
11488
11489             var method = o.method||this.method||(p ? "POST" : "GET");
11490
11491             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11492                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11493             }
11494
11495             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11496                 if(o.autoAbort){
11497                     this.abort();
11498                 }
11499             }else if(this.autoAbort !== false){
11500                 this.abort();
11501             }
11502
11503             if((method == 'GET' && p) || o.xmlData){
11504                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11505                 p = '';
11506             }
11507             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11508             return this.transId;
11509         }else{
11510             Roo.callback(o.callback, o.scope, [o, null, null]);
11511             return null;
11512         }
11513     },
11514
11515     /**
11516      * Determine whether this object has a request outstanding.
11517      * @param {Number} transactionId (Optional) defaults to the last transaction
11518      * @return {Boolean} True if there is an outstanding request.
11519      */
11520     isLoading : function(transId){
11521         if(transId){
11522             return Roo.lib.Ajax.isCallInProgress(transId);
11523         }else{
11524             return this.transId ? true : false;
11525         }
11526     },
11527
11528     /**
11529      * Aborts any outstanding request.
11530      * @param {Number} transactionId (Optional) defaults to the last transaction
11531      */
11532     abort : function(transId){
11533         if(transId || this.isLoading()){
11534             Roo.lib.Ajax.abort(transId || this.transId);
11535         }
11536     },
11537
11538     // private
11539     handleResponse : function(response){
11540         this.transId = false;
11541         var options = response.argument.options;
11542         response.argument = options ? options.argument : null;
11543         this.fireEvent("requestcomplete", this, response, options);
11544         Roo.callback(options.success, options.scope, [response, options]);
11545         Roo.callback(options.callback, options.scope, [options, true, response]);
11546     },
11547
11548     // private
11549     handleFailure : function(response, e){
11550         this.transId = false;
11551         var options = response.argument.options;
11552         response.argument = options ? options.argument : null;
11553         this.fireEvent("requestexception", this, response, options, e);
11554         Roo.callback(options.failure, options.scope, [response, options]);
11555         Roo.callback(options.callback, options.scope, [options, false, response]);
11556     },
11557
11558     // private
11559     doFormUpload : function(o, ps, url){
11560         var id = Roo.id();
11561         var frame = document.createElement('iframe');
11562         frame.id = id;
11563         frame.name = id;
11564         frame.className = 'x-hidden';
11565         if(Roo.isIE){
11566             frame.src = Roo.SSL_SECURE_URL;
11567         }
11568         document.body.appendChild(frame);
11569
11570         if(Roo.isIE){
11571            document.frames[id].name = id;
11572         }
11573
11574         var form = Roo.getDom(o.form);
11575         form.target = id;
11576         form.method = 'POST';
11577         form.enctype = form.encoding = 'multipart/form-data';
11578         if(url){
11579             form.action = url;
11580         }
11581
11582         var hiddens, hd;
11583         if(ps){ // add dynamic params
11584             hiddens = [];
11585             ps = Roo.urlDecode(ps, false);
11586             for(var k in ps){
11587                 if(ps.hasOwnProperty(k)){
11588                     hd = document.createElement('input');
11589                     hd.type = 'hidden';
11590                     hd.name = k;
11591                     hd.value = ps[k];
11592                     form.appendChild(hd);
11593                     hiddens.push(hd);
11594                 }
11595             }
11596         }
11597
11598         function cb(){
11599             var r = {  // bogus response object
11600                 responseText : '',
11601                 responseXML : null
11602             };
11603
11604             r.argument = o ? o.argument : null;
11605
11606             try { //
11607                 var doc;
11608                 if(Roo.isIE){
11609                     doc = frame.contentWindow.document;
11610                 }else {
11611                     doc = (frame.contentDocument || window.frames[id].document);
11612                 }
11613                 if(doc && doc.body){
11614                     r.responseText = doc.body.innerHTML;
11615                 }
11616                 if(doc && doc.XMLDocument){
11617                     r.responseXML = doc.XMLDocument;
11618                 }else {
11619                     r.responseXML = doc;
11620                 }
11621             }
11622             catch(e) {
11623                 // ignore
11624             }
11625
11626             Roo.EventManager.removeListener(frame, 'load', cb, this);
11627
11628             this.fireEvent("requestcomplete", this, r, o);
11629             Roo.callback(o.success, o.scope, [r, o]);
11630             Roo.callback(o.callback, o.scope, [o, true, r]);
11631
11632             setTimeout(function(){document.body.removeChild(frame);}, 100);
11633         }
11634
11635         Roo.EventManager.on(frame, 'load', cb, this);
11636         form.submit();
11637
11638         if(hiddens){ // remove dynamic params
11639             for(var i = 0, len = hiddens.length; i < len; i++){
11640                 form.removeChild(hiddens[i]);
11641             }
11642         }
11643     }
11644 });
11645 /*
11646  * Based on:
11647  * Ext JS Library 1.1.1
11648  * Copyright(c) 2006-2007, Ext JS, LLC.
11649  *
11650  * Originally Released Under LGPL - original licence link has changed is not relivant.
11651  *
11652  * Fork - LGPL
11653  * <script type="text/javascript">
11654  */
11655  
11656 /**
11657  * Global Ajax request class.
11658  * 
11659  * @class Roo.Ajax
11660  * @extends Roo.data.Connection
11661  * @static
11662  * 
11663  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11664  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11665  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11666  * @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)
11667  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11668  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11669  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11670  */
11671 Roo.Ajax = new Roo.data.Connection({
11672     // fix up the docs
11673     /**
11674      * @scope Roo.Ajax
11675      * @type {Boolear} 
11676      */
11677     autoAbort : false,
11678
11679     /**
11680      * Serialize the passed form into a url encoded string
11681      * @scope Roo.Ajax
11682      * @param {String/HTMLElement} form
11683      * @return {String}
11684      */
11685     serializeForm : function(form){
11686         return Roo.lib.Ajax.serializeForm(form);
11687     }
11688 });/*
11689  * Based on:
11690  * Ext JS Library 1.1.1
11691  * Copyright(c) 2006-2007, Ext JS, LLC.
11692  *
11693  * Originally Released Under LGPL - original licence link has changed is not relivant.
11694  *
11695  * Fork - LGPL
11696  * <script type="text/javascript">
11697  */
11698
11699  
11700 /**
11701  * @class Roo.UpdateManager
11702  * @extends Roo.util.Observable
11703  * Provides AJAX-style update for Element object.<br><br>
11704  * Usage:<br>
11705  * <pre><code>
11706  * // Get it from a Roo.Element object
11707  * var el = Roo.get("foo");
11708  * var mgr = el.getUpdateManager();
11709  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11710  * ...
11711  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11712  * <br>
11713  * // or directly (returns the same UpdateManager instance)
11714  * var mgr = new Roo.UpdateManager("myElementId");
11715  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11716  * mgr.on("update", myFcnNeedsToKnow);
11717  * <br>
11718    // short handed call directly from the element object
11719    Roo.get("foo").load({
11720         url: "bar.php",
11721         scripts:true,
11722         params: "for=bar",
11723         text: "Loading Foo..."
11724    });
11725  * </code></pre>
11726  * @constructor
11727  * Create new UpdateManager directly.
11728  * @param {String/HTMLElement/Roo.Element} el The element to update
11729  * @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).
11730  */
11731 Roo.UpdateManager = function(el, forceNew){
11732     el = Roo.get(el);
11733     if(!forceNew && el.updateManager){
11734         return el.updateManager;
11735     }
11736     /**
11737      * The Element object
11738      * @type Roo.Element
11739      */
11740     this.el = el;
11741     /**
11742      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11743      * @type String
11744      */
11745     this.defaultUrl = null;
11746
11747     this.addEvents({
11748         /**
11749          * @event beforeupdate
11750          * Fired before an update is made, return false from your handler and the update is cancelled.
11751          * @param {Roo.Element} el
11752          * @param {String/Object/Function} url
11753          * @param {String/Object} params
11754          */
11755         "beforeupdate": true,
11756         /**
11757          * @event update
11758          * Fired after successful update is made.
11759          * @param {Roo.Element} el
11760          * @param {Object} oResponseObject The response Object
11761          */
11762         "update": true,
11763         /**
11764          * @event failure
11765          * Fired on update failure.
11766          * @param {Roo.Element} el
11767          * @param {Object} oResponseObject The response Object
11768          */
11769         "failure": true
11770     });
11771     var d = Roo.UpdateManager.defaults;
11772     /**
11773      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11774      * @type String
11775      */
11776     this.sslBlankUrl = d.sslBlankUrl;
11777     /**
11778      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11779      * @type Boolean
11780      */
11781     this.disableCaching = d.disableCaching;
11782     /**
11783      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11784      * @type String
11785      */
11786     this.indicatorText = d.indicatorText;
11787     /**
11788      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11789      * @type String
11790      */
11791     this.showLoadIndicator = d.showLoadIndicator;
11792     /**
11793      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11794      * @type Number
11795      */
11796     this.timeout = d.timeout;
11797
11798     /**
11799      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11800      * @type Boolean
11801      */
11802     this.loadScripts = d.loadScripts;
11803
11804     /**
11805      * Transaction object of current executing transaction
11806      */
11807     this.transaction = null;
11808
11809     /**
11810      * @private
11811      */
11812     this.autoRefreshProcId = null;
11813     /**
11814      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11815      * @type Function
11816      */
11817     this.refreshDelegate = this.refresh.createDelegate(this);
11818     /**
11819      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11820      * @type Function
11821      */
11822     this.updateDelegate = this.update.createDelegate(this);
11823     /**
11824      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11825      * @type Function
11826      */
11827     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11828     /**
11829      * @private
11830      */
11831     this.successDelegate = this.processSuccess.createDelegate(this);
11832     /**
11833      * @private
11834      */
11835     this.failureDelegate = this.processFailure.createDelegate(this);
11836
11837     if(!this.renderer){
11838      /**
11839       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11840       */
11841     this.renderer = new Roo.UpdateManager.BasicRenderer();
11842     }
11843     
11844     Roo.UpdateManager.superclass.constructor.call(this);
11845 };
11846
11847 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11848     /**
11849      * Get the Element this UpdateManager is bound to
11850      * @return {Roo.Element} The element
11851      */
11852     getEl : function(){
11853         return this.el;
11854     },
11855     /**
11856      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11857      * @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:
11858 <pre><code>
11859 um.update({<br/>
11860     url: "your-url.php",<br/>
11861     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11862     callback: yourFunction,<br/>
11863     scope: yourObject, //(optional scope)  <br/>
11864     discardUrl: false, <br/>
11865     nocache: false,<br/>
11866     text: "Loading...",<br/>
11867     timeout: 30,<br/>
11868     scripts: false<br/>
11869 });
11870 </code></pre>
11871      * The only required property is url. The optional properties nocache, text and scripts
11872      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11873      * @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}
11874      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11875      * @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.
11876      */
11877     update : function(url, params, callback, discardUrl){
11878         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11879             var method = this.method,
11880                 cfg;
11881             if(typeof url == "object"){ // must be config object
11882                 cfg = url;
11883                 url = cfg.url;
11884                 params = params || cfg.params;
11885                 callback = callback || cfg.callback;
11886                 discardUrl = discardUrl || cfg.discardUrl;
11887                 if(callback && cfg.scope){
11888                     callback = callback.createDelegate(cfg.scope);
11889                 }
11890                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11891                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11892                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11893                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11894                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11895             }
11896             this.showLoading();
11897             if(!discardUrl){
11898                 this.defaultUrl = url;
11899             }
11900             if(typeof url == "function"){
11901                 url = url.call(this);
11902             }
11903
11904             method = method || (params ? "POST" : "GET");
11905             if(method == "GET"){
11906                 url = this.prepareUrl(url);
11907             }
11908
11909             var o = Roo.apply(cfg ||{}, {
11910                 url : url,
11911                 params: params,
11912                 success: this.successDelegate,
11913                 failure: this.failureDelegate,
11914                 callback: undefined,
11915                 timeout: (this.timeout*1000),
11916                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11917             });
11918             Roo.log("updated manager called with timeout of " + o.timeout);
11919             this.transaction = Roo.Ajax.request(o);
11920         }
11921     },
11922
11923     /**
11924      * 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.
11925      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11926      * @param {String/HTMLElement} form The form Id or form element
11927      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11928      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11929      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11930      */
11931     formUpdate : function(form, url, reset, callback){
11932         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11933             if(typeof url == "function"){
11934                 url = url.call(this);
11935             }
11936             form = Roo.getDom(form);
11937             this.transaction = Roo.Ajax.request({
11938                 form: form,
11939                 url:url,
11940                 success: this.successDelegate,
11941                 failure: this.failureDelegate,
11942                 timeout: (this.timeout*1000),
11943                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11944             });
11945             this.showLoading.defer(1, this);
11946         }
11947     },
11948
11949     /**
11950      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11951      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11952      */
11953     refresh : function(callback){
11954         if(this.defaultUrl == null){
11955             return;
11956         }
11957         this.update(this.defaultUrl, null, callback, true);
11958     },
11959
11960     /**
11961      * Set this element to auto refresh.
11962      * @param {Number} interval How often to update (in seconds).
11963      * @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)
11964      * @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}
11965      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11966      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11967      */
11968     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11969         if(refreshNow){
11970             this.update(url || this.defaultUrl, params, callback, true);
11971         }
11972         if(this.autoRefreshProcId){
11973             clearInterval(this.autoRefreshProcId);
11974         }
11975         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11976     },
11977
11978     /**
11979      * Stop auto refresh on this element.
11980      */
11981      stopAutoRefresh : function(){
11982         if(this.autoRefreshProcId){
11983             clearInterval(this.autoRefreshProcId);
11984             delete this.autoRefreshProcId;
11985         }
11986     },
11987
11988     isAutoRefreshing : function(){
11989        return this.autoRefreshProcId ? true : false;
11990     },
11991     /**
11992      * Called to update the element to "Loading" state. Override to perform custom action.
11993      */
11994     showLoading : function(){
11995         if(this.showLoadIndicator){
11996             this.el.update(this.indicatorText);
11997         }
11998     },
11999
12000     /**
12001      * Adds unique parameter to query string if disableCaching = true
12002      * @private
12003      */
12004     prepareUrl : function(url){
12005         if(this.disableCaching){
12006             var append = "_dc=" + (new Date().getTime());
12007             if(url.indexOf("?") !== -1){
12008                 url += "&" + append;
12009             }else{
12010                 url += "?" + append;
12011             }
12012         }
12013         return url;
12014     },
12015
12016     /**
12017      * @private
12018      */
12019     processSuccess : function(response){
12020         this.transaction = null;
12021         if(response.argument.form && response.argument.reset){
12022             try{ // put in try/catch since some older FF releases had problems with this
12023                 response.argument.form.reset();
12024             }catch(e){}
12025         }
12026         if(this.loadScripts){
12027             this.renderer.render(this.el, response, this,
12028                 this.updateComplete.createDelegate(this, [response]));
12029         }else{
12030             this.renderer.render(this.el, response, this);
12031             this.updateComplete(response);
12032         }
12033     },
12034
12035     updateComplete : function(response){
12036         this.fireEvent("update", this.el, response);
12037         if(typeof response.argument.callback == "function"){
12038             response.argument.callback(this.el, true, response);
12039         }
12040     },
12041
12042     /**
12043      * @private
12044      */
12045     processFailure : function(response){
12046         this.transaction = null;
12047         this.fireEvent("failure", this.el, response);
12048         if(typeof response.argument.callback == "function"){
12049             response.argument.callback(this.el, false, response);
12050         }
12051     },
12052
12053     /**
12054      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12055      * @param {Object} renderer The object implementing the render() method
12056      */
12057     setRenderer : function(renderer){
12058         this.renderer = renderer;
12059     },
12060
12061     getRenderer : function(){
12062        return this.renderer;
12063     },
12064
12065     /**
12066      * Set the defaultUrl used for updates
12067      * @param {String/Function} defaultUrl The url or a function to call to get the url
12068      */
12069     setDefaultUrl : function(defaultUrl){
12070         this.defaultUrl = defaultUrl;
12071     },
12072
12073     /**
12074      * Aborts the executing transaction
12075      */
12076     abort : function(){
12077         if(this.transaction){
12078             Roo.Ajax.abort(this.transaction);
12079         }
12080     },
12081
12082     /**
12083      * Returns true if an update is in progress
12084      * @return {Boolean}
12085      */
12086     isUpdating : function(){
12087         if(this.transaction){
12088             return Roo.Ajax.isLoading(this.transaction);
12089         }
12090         return false;
12091     }
12092 });
12093
12094 /**
12095  * @class Roo.UpdateManager.defaults
12096  * @static (not really - but it helps the doc tool)
12097  * The defaults collection enables customizing the default properties of UpdateManager
12098  */
12099    Roo.UpdateManager.defaults = {
12100        /**
12101          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12102          * @type Number
12103          */
12104          timeout : 30,
12105
12106          /**
12107          * True to process scripts by default (Defaults to false).
12108          * @type Boolean
12109          */
12110         loadScripts : false,
12111
12112         /**
12113         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12114         * @type String
12115         */
12116         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12117         /**
12118          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12119          * @type Boolean
12120          */
12121         disableCaching : false,
12122         /**
12123          * Whether to show indicatorText when loading (Defaults to true).
12124          * @type Boolean
12125          */
12126         showLoadIndicator : true,
12127         /**
12128          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12129          * @type String
12130          */
12131         indicatorText : '<div class="loading-indicator">Loading...</div>'
12132    };
12133
12134 /**
12135  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12136  *Usage:
12137  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12138  * @param {String/HTMLElement/Roo.Element} el The element to update
12139  * @param {String} url The url
12140  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12141  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12142  * @static
12143  * @deprecated
12144  * @member Roo.UpdateManager
12145  */
12146 Roo.UpdateManager.updateElement = function(el, url, params, options){
12147     var um = Roo.get(el, true).getUpdateManager();
12148     Roo.apply(um, options);
12149     um.update(url, params, options ? options.callback : null);
12150 };
12151 // alias for backwards compat
12152 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12153 /**
12154  * @class Roo.UpdateManager.BasicRenderer
12155  * Default Content renderer. Updates the elements innerHTML with the responseText.
12156  */
12157 Roo.UpdateManager.BasicRenderer = function(){};
12158
12159 Roo.UpdateManager.BasicRenderer.prototype = {
12160     /**
12161      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12162      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12163      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12164      * @param {Roo.Element} el The element being rendered
12165      * @param {Object} response The YUI Connect response object
12166      * @param {UpdateManager} updateManager The calling update manager
12167      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12168      */
12169      render : function(el, response, updateManager, callback){
12170         el.update(response.responseText, updateManager.loadScripts, callback);
12171     }
12172 };
12173 /*
12174  * Based on:
12175  * Roo JS
12176  * (c)) Alan Knowles
12177  * Licence : LGPL
12178  */
12179
12180
12181 /**
12182  * @class Roo.DomTemplate
12183  * @extends Roo.Template
12184  * An effort at a dom based template engine..
12185  *
12186  * Similar to XTemplate, except it uses dom parsing to create the template..
12187  *
12188  * Supported features:
12189  *
12190  *  Tags:
12191
12192 <pre><code>
12193       {a_variable} - output encoded.
12194       {a_variable.format:("Y-m-d")} - call a method on the variable
12195       {a_variable:raw} - unencoded output
12196       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12197       {a_variable:this.method_on_template(...)} - call a method on the template object.
12198  
12199 </code></pre>
12200  *  The tpl tag:
12201 <pre><code>
12202         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12203         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12204         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12205         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12206   
12207 </code></pre>
12208  *      
12209  */
12210 Roo.DomTemplate = function()
12211 {
12212      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12213      if (this.html) {
12214         this.compile();
12215      }
12216 };
12217
12218
12219 Roo.extend(Roo.DomTemplate, Roo.Template, {
12220     /**
12221      * id counter for sub templates.
12222      */
12223     id : 0,
12224     /**
12225      * flag to indicate if dom parser is inside a pre,
12226      * it will strip whitespace if not.
12227      */
12228     inPre : false,
12229     
12230     /**
12231      * The various sub templates
12232      */
12233     tpls : false,
12234     
12235     
12236     
12237     /**
12238      *
12239      * basic tag replacing syntax
12240      * WORD:WORD()
12241      *
12242      * // you can fake an object call by doing this
12243      *  x.t:(test,tesT) 
12244      * 
12245      */
12246     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12247     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12248     
12249     iterChild : function (node, method) {
12250         
12251         var oldPre = this.inPre;
12252         if (node.tagName == 'PRE') {
12253             this.inPre = true;
12254         }
12255         for( var i = 0; i < node.childNodes.length; i++) {
12256             method.call(this, node.childNodes[i]);
12257         }
12258         this.inPre = oldPre;
12259     },
12260     
12261     
12262     
12263     /**
12264      * compile the template
12265      *
12266      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12267      *
12268      */
12269     compile: function()
12270     {
12271         var s = this.html;
12272         
12273         // covert the html into DOM...
12274         var doc = false;
12275         var div =false;
12276         try {
12277             doc = document.implementation.createHTMLDocument("");
12278             doc.documentElement.innerHTML =   this.html  ;
12279             div = doc.documentElement;
12280         } catch (e) {
12281             // old IE... - nasty -- it causes all sorts of issues.. with
12282             // images getting pulled from server..
12283             div = document.createElement('div');
12284             div.innerHTML = this.html;
12285         }
12286         //doc.documentElement.innerHTML = htmlBody
12287          
12288         
12289         
12290         this.tpls = [];
12291         var _t = this;
12292         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12293         
12294         var tpls = this.tpls;
12295         
12296         // create a top level template from the snippet..
12297         
12298         //Roo.log(div.innerHTML);
12299         
12300         var tpl = {
12301             uid : 'master',
12302             id : this.id++,
12303             attr : false,
12304             value : false,
12305             body : div.innerHTML,
12306             
12307             forCall : false,
12308             execCall : false,
12309             dom : div,
12310             isTop : true
12311             
12312         };
12313         tpls.unshift(tpl);
12314         
12315         
12316         // compile them...
12317         this.tpls = [];
12318         Roo.each(tpls, function(tp){
12319             this.compileTpl(tp);
12320             this.tpls[tp.id] = tp;
12321         }, this);
12322         
12323         this.master = tpls[0];
12324         return this;
12325         
12326         
12327     },
12328     
12329     compileNode : function(node, istop) {
12330         // test for
12331         //Roo.log(node);
12332         
12333         
12334         // skip anything not a tag..
12335         if (node.nodeType != 1) {
12336             if (node.nodeType == 3 && !this.inPre) {
12337                 // reduce white space..
12338                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12339                 
12340             }
12341             return;
12342         }
12343         
12344         var tpl = {
12345             uid : false,
12346             id : false,
12347             attr : false,
12348             value : false,
12349             body : '',
12350             
12351             forCall : false,
12352             execCall : false,
12353             dom : false,
12354             isTop : istop
12355             
12356             
12357         };
12358         
12359         
12360         switch(true) {
12361             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12362             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12363             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12364             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12365             // no default..
12366         }
12367         
12368         
12369         if (!tpl.attr) {
12370             // just itterate children..
12371             this.iterChild(node,this.compileNode);
12372             return;
12373         }
12374         tpl.uid = this.id++;
12375         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12376         node.removeAttribute('roo-'+ tpl.attr);
12377         if (tpl.attr != 'name') {
12378             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12379             node.parentNode.replaceChild(placeholder,  node);
12380         } else {
12381             
12382             var placeholder =  document.createElement('span');
12383             placeholder.className = 'roo-tpl-' + tpl.value;
12384             node.parentNode.replaceChild(placeholder,  node);
12385         }
12386         
12387         // parent now sees '{domtplXXXX}
12388         this.iterChild(node,this.compileNode);
12389         
12390         // we should now have node body...
12391         var div = document.createElement('div');
12392         div.appendChild(node);
12393         tpl.dom = node;
12394         // this has the unfortunate side effect of converting tagged attributes
12395         // eg. href="{...}" into %7C...%7D
12396         // this has been fixed by searching for those combo's although it's a bit hacky..
12397         
12398         
12399         tpl.body = div.innerHTML;
12400         
12401         
12402          
12403         tpl.id = tpl.uid;
12404         switch(tpl.attr) {
12405             case 'for' :
12406                 switch (tpl.value) {
12407                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12408                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12409                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12410                 }
12411                 break;
12412             
12413             case 'exec':
12414                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12415                 break;
12416             
12417             case 'if':     
12418                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12419                 break;
12420             
12421             case 'name':
12422                 tpl.id  = tpl.value; // replace non characters???
12423                 break;
12424             
12425         }
12426         
12427         
12428         this.tpls.push(tpl);
12429         
12430         
12431         
12432     },
12433     
12434     
12435     
12436     
12437     /**
12438      * Compile a segment of the template into a 'sub-template'
12439      *
12440      * 
12441      * 
12442      *
12443      */
12444     compileTpl : function(tpl)
12445     {
12446         var fm = Roo.util.Format;
12447         var useF = this.disableFormats !== true;
12448         
12449         var sep = Roo.isGecko ? "+\n" : ",\n";
12450         
12451         var undef = function(str) {
12452             Roo.debug && Roo.log("Property not found :"  + str);
12453             return '';
12454         };
12455           
12456         //Roo.log(tpl.body);
12457         
12458         
12459         
12460         var fn = function(m, lbrace, name, format, args)
12461         {
12462             //Roo.log("ARGS");
12463             //Roo.log(arguments);
12464             args = args ? args.replace(/\\'/g,"'") : args;
12465             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12466             if (typeof(format) == 'undefined') {
12467                 format =  'htmlEncode'; 
12468             }
12469             if (format == 'raw' ) {
12470                 format = false;
12471             }
12472             
12473             if(name.substr(0, 6) == 'domtpl'){
12474                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12475             }
12476             
12477             // build an array of options to determine if value is undefined..
12478             
12479             // basically get 'xxxx.yyyy' then do
12480             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12481             //    (function () { Roo.log("Property not found"); return ''; })() :
12482             //    ......
12483             
12484             var udef_ar = [];
12485             var lookfor = '';
12486             Roo.each(name.split('.'), function(st) {
12487                 lookfor += (lookfor.length ? '.': '') + st;
12488                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12489             });
12490             
12491             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12492             
12493             
12494             if(format && useF){
12495                 
12496                 args = args ? ',' + args : "";
12497                  
12498                 if(format.substr(0, 5) != "this."){
12499                     format = "fm." + format + '(';
12500                 }else{
12501                     format = 'this.call("'+ format.substr(5) + '", ';
12502                     args = ", values";
12503                 }
12504                 
12505                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12506             }
12507              
12508             if (args && args.length) {
12509                 // called with xxyx.yuu:(test,test)
12510                 // change to ()
12511                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12512             }
12513             // raw.. - :raw modifier..
12514             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12515             
12516         };
12517         var body;
12518         // branched to use + in gecko and [].join() in others
12519         if(Roo.isGecko){
12520             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12521                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12522                     "';};};";
12523         }else{
12524             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12525             body.push(tpl.body.replace(/(\r\n|\n)/g,
12526                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12527             body.push("'].join('');};};");
12528             body = body.join('');
12529         }
12530         
12531         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12532        
12533         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12534         eval(body);
12535         
12536         return this;
12537     },
12538      
12539     /**
12540      * same as applyTemplate, except it's done to one of the subTemplates
12541      * when using named templates, you can do:
12542      *
12543      * var str = pl.applySubTemplate('your-name', values);
12544      *
12545      * 
12546      * @param {Number} id of the template
12547      * @param {Object} values to apply to template
12548      * @param {Object} parent (normaly the instance of this object)
12549      */
12550     applySubTemplate : function(id, values, parent)
12551     {
12552         
12553         
12554         var t = this.tpls[id];
12555         
12556         
12557         try { 
12558             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12559                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12560                 return '';
12561             }
12562         } catch(e) {
12563             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12564             Roo.log(values);
12565           
12566             return '';
12567         }
12568         try { 
12569             
12570             if(t.execCall && t.execCall.call(this, values, parent)){
12571                 return '';
12572             }
12573         } catch(e) {
12574             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12575             Roo.log(values);
12576             return '';
12577         }
12578         
12579         try {
12580             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12581             parent = t.target ? values : parent;
12582             if(t.forCall && vs instanceof Array){
12583                 var buf = [];
12584                 for(var i = 0, len = vs.length; i < len; i++){
12585                     try {
12586                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12587                     } catch (e) {
12588                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12589                         Roo.log(e.body);
12590                         //Roo.log(t.compiled);
12591                         Roo.log(vs[i]);
12592                     }   
12593                 }
12594                 return buf.join('');
12595             }
12596         } catch (e) {
12597             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12598             Roo.log(values);
12599             return '';
12600         }
12601         try {
12602             return t.compiled.call(this, vs, parent);
12603         } catch (e) {
12604             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12605             Roo.log(e.body);
12606             //Roo.log(t.compiled);
12607             Roo.log(values);
12608             return '';
12609         }
12610     },
12611
12612    
12613
12614     applyTemplate : function(values){
12615         return this.master.compiled.call(this, values, {});
12616         //var s = this.subs;
12617     },
12618
12619     apply : function(){
12620         return this.applyTemplate.apply(this, arguments);
12621     }
12622
12623  });
12624
12625 Roo.DomTemplate.from = function(el){
12626     el = Roo.getDom(el);
12627     return new Roo.Domtemplate(el.value || el.innerHTML);
12628 };/*
12629  * Based on:
12630  * Ext JS Library 1.1.1
12631  * Copyright(c) 2006-2007, Ext JS, LLC.
12632  *
12633  * Originally Released Under LGPL - original licence link has changed is not relivant.
12634  *
12635  * Fork - LGPL
12636  * <script type="text/javascript">
12637  */
12638
12639 /**
12640  * @class Roo.util.DelayedTask
12641  * Provides a convenient method of performing setTimeout where a new
12642  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12643  * You can use this class to buffer
12644  * the keypress events for a certain number of milliseconds, and perform only if they stop
12645  * for that amount of time.
12646  * @constructor The parameters to this constructor serve as defaults and are not required.
12647  * @param {Function} fn (optional) The default function to timeout
12648  * @param {Object} scope (optional) The default scope of that timeout
12649  * @param {Array} args (optional) The default Array of arguments
12650  */
12651 Roo.util.DelayedTask = function(fn, scope, args){
12652     var id = null, d, t;
12653
12654     var call = function(){
12655         var now = new Date().getTime();
12656         if(now - t >= d){
12657             clearInterval(id);
12658             id = null;
12659             fn.apply(scope, args || []);
12660         }
12661     };
12662     /**
12663      * Cancels any pending timeout and queues a new one
12664      * @param {Number} delay The milliseconds to delay
12665      * @param {Function} newFn (optional) Overrides function passed to constructor
12666      * @param {Object} newScope (optional) Overrides scope passed to constructor
12667      * @param {Array} newArgs (optional) Overrides args passed to constructor
12668      */
12669     this.delay = function(delay, newFn, newScope, newArgs){
12670         if(id && delay != d){
12671             this.cancel();
12672         }
12673         d = delay;
12674         t = new Date().getTime();
12675         fn = newFn || fn;
12676         scope = newScope || scope;
12677         args = newArgs || args;
12678         if(!id){
12679             id = setInterval(call, d);
12680         }
12681     };
12682
12683     /**
12684      * Cancel the last queued timeout
12685      */
12686     this.cancel = function(){
12687         if(id){
12688             clearInterval(id);
12689             id = null;
12690         }
12691     };
12692 };/*
12693  * Based on:
12694  * Ext JS Library 1.1.1
12695  * Copyright(c) 2006-2007, Ext JS, LLC.
12696  *
12697  * Originally Released Under LGPL - original licence link has changed is not relivant.
12698  *
12699  * Fork - LGPL
12700  * <script type="text/javascript">
12701  */
12702  
12703  
12704 Roo.util.TaskRunner = function(interval){
12705     interval = interval || 10;
12706     var tasks = [], removeQueue = [];
12707     var id = 0;
12708     var running = false;
12709
12710     var stopThread = function(){
12711         running = false;
12712         clearInterval(id);
12713         id = 0;
12714     };
12715
12716     var startThread = function(){
12717         if(!running){
12718             running = true;
12719             id = setInterval(runTasks, interval);
12720         }
12721     };
12722
12723     var removeTask = function(task){
12724         removeQueue.push(task);
12725         if(task.onStop){
12726             task.onStop();
12727         }
12728     };
12729
12730     var runTasks = function(){
12731         if(removeQueue.length > 0){
12732             for(var i = 0, len = removeQueue.length; i < len; i++){
12733                 tasks.remove(removeQueue[i]);
12734             }
12735             removeQueue = [];
12736             if(tasks.length < 1){
12737                 stopThread();
12738                 return;
12739             }
12740         }
12741         var now = new Date().getTime();
12742         for(var i = 0, len = tasks.length; i < len; ++i){
12743             var t = tasks[i];
12744             var itime = now - t.taskRunTime;
12745             if(t.interval <= itime){
12746                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12747                 t.taskRunTime = now;
12748                 if(rt === false || t.taskRunCount === t.repeat){
12749                     removeTask(t);
12750                     return;
12751                 }
12752             }
12753             if(t.duration && t.duration <= (now - t.taskStartTime)){
12754                 removeTask(t);
12755             }
12756         }
12757     };
12758
12759     /**
12760      * Queues a new task.
12761      * @param {Object} task
12762      */
12763     this.start = function(task){
12764         tasks.push(task);
12765         task.taskStartTime = new Date().getTime();
12766         task.taskRunTime = 0;
12767         task.taskRunCount = 0;
12768         startThread();
12769         return task;
12770     };
12771
12772     this.stop = function(task){
12773         removeTask(task);
12774         return task;
12775     };
12776
12777     this.stopAll = function(){
12778         stopThread();
12779         for(var i = 0, len = tasks.length; i < len; i++){
12780             if(tasks[i].onStop){
12781                 tasks[i].onStop();
12782             }
12783         }
12784         tasks = [];
12785         removeQueue = [];
12786     };
12787 };
12788
12789 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12790  * Based on:
12791  * Ext JS Library 1.1.1
12792  * Copyright(c) 2006-2007, Ext JS, LLC.
12793  *
12794  * Originally Released Under LGPL - original licence link has changed is not relivant.
12795  *
12796  * Fork - LGPL
12797  * <script type="text/javascript">
12798  */
12799
12800  
12801 /**
12802  * @class Roo.util.MixedCollection
12803  * @extends Roo.util.Observable
12804  * A Collection class that maintains both numeric indexes and keys and exposes events.
12805  * @constructor
12806  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12807  * collection (defaults to false)
12808  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12809  * and return the key value for that item.  This is used when available to look up the key on items that
12810  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12811  * equivalent to providing an implementation for the {@link #getKey} method.
12812  */
12813 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12814     this.items = [];
12815     this.map = {};
12816     this.keys = [];
12817     this.length = 0;
12818     this.addEvents({
12819         /**
12820          * @event clear
12821          * Fires when the collection is cleared.
12822          */
12823         "clear" : true,
12824         /**
12825          * @event add
12826          * Fires when an item is added to the collection.
12827          * @param {Number} index The index at which the item was added.
12828          * @param {Object} o The item added.
12829          * @param {String} key The key associated with the added item.
12830          */
12831         "add" : true,
12832         /**
12833          * @event replace
12834          * Fires when an item is replaced in the collection.
12835          * @param {String} key he key associated with the new added.
12836          * @param {Object} old The item being replaced.
12837          * @param {Object} new The new item.
12838          */
12839         "replace" : true,
12840         /**
12841          * @event remove
12842          * Fires when an item is removed from the collection.
12843          * @param {Object} o The item being removed.
12844          * @param {String} key (optional) The key associated with the removed item.
12845          */
12846         "remove" : true,
12847         "sort" : true
12848     });
12849     this.allowFunctions = allowFunctions === true;
12850     if(keyFn){
12851         this.getKey = keyFn;
12852     }
12853     Roo.util.MixedCollection.superclass.constructor.call(this);
12854 };
12855
12856 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12857     allowFunctions : false,
12858     
12859 /**
12860  * Adds an item to the collection.
12861  * @param {String} key The key to associate with the item
12862  * @param {Object} o The item to add.
12863  * @return {Object} The item added.
12864  */
12865     add : function(key, o){
12866         if(arguments.length == 1){
12867             o = arguments[0];
12868             key = this.getKey(o);
12869         }
12870         if(typeof key == "undefined" || key === null){
12871             this.length++;
12872             this.items.push(o);
12873             this.keys.push(null);
12874         }else{
12875             var old = this.map[key];
12876             if(old){
12877                 return this.replace(key, o);
12878             }
12879             this.length++;
12880             this.items.push(o);
12881             this.map[key] = o;
12882             this.keys.push(key);
12883         }
12884         this.fireEvent("add", this.length-1, o, key);
12885         return o;
12886     },
12887        
12888 /**
12889   * MixedCollection has a generic way to fetch keys if you implement getKey.
12890 <pre><code>
12891 // normal way
12892 var mc = new Roo.util.MixedCollection();
12893 mc.add(someEl.dom.id, someEl);
12894 mc.add(otherEl.dom.id, otherEl);
12895 //and so on
12896
12897 // using getKey
12898 var mc = new Roo.util.MixedCollection();
12899 mc.getKey = function(el){
12900    return el.dom.id;
12901 };
12902 mc.add(someEl);
12903 mc.add(otherEl);
12904
12905 // or via the constructor
12906 var mc = new Roo.util.MixedCollection(false, function(el){
12907    return el.dom.id;
12908 });
12909 mc.add(someEl);
12910 mc.add(otherEl);
12911 </code></pre>
12912  * @param o {Object} The item for which to find the key.
12913  * @return {Object} The key for the passed item.
12914  */
12915     getKey : function(o){
12916          return o.id; 
12917     },
12918    
12919 /**
12920  * Replaces an item in the collection.
12921  * @param {String} key The key associated with the item to replace, or the item to replace.
12922  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12923  * @return {Object}  The new item.
12924  */
12925     replace : function(key, o){
12926         if(arguments.length == 1){
12927             o = arguments[0];
12928             key = this.getKey(o);
12929         }
12930         var old = this.item(key);
12931         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12932              return this.add(key, o);
12933         }
12934         var index = this.indexOfKey(key);
12935         this.items[index] = o;
12936         this.map[key] = o;
12937         this.fireEvent("replace", key, old, o);
12938         return o;
12939     },
12940    
12941 /**
12942  * Adds all elements of an Array or an Object to the collection.
12943  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12944  * an Array of values, each of which are added to the collection.
12945  */
12946     addAll : function(objs){
12947         if(arguments.length > 1 || objs instanceof Array){
12948             var args = arguments.length > 1 ? arguments : objs;
12949             for(var i = 0, len = args.length; i < len; i++){
12950                 this.add(args[i]);
12951             }
12952         }else{
12953             for(var key in objs){
12954                 if(this.allowFunctions || typeof objs[key] != "function"){
12955                     this.add(key, objs[key]);
12956                 }
12957             }
12958         }
12959     },
12960    
12961 /**
12962  * Executes the specified function once for every item in the collection, passing each
12963  * item as the first and only parameter. returning false from the function will stop the iteration.
12964  * @param {Function} fn The function to execute for each item.
12965  * @param {Object} scope (optional) The scope in which to execute the function.
12966  */
12967     each : function(fn, scope){
12968         var items = [].concat(this.items); // each safe for removal
12969         for(var i = 0, len = items.length; i < len; i++){
12970             if(fn.call(scope || items[i], items[i], i, len) === false){
12971                 break;
12972             }
12973         }
12974     },
12975    
12976 /**
12977  * Executes the specified function once for every key in the collection, passing each
12978  * key, and its associated item as the first two parameters.
12979  * @param {Function} fn The function to execute for each item.
12980  * @param {Object} scope (optional) The scope in which to execute the function.
12981  */
12982     eachKey : function(fn, scope){
12983         for(var i = 0, len = this.keys.length; i < len; i++){
12984             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12985         }
12986     },
12987    
12988 /**
12989  * Returns the first item in the collection which elicits a true return value from the
12990  * passed selection function.
12991  * @param {Function} fn The selection function to execute for each item.
12992  * @param {Object} scope (optional) The scope in which to execute the function.
12993  * @return {Object} The first item in the collection which returned true from the selection function.
12994  */
12995     find : function(fn, scope){
12996         for(var i = 0, len = this.items.length; i < len; i++){
12997             if(fn.call(scope || window, this.items[i], this.keys[i])){
12998                 return this.items[i];
12999             }
13000         }
13001         return null;
13002     },
13003    
13004 /**
13005  * Inserts an item at the specified index in the collection.
13006  * @param {Number} index The index to insert the item at.
13007  * @param {String} key The key to associate with the new item, or the item itself.
13008  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13009  * @return {Object} The item inserted.
13010  */
13011     insert : function(index, key, o){
13012         if(arguments.length == 2){
13013             o = arguments[1];
13014             key = this.getKey(o);
13015         }
13016         if(index >= this.length){
13017             return this.add(key, o);
13018         }
13019         this.length++;
13020         this.items.splice(index, 0, o);
13021         if(typeof key != "undefined" && key != null){
13022             this.map[key] = o;
13023         }
13024         this.keys.splice(index, 0, key);
13025         this.fireEvent("add", index, o, key);
13026         return o;
13027     },
13028    
13029 /**
13030  * Removed an item from the collection.
13031  * @param {Object} o The item to remove.
13032  * @return {Object} The item removed.
13033  */
13034     remove : function(o){
13035         return this.removeAt(this.indexOf(o));
13036     },
13037    
13038 /**
13039  * Remove an item from a specified index in the collection.
13040  * @param {Number} index The index within the collection of the item to remove.
13041  */
13042     removeAt : function(index){
13043         if(index < this.length && index >= 0){
13044             this.length--;
13045             var o = this.items[index];
13046             this.items.splice(index, 1);
13047             var key = this.keys[index];
13048             if(typeof key != "undefined"){
13049                 delete this.map[key];
13050             }
13051             this.keys.splice(index, 1);
13052             this.fireEvent("remove", o, key);
13053         }
13054     },
13055    
13056 /**
13057  * Removed an item associated with the passed key fom the collection.
13058  * @param {String} key The key of the item to remove.
13059  */
13060     removeKey : function(key){
13061         return this.removeAt(this.indexOfKey(key));
13062     },
13063    
13064 /**
13065  * Returns the number of items in the collection.
13066  * @return {Number} the number of items in the collection.
13067  */
13068     getCount : function(){
13069         return this.length; 
13070     },
13071    
13072 /**
13073  * Returns index within the collection of the passed Object.
13074  * @param {Object} o The item to find the index of.
13075  * @return {Number} index of the item.
13076  */
13077     indexOf : function(o){
13078         if(!this.items.indexOf){
13079             for(var i = 0, len = this.items.length; i < len; i++){
13080                 if(this.items[i] == o) return i;
13081             }
13082             return -1;
13083         }else{
13084             return this.items.indexOf(o);
13085         }
13086     },
13087    
13088 /**
13089  * Returns index within the collection of the passed key.
13090  * @param {String} key The key to find the index of.
13091  * @return {Number} index of the key.
13092  */
13093     indexOfKey : function(key){
13094         if(!this.keys.indexOf){
13095             for(var i = 0, len = this.keys.length; i < len; i++){
13096                 if(this.keys[i] == key) return i;
13097             }
13098             return -1;
13099         }else{
13100             return this.keys.indexOf(key);
13101         }
13102     },
13103    
13104 /**
13105  * Returns the item associated with the passed key OR index. Key has priority over index.
13106  * @param {String/Number} key The key or index of the item.
13107  * @return {Object} The item associated with the passed key.
13108  */
13109     item : function(key){
13110         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13111         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13112     },
13113     
13114 /**
13115  * Returns the item at the specified index.
13116  * @param {Number} index The index of the item.
13117  * @return {Object}
13118  */
13119     itemAt : function(index){
13120         return this.items[index];
13121     },
13122     
13123 /**
13124  * Returns the item associated with the passed key.
13125  * @param {String/Number} key The key of the item.
13126  * @return {Object} The item associated with the passed key.
13127  */
13128     key : function(key){
13129         return this.map[key];
13130     },
13131    
13132 /**
13133  * Returns true if the collection contains the passed Object as an item.
13134  * @param {Object} o  The Object to look for in the collection.
13135  * @return {Boolean} True if the collection contains the Object as an item.
13136  */
13137     contains : function(o){
13138         return this.indexOf(o) != -1;
13139     },
13140    
13141 /**
13142  * Returns true if the collection contains the passed Object as a key.
13143  * @param {String} key The key to look for in the collection.
13144  * @return {Boolean} True if the collection contains the Object as a key.
13145  */
13146     containsKey : function(key){
13147         return typeof this.map[key] != "undefined";
13148     },
13149    
13150 /**
13151  * Removes all items from the collection.
13152  */
13153     clear : function(){
13154         this.length = 0;
13155         this.items = [];
13156         this.keys = [];
13157         this.map = {};
13158         this.fireEvent("clear");
13159     },
13160    
13161 /**
13162  * Returns the first item in the collection.
13163  * @return {Object} the first item in the collection..
13164  */
13165     first : function(){
13166         return this.items[0]; 
13167     },
13168    
13169 /**
13170  * Returns the last item in the collection.
13171  * @return {Object} the last item in the collection..
13172  */
13173     last : function(){
13174         return this.items[this.length-1];   
13175     },
13176     
13177     _sort : function(property, dir, fn){
13178         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13179         fn = fn || function(a, b){
13180             return a-b;
13181         };
13182         var c = [], k = this.keys, items = this.items;
13183         for(var i = 0, len = items.length; i < len; i++){
13184             c[c.length] = {key: k[i], value: items[i], index: i};
13185         }
13186         c.sort(function(a, b){
13187             var v = fn(a[property], b[property]) * dsc;
13188             if(v == 0){
13189                 v = (a.index < b.index ? -1 : 1);
13190             }
13191             return v;
13192         });
13193         for(var i = 0, len = c.length; i < len; i++){
13194             items[i] = c[i].value;
13195             k[i] = c[i].key;
13196         }
13197         this.fireEvent("sort", this);
13198     },
13199     
13200     /**
13201      * Sorts this collection with the passed comparison function
13202      * @param {String} direction (optional) "ASC" or "DESC"
13203      * @param {Function} fn (optional) comparison function
13204      */
13205     sort : function(dir, fn){
13206         this._sort("value", dir, fn);
13207     },
13208     
13209     /**
13210      * Sorts this collection by keys
13211      * @param {String} direction (optional) "ASC" or "DESC"
13212      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13213      */
13214     keySort : function(dir, fn){
13215         this._sort("key", dir, fn || function(a, b){
13216             return String(a).toUpperCase()-String(b).toUpperCase();
13217         });
13218     },
13219     
13220     /**
13221      * Returns a range of items in this collection
13222      * @param {Number} startIndex (optional) defaults to 0
13223      * @param {Number} endIndex (optional) default to the last item
13224      * @return {Array} An array of items
13225      */
13226     getRange : function(start, end){
13227         var items = this.items;
13228         if(items.length < 1){
13229             return [];
13230         }
13231         start = start || 0;
13232         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13233         var r = [];
13234         if(start <= end){
13235             for(var i = start; i <= end; i++) {
13236                     r[r.length] = items[i];
13237             }
13238         }else{
13239             for(var i = start; i >= end; i--) {
13240                     r[r.length] = items[i];
13241             }
13242         }
13243         return r;
13244     },
13245         
13246     /**
13247      * Filter the <i>objects</i> in this collection by a specific property. 
13248      * Returns a new collection that has been filtered.
13249      * @param {String} property A property on your objects
13250      * @param {String/RegExp} value Either string that the property values 
13251      * should start with or a RegExp to test against the property
13252      * @return {MixedCollection} The new filtered collection
13253      */
13254     filter : function(property, value){
13255         if(!value.exec){ // not a regex
13256             value = String(value);
13257             if(value.length == 0){
13258                 return this.clone();
13259             }
13260             value = new RegExp("^" + Roo.escapeRe(value), "i");
13261         }
13262         return this.filterBy(function(o){
13263             return o && value.test(o[property]);
13264         });
13265         },
13266     
13267     /**
13268      * Filter by a function. * Returns a new collection that has been filtered.
13269      * The passed function will be called with each 
13270      * object in the collection. If the function returns true, the value is included 
13271      * otherwise it is filtered.
13272      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13273      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13274      * @return {MixedCollection} The new filtered collection
13275      */
13276     filterBy : function(fn, scope){
13277         var r = new Roo.util.MixedCollection();
13278         r.getKey = this.getKey;
13279         var k = this.keys, it = this.items;
13280         for(var i = 0, len = it.length; i < len; i++){
13281             if(fn.call(scope||this, it[i], k[i])){
13282                                 r.add(k[i], it[i]);
13283                         }
13284         }
13285         return r;
13286     },
13287     
13288     /**
13289      * Creates a duplicate of this collection
13290      * @return {MixedCollection}
13291      */
13292     clone : function(){
13293         var r = new Roo.util.MixedCollection();
13294         var k = this.keys, it = this.items;
13295         for(var i = 0, len = it.length; i < len; i++){
13296             r.add(k[i], it[i]);
13297         }
13298         r.getKey = this.getKey;
13299         return r;
13300     }
13301 });
13302 /**
13303  * Returns the item associated with the passed key or index.
13304  * @method
13305  * @param {String/Number} key The key or index of the item.
13306  * @return {Object} The item associated with the passed key.
13307  */
13308 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13309  * Based on:
13310  * Ext JS Library 1.1.1
13311  * Copyright(c) 2006-2007, Ext JS, LLC.
13312  *
13313  * Originally Released Under LGPL - original licence link has changed is not relivant.
13314  *
13315  * Fork - LGPL
13316  * <script type="text/javascript">
13317  */
13318 /**
13319  * @class Roo.util.JSON
13320  * Modified version of Douglas Crockford"s json.js that doesn"t
13321  * mess with the Object prototype 
13322  * http://www.json.org/js.html
13323  * @singleton
13324  */
13325 Roo.util.JSON = new (function(){
13326     var useHasOwn = {}.hasOwnProperty ? true : false;
13327     
13328     // crashes Safari in some instances
13329     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13330     
13331     var pad = function(n) {
13332         return n < 10 ? "0" + n : n;
13333     };
13334     
13335     var m = {
13336         "\b": '\\b',
13337         "\t": '\\t',
13338         "\n": '\\n',
13339         "\f": '\\f',
13340         "\r": '\\r',
13341         '"' : '\\"',
13342         "\\": '\\\\'
13343     };
13344
13345     var encodeString = function(s){
13346         if (/["\\\x00-\x1f]/.test(s)) {
13347             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13348                 var c = m[b];
13349                 if(c){
13350                     return c;
13351                 }
13352                 c = b.charCodeAt();
13353                 return "\\u00" +
13354                     Math.floor(c / 16).toString(16) +
13355                     (c % 16).toString(16);
13356             }) + '"';
13357         }
13358         return '"' + s + '"';
13359     };
13360     
13361     var encodeArray = function(o){
13362         var a = ["["], b, i, l = o.length, v;
13363             for (i = 0; i < l; i += 1) {
13364                 v = o[i];
13365                 switch (typeof v) {
13366                     case "undefined":
13367                     case "function":
13368                     case "unknown":
13369                         break;
13370                     default:
13371                         if (b) {
13372                             a.push(',');
13373                         }
13374                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13375                         b = true;
13376                 }
13377             }
13378             a.push("]");
13379             return a.join("");
13380     };
13381     
13382     var encodeDate = function(o){
13383         return '"' + o.getFullYear() + "-" +
13384                 pad(o.getMonth() + 1) + "-" +
13385                 pad(o.getDate()) + "T" +
13386                 pad(o.getHours()) + ":" +
13387                 pad(o.getMinutes()) + ":" +
13388                 pad(o.getSeconds()) + '"';
13389     };
13390     
13391     /**
13392      * Encodes an Object, Array or other value
13393      * @param {Mixed} o The variable to encode
13394      * @return {String} The JSON string
13395      */
13396     this.encode = function(o)
13397     {
13398         // should this be extended to fully wrap stringify..
13399         
13400         if(typeof o == "undefined" || o === null){
13401             return "null";
13402         }else if(o instanceof Array){
13403             return encodeArray(o);
13404         }else if(o instanceof Date){
13405             return encodeDate(o);
13406         }else if(typeof o == "string"){
13407             return encodeString(o);
13408         }else if(typeof o == "number"){
13409             return isFinite(o) ? String(o) : "null";
13410         }else if(typeof o == "boolean"){
13411             return String(o);
13412         }else {
13413             var a = ["{"], b, i, v;
13414             for (i in o) {
13415                 if(!useHasOwn || o.hasOwnProperty(i)) {
13416                     v = o[i];
13417                     switch (typeof v) {
13418                     case "undefined":
13419                     case "function":
13420                     case "unknown":
13421                         break;
13422                     default:
13423                         if(b){
13424                             a.push(',');
13425                         }
13426                         a.push(this.encode(i), ":",
13427                                 v === null ? "null" : this.encode(v));
13428                         b = true;
13429                     }
13430                 }
13431             }
13432             a.push("}");
13433             return a.join("");
13434         }
13435     };
13436     
13437     /**
13438      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13439      * @param {String} json The JSON string
13440      * @return {Object} The resulting object
13441      */
13442     this.decode = function(json){
13443         
13444         return  /** eval:var:json */ eval("(" + json + ')');
13445     };
13446 })();
13447 /** 
13448  * Shorthand for {@link Roo.util.JSON#encode}
13449  * @member Roo encode 
13450  * @method */
13451 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13452 /** 
13453  * Shorthand for {@link Roo.util.JSON#decode}
13454  * @member Roo decode 
13455  * @method */
13456 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13457 /*
13458  * Based on:
13459  * Ext JS Library 1.1.1
13460  * Copyright(c) 2006-2007, Ext JS, LLC.
13461  *
13462  * Originally Released Under LGPL - original licence link has changed is not relivant.
13463  *
13464  * Fork - LGPL
13465  * <script type="text/javascript">
13466  */
13467  
13468 /**
13469  * @class Roo.util.Format
13470  * Reusable data formatting functions
13471  * @singleton
13472  */
13473 Roo.util.Format = function(){
13474     var trimRe = /^\s+|\s+$/g;
13475     return {
13476         /**
13477          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13478          * @param {String} value The string to truncate
13479          * @param {Number} length The maximum length to allow before truncating
13480          * @return {String} The converted text
13481          */
13482         ellipsis : function(value, len){
13483             if(value && value.length > len){
13484                 return value.substr(0, len-3)+"...";
13485             }
13486             return value;
13487         },
13488
13489         /**
13490          * Checks a reference and converts it to empty string if it is undefined
13491          * @param {Mixed} value Reference to check
13492          * @return {Mixed} Empty string if converted, otherwise the original value
13493          */
13494         undef : function(value){
13495             return typeof value != "undefined" ? value : "";
13496         },
13497
13498         /**
13499          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13500          * @param {String} value The string to encode
13501          * @return {String} The encoded text
13502          */
13503         htmlEncode : function(value){
13504             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13505         },
13506
13507         /**
13508          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13509          * @param {String} value The string to decode
13510          * @return {String} The decoded text
13511          */
13512         htmlDecode : function(value){
13513             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13514         },
13515
13516         /**
13517          * Trims any whitespace from either side of a string
13518          * @param {String} value The text to trim
13519          * @return {String} The trimmed text
13520          */
13521         trim : function(value){
13522             return String(value).replace(trimRe, "");
13523         },
13524
13525         /**
13526          * Returns a substring from within an original string
13527          * @param {String} value The original text
13528          * @param {Number} start The start index of the substring
13529          * @param {Number} length The length of the substring
13530          * @return {String} The substring
13531          */
13532         substr : function(value, start, length){
13533             return String(value).substr(start, length);
13534         },
13535
13536         /**
13537          * Converts a string to all lower case letters
13538          * @param {String} value The text to convert
13539          * @return {String} The converted text
13540          */
13541         lowercase : function(value){
13542             return String(value).toLowerCase();
13543         },
13544
13545         /**
13546          * Converts a string to all upper case letters
13547          * @param {String} value The text to convert
13548          * @return {String} The converted text
13549          */
13550         uppercase : function(value){
13551             return String(value).toUpperCase();
13552         },
13553
13554         /**
13555          * Converts the first character only of a string to upper case
13556          * @param {String} value The text to convert
13557          * @return {String} The converted text
13558          */
13559         capitalize : function(value){
13560             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13561         },
13562
13563         // private
13564         call : function(value, fn){
13565             if(arguments.length > 2){
13566                 var args = Array.prototype.slice.call(arguments, 2);
13567                 args.unshift(value);
13568                  
13569                 return /** eval:var:value */  eval(fn).apply(window, args);
13570             }else{
13571                 /** eval:var:value */
13572                 return /** eval:var:value */ eval(fn).call(window, value);
13573             }
13574         },
13575
13576        
13577         /**
13578          * safer version of Math.toFixed..??/
13579          * @param {Number/String} value The numeric value to format
13580          * @param {Number/String} value Decimal places 
13581          * @return {String} The formatted currency string
13582          */
13583         toFixed : function(v, n)
13584         {
13585             // why not use to fixed - precision is buggered???
13586             if (!n) {
13587                 return Math.round(v-0);
13588             }
13589             var fact = Math.pow(10,n+1);
13590             v = (Math.round((v-0)*fact))/fact;
13591             var z = (''+fact).substring(2);
13592             if (v == Math.floor(v)) {
13593                 return Math.floor(v) + '.' + z;
13594             }
13595             
13596             // now just padd decimals..
13597             var ps = String(v).split('.');
13598             var fd = (ps[1] + z);
13599             var r = fd.substring(0,n); 
13600             var rm = fd.substring(n); 
13601             if (rm < 5) {
13602                 return ps[0] + '.' + r;
13603             }
13604             r*=1; // turn it into a number;
13605             r++;
13606             if (String(r).length != n) {
13607                 ps[0]*=1;
13608                 ps[0]++;
13609                 r = String(r).substring(1); // chop the end off.
13610             }
13611             
13612             return ps[0] + '.' + r;
13613              
13614         },
13615         
13616         /**
13617          * Format a number as US currency
13618          * @param {Number/String} value The numeric value to format
13619          * @return {String} The formatted currency string
13620          */
13621         usMoney : function(v){
13622             return '$' + Roo.util.Format.number(v);
13623         },
13624         
13625         /**
13626          * Format a number
13627          * eventually this should probably emulate php's number_format
13628          * @param {Number/String} value The numeric value to format
13629          * @param {Number} decimals number of decimal places
13630          * @return {String} The formatted currency string
13631          */
13632         number : function(v,decimals)
13633         {
13634             // multiply and round.
13635             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13636             var mul = Math.pow(10, decimals);
13637             var zero = String(mul).substring(1);
13638             v = (Math.round((v-0)*mul))/mul;
13639             
13640             // if it's '0' number.. then
13641             
13642             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13643             v = String(v);
13644             var ps = v.split('.');
13645             var whole = ps[0];
13646             
13647             
13648             var r = /(\d+)(\d{3})/;
13649             // add comma's
13650             while (r.test(whole)) {
13651                 whole = whole.replace(r, '$1' + ',' + '$2');
13652             }
13653             
13654             
13655             var sub = ps[1] ?
13656                     // has decimals..
13657                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13658                     // does not have decimals
13659                     (decimals ? ('.' + zero) : '');
13660             
13661             
13662             return whole + sub ;
13663         },
13664         
13665         /**
13666          * Parse a value into a formatted date using the specified format pattern.
13667          * @param {Mixed} value The value to format
13668          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13669          * @return {String} The formatted date string
13670          */
13671         date : function(v, format){
13672             if(!v){
13673                 return "";
13674             }
13675             if(!(v instanceof Date)){
13676                 v = new Date(Date.parse(v));
13677             }
13678             return v.dateFormat(format || Roo.util.Format.defaults.date);
13679         },
13680
13681         /**
13682          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13683          * @param {String} format Any valid date format string
13684          * @return {Function} The date formatting function
13685          */
13686         dateRenderer : function(format){
13687             return function(v){
13688                 return Roo.util.Format.date(v, format);  
13689             };
13690         },
13691
13692         // private
13693         stripTagsRE : /<\/?[^>]+>/gi,
13694         
13695         /**
13696          * Strips all HTML tags
13697          * @param {Mixed} value The text from which to strip tags
13698          * @return {String} The stripped text
13699          */
13700         stripTags : function(v){
13701             return !v ? v : String(v).replace(this.stripTagsRE, "");
13702         }
13703     };
13704 }();
13705 Roo.util.Format.defaults = {
13706     date : 'd/M/Y'
13707 };/*
13708  * Based on:
13709  * Ext JS Library 1.1.1
13710  * Copyright(c) 2006-2007, Ext JS, LLC.
13711  *
13712  * Originally Released Under LGPL - original licence link has changed is not relivant.
13713  *
13714  * Fork - LGPL
13715  * <script type="text/javascript">
13716  */
13717
13718
13719  
13720
13721 /**
13722  * @class Roo.MasterTemplate
13723  * @extends Roo.Template
13724  * Provides a template that can have child templates. The syntax is:
13725 <pre><code>
13726 var t = new Roo.MasterTemplate(
13727         '&lt;select name="{name}"&gt;',
13728                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13729         '&lt;/select&gt;'
13730 );
13731 t.add('options', {value: 'foo', text: 'bar'});
13732 // or you can add multiple child elements in one shot
13733 t.addAll('options', [
13734     {value: 'foo', text: 'bar'},
13735     {value: 'foo2', text: 'bar2'},
13736     {value: 'foo3', text: 'bar3'}
13737 ]);
13738 // then append, applying the master template values
13739 t.append('my-form', {name: 'my-select'});
13740 </code></pre>
13741 * A name attribute for the child template is not required if you have only one child
13742 * template or you want to refer to them by index.
13743  */
13744 Roo.MasterTemplate = function(){
13745     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13746     this.originalHtml = this.html;
13747     var st = {};
13748     var m, re = this.subTemplateRe;
13749     re.lastIndex = 0;
13750     var subIndex = 0;
13751     while(m = re.exec(this.html)){
13752         var name = m[1], content = m[2];
13753         st[subIndex] = {
13754             name: name,
13755             index: subIndex,
13756             buffer: [],
13757             tpl : new Roo.Template(content)
13758         };
13759         if(name){
13760             st[name] = st[subIndex];
13761         }
13762         st[subIndex].tpl.compile();
13763         st[subIndex].tpl.call = this.call.createDelegate(this);
13764         subIndex++;
13765     }
13766     this.subCount = subIndex;
13767     this.subs = st;
13768 };
13769 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13770     /**
13771     * The regular expression used to match sub templates
13772     * @type RegExp
13773     * @property
13774     */
13775     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13776
13777     /**
13778      * Applies the passed values to a child template.
13779      * @param {String/Number} name (optional) The name or index of the child template
13780      * @param {Array/Object} values The values to be applied to the template
13781      * @return {MasterTemplate} this
13782      */
13783      add : function(name, values){
13784         if(arguments.length == 1){
13785             values = arguments[0];
13786             name = 0;
13787         }
13788         var s = this.subs[name];
13789         s.buffer[s.buffer.length] = s.tpl.apply(values);
13790         return this;
13791     },
13792
13793     /**
13794      * Applies all the passed values to a child template.
13795      * @param {String/Number} name (optional) The name or index of the child template
13796      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13797      * @param {Boolean} reset (optional) True to reset the template first
13798      * @return {MasterTemplate} this
13799      */
13800     fill : function(name, values, reset){
13801         var a = arguments;
13802         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13803             values = a[0];
13804             name = 0;
13805             reset = a[1];
13806         }
13807         if(reset){
13808             this.reset();
13809         }
13810         for(var i = 0, len = values.length; i < len; i++){
13811             this.add(name, values[i]);
13812         }
13813         return this;
13814     },
13815
13816     /**
13817      * Resets the template for reuse
13818      * @return {MasterTemplate} this
13819      */
13820      reset : function(){
13821         var s = this.subs;
13822         for(var i = 0; i < this.subCount; i++){
13823             s[i].buffer = [];
13824         }
13825         return this;
13826     },
13827
13828     applyTemplate : function(values){
13829         var s = this.subs;
13830         var replaceIndex = -1;
13831         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13832             return s[++replaceIndex].buffer.join("");
13833         });
13834         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13835     },
13836
13837     apply : function(){
13838         return this.applyTemplate.apply(this, arguments);
13839     },
13840
13841     compile : function(){return this;}
13842 });
13843
13844 /**
13845  * Alias for fill().
13846  * @method
13847  */
13848 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13849  /**
13850  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13851  * var tpl = Roo.MasterTemplate.from('element-id');
13852  * @param {String/HTMLElement} el
13853  * @param {Object} config
13854  * @static
13855  */
13856 Roo.MasterTemplate.from = function(el, config){
13857     el = Roo.getDom(el);
13858     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13859 };/*
13860  * Based on:
13861  * Ext JS Library 1.1.1
13862  * Copyright(c) 2006-2007, Ext JS, LLC.
13863  *
13864  * Originally Released Under LGPL - original licence link has changed is not relivant.
13865  *
13866  * Fork - LGPL
13867  * <script type="text/javascript">
13868  */
13869
13870  
13871 /**
13872  * @class Roo.util.CSS
13873  * Utility class for manipulating CSS rules
13874  * @singleton
13875  */
13876 Roo.util.CSS = function(){
13877         var rules = null;
13878         var doc = document;
13879
13880     var camelRe = /(-[a-z])/gi;
13881     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13882
13883    return {
13884    /**
13885     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13886     * tag and appended to the HEAD of the document.
13887     * @param {String|Object} cssText The text containing the css rules
13888     * @param {String} id An id to add to the stylesheet for later removal
13889     * @return {StyleSheet}
13890     */
13891     createStyleSheet : function(cssText, id){
13892         var ss;
13893         var head = doc.getElementsByTagName("head")[0];
13894         var nrules = doc.createElement("style");
13895         nrules.setAttribute("type", "text/css");
13896         if(id){
13897             nrules.setAttribute("id", id);
13898         }
13899         if (typeof(cssText) != 'string') {
13900             // support object maps..
13901             // not sure if this a good idea.. 
13902             // perhaps it should be merged with the general css handling
13903             // and handle js style props.
13904             var cssTextNew = [];
13905             for(var n in cssText) {
13906                 var citems = [];
13907                 for(var k in cssText[n]) {
13908                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13909                 }
13910                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13911                 
13912             }
13913             cssText = cssTextNew.join("\n");
13914             
13915         }
13916        
13917        
13918        if(Roo.isIE){
13919            head.appendChild(nrules);
13920            ss = nrules.styleSheet;
13921            ss.cssText = cssText;
13922        }else{
13923            try{
13924                 nrules.appendChild(doc.createTextNode(cssText));
13925            }catch(e){
13926                nrules.cssText = cssText; 
13927            }
13928            head.appendChild(nrules);
13929            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13930        }
13931        this.cacheStyleSheet(ss);
13932        return ss;
13933    },
13934
13935    /**
13936     * Removes a style or link tag by id
13937     * @param {String} id The id of the tag
13938     */
13939    removeStyleSheet : function(id){
13940        var existing = doc.getElementById(id);
13941        if(existing){
13942            existing.parentNode.removeChild(existing);
13943        }
13944    },
13945
13946    /**
13947     * Dynamically swaps an existing stylesheet reference for a new one
13948     * @param {String} id The id of an existing link tag to remove
13949     * @param {String} url The href of the new stylesheet to include
13950     */
13951    swapStyleSheet : function(id, url){
13952        this.removeStyleSheet(id);
13953        var ss = doc.createElement("link");
13954        ss.setAttribute("rel", "stylesheet");
13955        ss.setAttribute("type", "text/css");
13956        ss.setAttribute("id", id);
13957        ss.setAttribute("href", url);
13958        doc.getElementsByTagName("head")[0].appendChild(ss);
13959    },
13960    
13961    /**
13962     * Refresh the rule cache if you have dynamically added stylesheets
13963     * @return {Object} An object (hash) of rules indexed by selector
13964     */
13965    refreshCache : function(){
13966        return this.getRules(true);
13967    },
13968
13969    // private
13970    cacheStyleSheet : function(stylesheet){
13971        if(!rules){
13972            rules = {};
13973        }
13974        try{// try catch for cross domain access issue
13975            var ssRules = stylesheet.cssRules || stylesheet.rules;
13976            for(var j = ssRules.length-1; j >= 0; --j){
13977                rules[ssRules[j].selectorText] = ssRules[j];
13978            }
13979        }catch(e){}
13980    },
13981    
13982    /**
13983     * Gets all css rules for the document
13984     * @param {Boolean} refreshCache true to refresh the internal cache
13985     * @return {Object} An object (hash) of rules indexed by selector
13986     */
13987    getRules : function(refreshCache){
13988                 if(rules == null || refreshCache){
13989                         rules = {};
13990                         var ds = doc.styleSheets;
13991                         for(var i =0, len = ds.length; i < len; i++){
13992                             try{
13993                         this.cacheStyleSheet(ds[i]);
13994                     }catch(e){} 
13995                 }
13996                 }
13997                 return rules;
13998         },
13999         
14000         /**
14001     * Gets an an individual CSS rule by selector(s)
14002     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14003     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14004     * @return {CSSRule} The CSS rule or null if one is not found
14005     */
14006    getRule : function(selector, refreshCache){
14007                 var rs = this.getRules(refreshCache);
14008                 if(!(selector instanceof Array)){
14009                     return rs[selector];
14010                 }
14011                 for(var i = 0; i < selector.length; i++){
14012                         if(rs[selector[i]]){
14013                                 return rs[selector[i]];
14014                         }
14015                 }
14016                 return null;
14017         },
14018         
14019         
14020         /**
14021     * Updates a rule property
14022     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14023     * @param {String} property The css property
14024     * @param {String} value The new value for the property
14025     * @return {Boolean} true If a rule was found and updated
14026     */
14027    updateRule : function(selector, property, value){
14028                 if(!(selector instanceof Array)){
14029                         var rule = this.getRule(selector);
14030                         if(rule){
14031                                 rule.style[property.replace(camelRe, camelFn)] = value;
14032                                 return true;
14033                         }
14034                 }else{
14035                         for(var i = 0; i < selector.length; i++){
14036                                 if(this.updateRule(selector[i], property, value)){
14037                                         return true;
14038                                 }
14039                         }
14040                 }
14041                 return false;
14042         }
14043    };   
14044 }();/*
14045  * Based on:
14046  * Ext JS Library 1.1.1
14047  * Copyright(c) 2006-2007, Ext JS, LLC.
14048  *
14049  * Originally Released Under LGPL - original licence link has changed is not relivant.
14050  *
14051  * Fork - LGPL
14052  * <script type="text/javascript">
14053  */
14054
14055  
14056
14057 /**
14058  * @class Roo.util.ClickRepeater
14059  * @extends Roo.util.Observable
14060  * 
14061  * A wrapper class which can be applied to any element. Fires a "click" event while the
14062  * mouse is pressed. The interval between firings may be specified in the config but
14063  * defaults to 10 milliseconds.
14064  * 
14065  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14066  * 
14067  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14068  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14069  * Similar to an autorepeat key delay.
14070  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14071  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14072  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14073  *           "interval" and "delay" are ignored. "immediate" is honored.
14074  * @cfg {Boolean} preventDefault True to prevent the default click event
14075  * @cfg {Boolean} stopDefault True to stop the default click event
14076  * 
14077  * @history
14078  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14079  *     2007-02-02 jvs Renamed to ClickRepeater
14080  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14081  *
14082  *  @constructor
14083  * @param {String/HTMLElement/Element} el The element to listen on
14084  * @param {Object} config
14085  **/
14086 Roo.util.ClickRepeater = function(el, config)
14087 {
14088     this.el = Roo.get(el);
14089     this.el.unselectable();
14090
14091     Roo.apply(this, config);
14092
14093     this.addEvents({
14094     /**
14095      * @event mousedown
14096      * Fires when the mouse button is depressed.
14097      * @param {Roo.util.ClickRepeater} this
14098      */
14099         "mousedown" : true,
14100     /**
14101      * @event click
14102      * Fires on a specified interval during the time the element is pressed.
14103      * @param {Roo.util.ClickRepeater} this
14104      */
14105         "click" : true,
14106     /**
14107      * @event mouseup
14108      * Fires when the mouse key is released.
14109      * @param {Roo.util.ClickRepeater} this
14110      */
14111         "mouseup" : true
14112     });
14113
14114     this.el.on("mousedown", this.handleMouseDown, this);
14115     if(this.preventDefault || this.stopDefault){
14116         this.el.on("click", function(e){
14117             if(this.preventDefault){
14118                 e.preventDefault();
14119             }
14120             if(this.stopDefault){
14121                 e.stopEvent();
14122             }
14123         }, this);
14124     }
14125
14126     // allow inline handler
14127     if(this.handler){
14128         this.on("click", this.handler,  this.scope || this);
14129     }
14130
14131     Roo.util.ClickRepeater.superclass.constructor.call(this);
14132 };
14133
14134 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14135     interval : 20,
14136     delay: 250,
14137     preventDefault : true,
14138     stopDefault : false,
14139     timer : 0,
14140
14141     // private
14142     handleMouseDown : function(){
14143         clearTimeout(this.timer);
14144         this.el.blur();
14145         if(this.pressClass){
14146             this.el.addClass(this.pressClass);
14147         }
14148         this.mousedownTime = new Date();
14149
14150         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14151         this.el.on("mouseout", this.handleMouseOut, this);
14152
14153         this.fireEvent("mousedown", this);
14154         this.fireEvent("click", this);
14155         
14156         this.timer = this.click.defer(this.delay || this.interval, this);
14157     },
14158
14159     // private
14160     click : function(){
14161         this.fireEvent("click", this);
14162         this.timer = this.click.defer(this.getInterval(), this);
14163     },
14164
14165     // private
14166     getInterval: function(){
14167         if(!this.accelerate){
14168             return this.interval;
14169         }
14170         var pressTime = this.mousedownTime.getElapsed();
14171         if(pressTime < 500){
14172             return 400;
14173         }else if(pressTime < 1700){
14174             return 320;
14175         }else if(pressTime < 2600){
14176             return 250;
14177         }else if(pressTime < 3500){
14178             return 180;
14179         }else if(pressTime < 4400){
14180             return 140;
14181         }else if(pressTime < 5300){
14182             return 80;
14183         }else if(pressTime < 6200){
14184             return 50;
14185         }else{
14186             return 10;
14187         }
14188     },
14189
14190     // private
14191     handleMouseOut : function(){
14192         clearTimeout(this.timer);
14193         if(this.pressClass){
14194             this.el.removeClass(this.pressClass);
14195         }
14196         this.el.on("mouseover", this.handleMouseReturn, this);
14197     },
14198
14199     // private
14200     handleMouseReturn : function(){
14201         this.el.un("mouseover", this.handleMouseReturn);
14202         if(this.pressClass){
14203             this.el.addClass(this.pressClass);
14204         }
14205         this.click();
14206     },
14207
14208     // private
14209     handleMouseUp : function(){
14210         clearTimeout(this.timer);
14211         this.el.un("mouseover", this.handleMouseReturn);
14212         this.el.un("mouseout", this.handleMouseOut);
14213         Roo.get(document).un("mouseup", this.handleMouseUp);
14214         this.el.removeClass(this.pressClass);
14215         this.fireEvent("mouseup", this);
14216     }
14217 });/*
14218  * Based on:
14219  * Ext JS Library 1.1.1
14220  * Copyright(c) 2006-2007, Ext JS, LLC.
14221  *
14222  * Originally Released Under LGPL - original licence link has changed is not relivant.
14223  *
14224  * Fork - LGPL
14225  * <script type="text/javascript">
14226  */
14227
14228  
14229 /**
14230  * @class Roo.KeyNav
14231  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14232  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14233  * way to implement custom navigation schemes for any UI component.</p>
14234  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14235  * pageUp, pageDown, del, home, end.  Usage:</p>
14236  <pre><code>
14237 var nav = new Roo.KeyNav("my-element", {
14238     "left" : function(e){
14239         this.moveLeft(e.ctrlKey);
14240     },
14241     "right" : function(e){
14242         this.moveRight(e.ctrlKey);
14243     },
14244     "enter" : function(e){
14245         this.save();
14246     },
14247     scope : this
14248 });
14249 </code></pre>
14250  * @constructor
14251  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14252  * @param {Object} config The config
14253  */
14254 Roo.KeyNav = function(el, config){
14255     this.el = Roo.get(el);
14256     Roo.apply(this, config);
14257     if(!this.disabled){
14258         this.disabled = true;
14259         this.enable();
14260     }
14261 };
14262
14263 Roo.KeyNav.prototype = {
14264     /**
14265      * @cfg {Boolean} disabled
14266      * True to disable this KeyNav instance (defaults to false)
14267      */
14268     disabled : false,
14269     /**
14270      * @cfg {String} defaultEventAction
14271      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14272      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14273      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14274      */
14275     defaultEventAction: "stopEvent",
14276     /**
14277      * @cfg {Boolean} forceKeyDown
14278      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14279      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14280      * handle keydown instead of keypress.
14281      */
14282     forceKeyDown : false,
14283
14284     // private
14285     prepareEvent : function(e){
14286         var k = e.getKey();
14287         var h = this.keyToHandler[k];
14288         //if(h && this[h]){
14289         //    e.stopPropagation();
14290         //}
14291         if(Roo.isSafari && h && k >= 37 && k <= 40){
14292             e.stopEvent();
14293         }
14294     },
14295
14296     // private
14297     relay : function(e){
14298         var k = e.getKey();
14299         var h = this.keyToHandler[k];
14300         if(h && this[h]){
14301             if(this.doRelay(e, this[h], h) !== true){
14302                 e[this.defaultEventAction]();
14303             }
14304         }
14305     },
14306
14307     // private
14308     doRelay : function(e, h, hname){
14309         return h.call(this.scope || this, e);
14310     },
14311
14312     // possible handlers
14313     enter : false,
14314     left : false,
14315     right : false,
14316     up : false,
14317     down : false,
14318     tab : false,
14319     esc : false,
14320     pageUp : false,
14321     pageDown : false,
14322     del : false,
14323     home : false,
14324     end : false,
14325
14326     // quick lookup hash
14327     keyToHandler : {
14328         37 : "left",
14329         39 : "right",
14330         38 : "up",
14331         40 : "down",
14332         33 : "pageUp",
14333         34 : "pageDown",
14334         46 : "del",
14335         36 : "home",
14336         35 : "end",
14337         13 : "enter",
14338         27 : "esc",
14339         9  : "tab"
14340     },
14341
14342         /**
14343          * Enable this KeyNav
14344          */
14345         enable: function(){
14346                 if(this.disabled){
14347             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14348             // the EventObject will normalize Safari automatically
14349             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14350                 this.el.on("keydown", this.relay,  this);
14351             }else{
14352                 this.el.on("keydown", this.prepareEvent,  this);
14353                 this.el.on("keypress", this.relay,  this);
14354             }
14355                     this.disabled = false;
14356                 }
14357         },
14358
14359         /**
14360          * Disable this KeyNav
14361          */
14362         disable: function(){
14363                 if(!this.disabled){
14364                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14365                 this.el.un("keydown", this.relay);
14366             }else{
14367                 this.el.un("keydown", this.prepareEvent);
14368                 this.el.un("keypress", this.relay);
14369             }
14370                     this.disabled = true;
14371                 }
14372         }
14373 };/*
14374  * Based on:
14375  * Ext JS Library 1.1.1
14376  * Copyright(c) 2006-2007, Ext JS, LLC.
14377  *
14378  * Originally Released Under LGPL - original licence link has changed is not relivant.
14379  *
14380  * Fork - LGPL
14381  * <script type="text/javascript">
14382  */
14383
14384  
14385 /**
14386  * @class Roo.KeyMap
14387  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14388  * The constructor accepts the same config object as defined by {@link #addBinding}.
14389  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14390  * combination it will call the function with this signature (if the match is a multi-key
14391  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14392  * A KeyMap can also handle a string representation of keys.<br />
14393  * Usage:
14394  <pre><code>
14395 // map one key by key code
14396 var map = new Roo.KeyMap("my-element", {
14397     key: 13, // or Roo.EventObject.ENTER
14398     fn: myHandler,
14399     scope: myObject
14400 });
14401
14402 // map multiple keys to one action by string
14403 var map = new Roo.KeyMap("my-element", {
14404     key: "a\r\n\t",
14405     fn: myHandler,
14406     scope: myObject
14407 });
14408
14409 // map multiple keys to multiple actions by strings and array of codes
14410 var map = new Roo.KeyMap("my-element", [
14411     {
14412         key: [10,13],
14413         fn: function(){ alert("Return was pressed"); }
14414     }, {
14415         key: "abc",
14416         fn: function(){ alert('a, b or c was pressed'); }
14417     }, {
14418         key: "\t",
14419         ctrl:true,
14420         shift:true,
14421         fn: function(){ alert('Control + shift + tab was pressed.'); }
14422     }
14423 ]);
14424 </code></pre>
14425  * <b>Note: A KeyMap starts enabled</b>
14426  * @constructor
14427  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14428  * @param {Object} config The config (see {@link #addBinding})
14429  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14430  */
14431 Roo.KeyMap = function(el, config, eventName){
14432     this.el  = Roo.get(el);
14433     this.eventName = eventName || "keydown";
14434     this.bindings = [];
14435     if(config){
14436         this.addBinding(config);
14437     }
14438     this.enable();
14439 };
14440
14441 Roo.KeyMap.prototype = {
14442     /**
14443      * True to stop the event from bubbling and prevent the default browser action if the
14444      * key was handled by the KeyMap (defaults to false)
14445      * @type Boolean
14446      */
14447     stopEvent : false,
14448
14449     /**
14450      * Add a new binding to this KeyMap. The following config object properties are supported:
14451      * <pre>
14452 Property    Type             Description
14453 ----------  ---------------  ----------------------------------------------------------------------
14454 key         String/Array     A single keycode or an array of keycodes to handle
14455 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14456 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14457 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14458 fn          Function         The function to call when KeyMap finds the expected key combination
14459 scope       Object           The scope of the callback function
14460 </pre>
14461      *
14462      * Usage:
14463      * <pre><code>
14464 // Create a KeyMap
14465 var map = new Roo.KeyMap(document, {
14466     key: Roo.EventObject.ENTER,
14467     fn: handleKey,
14468     scope: this
14469 });
14470
14471 //Add a new binding to the existing KeyMap later
14472 map.addBinding({
14473     key: 'abc',
14474     shift: true,
14475     fn: handleKey,
14476     scope: this
14477 });
14478 </code></pre>
14479      * @param {Object/Array} config A single KeyMap config or an array of configs
14480      */
14481         addBinding : function(config){
14482         if(config instanceof Array){
14483             for(var i = 0, len = config.length; i < len; i++){
14484                 this.addBinding(config[i]);
14485             }
14486             return;
14487         }
14488         var keyCode = config.key,
14489             shift = config.shift, 
14490             ctrl = config.ctrl, 
14491             alt = config.alt,
14492             fn = config.fn,
14493             scope = config.scope;
14494         if(typeof keyCode == "string"){
14495             var ks = [];
14496             var keyString = keyCode.toUpperCase();
14497             for(var j = 0, len = keyString.length; j < len; j++){
14498                 ks.push(keyString.charCodeAt(j));
14499             }
14500             keyCode = ks;
14501         }
14502         var keyArray = keyCode instanceof Array;
14503         var handler = function(e){
14504             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14505                 var k = e.getKey();
14506                 if(keyArray){
14507                     for(var i = 0, len = keyCode.length; i < len; i++){
14508                         if(keyCode[i] == k){
14509                           if(this.stopEvent){
14510                               e.stopEvent();
14511                           }
14512                           fn.call(scope || window, k, e);
14513                           return;
14514                         }
14515                     }
14516                 }else{
14517                     if(k == keyCode){
14518                         if(this.stopEvent){
14519                            e.stopEvent();
14520                         }
14521                         fn.call(scope || window, k, e);
14522                     }
14523                 }
14524             }
14525         };
14526         this.bindings.push(handler);  
14527         },
14528
14529     /**
14530      * Shorthand for adding a single key listener
14531      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14532      * following options:
14533      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14534      * @param {Function} fn The function to call
14535      * @param {Object} scope (optional) The scope of the function
14536      */
14537     on : function(key, fn, scope){
14538         var keyCode, shift, ctrl, alt;
14539         if(typeof key == "object" && !(key instanceof Array)){
14540             keyCode = key.key;
14541             shift = key.shift;
14542             ctrl = key.ctrl;
14543             alt = key.alt;
14544         }else{
14545             keyCode = key;
14546         }
14547         this.addBinding({
14548             key: keyCode,
14549             shift: shift,
14550             ctrl: ctrl,
14551             alt: alt,
14552             fn: fn,
14553             scope: scope
14554         })
14555     },
14556
14557     // private
14558     handleKeyDown : function(e){
14559             if(this.enabled){ //just in case
14560             var b = this.bindings;
14561             for(var i = 0, len = b.length; i < len; i++){
14562                 b[i].call(this, e);
14563             }
14564             }
14565         },
14566         
14567         /**
14568          * Returns true if this KeyMap is enabled
14569          * @return {Boolean} 
14570          */
14571         isEnabled : function(){
14572             return this.enabled;  
14573         },
14574         
14575         /**
14576          * Enables this KeyMap
14577          */
14578         enable: function(){
14579                 if(!this.enabled){
14580                     this.el.on(this.eventName, this.handleKeyDown, this);
14581                     this.enabled = true;
14582                 }
14583         },
14584
14585         /**
14586          * Disable this KeyMap
14587          */
14588         disable: function(){
14589                 if(this.enabled){
14590                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14591                     this.enabled = false;
14592                 }
14593         }
14594 };/*
14595  * Based on:
14596  * Ext JS Library 1.1.1
14597  * Copyright(c) 2006-2007, Ext JS, LLC.
14598  *
14599  * Originally Released Under LGPL - original licence link has changed is not relivant.
14600  *
14601  * Fork - LGPL
14602  * <script type="text/javascript">
14603  */
14604
14605  
14606 /**
14607  * @class Roo.util.TextMetrics
14608  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14609  * wide, in pixels, a given block of text will be.
14610  * @singleton
14611  */
14612 Roo.util.TextMetrics = function(){
14613     var shared;
14614     return {
14615         /**
14616          * Measures the size of the specified text
14617          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14618          * that can affect the size of the rendered text
14619          * @param {String} text The text to measure
14620          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14621          * in order to accurately measure the text height
14622          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14623          */
14624         measure : function(el, text, fixedWidth){
14625             if(!shared){
14626                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14627             }
14628             shared.bind(el);
14629             shared.setFixedWidth(fixedWidth || 'auto');
14630             return shared.getSize(text);
14631         },
14632
14633         /**
14634          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14635          * the overhead of multiple calls to initialize the style properties on each measurement.
14636          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14637          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14638          * in order to accurately measure the text height
14639          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14640          */
14641         createInstance : function(el, fixedWidth){
14642             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14643         }
14644     };
14645 }();
14646
14647  
14648
14649 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14650     var ml = new Roo.Element(document.createElement('div'));
14651     document.body.appendChild(ml.dom);
14652     ml.position('absolute');
14653     ml.setLeftTop(-1000, -1000);
14654     ml.hide();
14655
14656     if(fixedWidth){
14657         ml.setWidth(fixedWidth);
14658     }
14659      
14660     var instance = {
14661         /**
14662          * Returns the size of the specified text based on the internal element's style and width properties
14663          * @memberOf Roo.util.TextMetrics.Instance#
14664          * @param {String} text The text to measure
14665          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14666          */
14667         getSize : function(text){
14668             ml.update(text);
14669             var s = ml.getSize();
14670             ml.update('');
14671             return s;
14672         },
14673
14674         /**
14675          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14676          * that can affect the size of the rendered text
14677          * @memberOf Roo.util.TextMetrics.Instance#
14678          * @param {String/HTMLElement} el The element, dom node or id
14679          */
14680         bind : function(el){
14681             ml.setStyle(
14682                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14683             );
14684         },
14685
14686         /**
14687          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14688          * to set a fixed width in order to accurately measure the text height.
14689          * @memberOf Roo.util.TextMetrics.Instance#
14690          * @param {Number} width The width to set on the element
14691          */
14692         setFixedWidth : function(width){
14693             ml.setWidth(width);
14694         },
14695
14696         /**
14697          * Returns the measured width of the specified text
14698          * @memberOf Roo.util.TextMetrics.Instance#
14699          * @param {String} text The text to measure
14700          * @return {Number} width The width in pixels
14701          */
14702         getWidth : function(text){
14703             ml.dom.style.width = 'auto';
14704             return this.getSize(text).width;
14705         },
14706
14707         /**
14708          * Returns the measured height of the specified text.  For multiline text, be sure to call
14709          * {@link #setFixedWidth} if necessary.
14710          * @memberOf Roo.util.TextMetrics.Instance#
14711          * @param {String} text The text to measure
14712          * @return {Number} height The height in pixels
14713          */
14714         getHeight : function(text){
14715             return this.getSize(text).height;
14716         }
14717     };
14718
14719     instance.bind(bindTo);
14720
14721     return instance;
14722 };
14723
14724 // backwards compat
14725 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14726  * Based on:
14727  * Ext JS Library 1.1.1
14728  * Copyright(c) 2006-2007, Ext JS, LLC.
14729  *
14730  * Originally Released Under LGPL - original licence link has changed is not relivant.
14731  *
14732  * Fork - LGPL
14733  * <script type="text/javascript">
14734  */
14735
14736 /**
14737  * @class Roo.state.Provider
14738  * Abstract base class for state provider implementations. This class provides methods
14739  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14740  * Provider interface.
14741  */
14742 Roo.state.Provider = function(){
14743     /**
14744      * @event statechange
14745      * Fires when a state change occurs.
14746      * @param {Provider} this This state provider
14747      * @param {String} key The state key which was changed
14748      * @param {String} value The encoded value for the state
14749      */
14750     this.addEvents({
14751         "statechange": true
14752     });
14753     this.state = {};
14754     Roo.state.Provider.superclass.constructor.call(this);
14755 };
14756 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14757     /**
14758      * Returns the current value for a key
14759      * @param {String} name The key name
14760      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14761      * @return {Mixed} The state data
14762      */
14763     get : function(name, defaultValue){
14764         return typeof this.state[name] == "undefined" ?
14765             defaultValue : this.state[name];
14766     },
14767     
14768     /**
14769      * Clears a value from the state
14770      * @param {String} name The key name
14771      */
14772     clear : function(name){
14773         delete this.state[name];
14774         this.fireEvent("statechange", this, name, null);
14775     },
14776     
14777     /**
14778      * Sets the value for a key
14779      * @param {String} name The key name
14780      * @param {Mixed} value The value to set
14781      */
14782     set : function(name, value){
14783         this.state[name] = value;
14784         this.fireEvent("statechange", this, name, value);
14785     },
14786     
14787     /**
14788      * Decodes a string previously encoded with {@link #encodeValue}.
14789      * @param {String} value The value to decode
14790      * @return {Mixed} The decoded value
14791      */
14792     decodeValue : function(cookie){
14793         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14794         var matches = re.exec(unescape(cookie));
14795         if(!matches || !matches[1]) return; // non state cookie
14796         var type = matches[1];
14797         var v = matches[2];
14798         switch(type){
14799             case "n":
14800                 return parseFloat(v);
14801             case "d":
14802                 return new Date(Date.parse(v));
14803             case "b":
14804                 return (v == "1");
14805             case "a":
14806                 var all = [];
14807                 var values = v.split("^");
14808                 for(var i = 0, len = values.length; i < len; i++){
14809                     all.push(this.decodeValue(values[i]));
14810                 }
14811                 return all;
14812            case "o":
14813                 var all = {};
14814                 var values = v.split("^");
14815                 for(var i = 0, len = values.length; i < len; i++){
14816                     var kv = values[i].split("=");
14817                     all[kv[0]] = this.decodeValue(kv[1]);
14818                 }
14819                 return all;
14820            default:
14821                 return v;
14822         }
14823     },
14824     
14825     /**
14826      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14827      * @param {Mixed} value The value to encode
14828      * @return {String} The encoded value
14829      */
14830     encodeValue : function(v){
14831         var enc;
14832         if(typeof v == "number"){
14833             enc = "n:" + v;
14834         }else if(typeof v == "boolean"){
14835             enc = "b:" + (v ? "1" : "0");
14836         }else if(v instanceof Date){
14837             enc = "d:" + v.toGMTString();
14838         }else if(v instanceof Array){
14839             var flat = "";
14840             for(var i = 0, len = v.length; i < len; i++){
14841                 flat += this.encodeValue(v[i]);
14842                 if(i != len-1) flat += "^";
14843             }
14844             enc = "a:" + flat;
14845         }else if(typeof v == "object"){
14846             var flat = "";
14847             for(var key in v){
14848                 if(typeof v[key] != "function"){
14849                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14850                 }
14851             }
14852             enc = "o:" + flat.substring(0, flat.length-1);
14853         }else{
14854             enc = "s:" + v;
14855         }
14856         return escape(enc);        
14857     }
14858 });
14859
14860 /*
14861  * Based on:
14862  * Ext JS Library 1.1.1
14863  * Copyright(c) 2006-2007, Ext JS, LLC.
14864  *
14865  * Originally Released Under LGPL - original licence link has changed is not relivant.
14866  *
14867  * Fork - LGPL
14868  * <script type="text/javascript">
14869  */
14870 /**
14871  * @class Roo.state.Manager
14872  * This is the global state manager. By default all components that are "state aware" check this class
14873  * for state information if you don't pass them a custom state provider. In order for this class
14874  * to be useful, it must be initialized with a provider when your application initializes.
14875  <pre><code>
14876 // in your initialization function
14877 init : function(){
14878    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14879    ...
14880    // supposed you have a {@link Roo.BorderLayout}
14881    var layout = new Roo.BorderLayout(...);
14882    layout.restoreState();
14883    // or a {Roo.BasicDialog}
14884    var dialog = new Roo.BasicDialog(...);
14885    dialog.restoreState();
14886  </code></pre>
14887  * @singleton
14888  */
14889 Roo.state.Manager = function(){
14890     var provider = new Roo.state.Provider();
14891     
14892     return {
14893         /**
14894          * Configures the default state provider for your application
14895          * @param {Provider} stateProvider The state provider to set
14896          */
14897         setProvider : function(stateProvider){
14898             provider = stateProvider;
14899         },
14900         
14901         /**
14902          * Returns the current value for a key
14903          * @param {String} name The key name
14904          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14905          * @return {Mixed} The state data
14906          */
14907         get : function(key, defaultValue){
14908             return provider.get(key, defaultValue);
14909         },
14910         
14911         /**
14912          * Sets the value for a key
14913          * @param {String} name The key name
14914          * @param {Mixed} value The state data
14915          */
14916          set : function(key, value){
14917             provider.set(key, value);
14918         },
14919         
14920         /**
14921          * Clears a value from the state
14922          * @param {String} name The key name
14923          */
14924         clear : function(key){
14925             provider.clear(key);
14926         },
14927         
14928         /**
14929          * Gets the currently configured state provider
14930          * @return {Provider} The state provider
14931          */
14932         getProvider : function(){
14933             return provider;
14934         }
14935     };
14936 }();
14937 /*
14938  * Based on:
14939  * Ext JS Library 1.1.1
14940  * Copyright(c) 2006-2007, Ext JS, LLC.
14941  *
14942  * Originally Released Under LGPL - original licence link has changed is not relivant.
14943  *
14944  * Fork - LGPL
14945  * <script type="text/javascript">
14946  */
14947 /**
14948  * @class Roo.state.CookieProvider
14949  * @extends Roo.state.Provider
14950  * The default Provider implementation which saves state via cookies.
14951  * <br />Usage:
14952  <pre><code>
14953    var cp = new Roo.state.CookieProvider({
14954        path: "/cgi-bin/",
14955        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14956        domain: "roojs.com"
14957    })
14958    Roo.state.Manager.setProvider(cp);
14959  </code></pre>
14960  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14961  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14962  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14963  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14964  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14965  * domain the page is running on including the 'www' like 'www.roojs.com')
14966  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14967  * @constructor
14968  * Create a new CookieProvider
14969  * @param {Object} config The configuration object
14970  */
14971 Roo.state.CookieProvider = function(config){
14972     Roo.state.CookieProvider.superclass.constructor.call(this);
14973     this.path = "/";
14974     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14975     this.domain = null;
14976     this.secure = false;
14977     Roo.apply(this, config);
14978     this.state = this.readCookies();
14979 };
14980
14981 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14982     // private
14983     set : function(name, value){
14984         if(typeof value == "undefined" || value === null){
14985             this.clear(name);
14986             return;
14987         }
14988         this.setCookie(name, value);
14989         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14990     },
14991
14992     // private
14993     clear : function(name){
14994         this.clearCookie(name);
14995         Roo.state.CookieProvider.superclass.clear.call(this, name);
14996     },
14997
14998     // private
14999     readCookies : function(){
15000         var cookies = {};
15001         var c = document.cookie + ";";
15002         var re = /\s?(.*?)=(.*?);/g;
15003         var matches;
15004         while((matches = re.exec(c)) != null){
15005             var name = matches[1];
15006             var value = matches[2];
15007             if(name && name.substring(0,3) == "ys-"){
15008                 cookies[name.substr(3)] = this.decodeValue(value);
15009             }
15010         }
15011         return cookies;
15012     },
15013
15014     // private
15015     setCookie : function(name, value){
15016         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15017            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15018            ((this.path == null) ? "" : ("; path=" + this.path)) +
15019            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15020            ((this.secure == true) ? "; secure" : "");
15021     },
15022
15023     // private
15024     clearCookie : function(name){
15025         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15026            ((this.path == null) ? "" : ("; path=" + this.path)) +
15027            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15028            ((this.secure == true) ? "; secure" : "");
15029     }
15030 });/*
15031  * Based on:
15032  * Ext JS Library 1.1.1
15033  * Copyright(c) 2006-2007, Ext JS, LLC.
15034  *
15035  * Originally Released Under LGPL - original licence link has changed is not relivant.
15036  *
15037  * Fork - LGPL
15038  * <script type="text/javascript">
15039  */
15040  
15041
15042 /**
15043  * @class Roo.ComponentMgr
15044  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15045  * @singleton
15046  */
15047 Roo.ComponentMgr = function(){
15048     var all = new Roo.util.MixedCollection();
15049
15050     return {
15051         /**
15052          * Registers a component.
15053          * @param {Roo.Component} c The component
15054          */
15055         register : function(c){
15056             all.add(c);
15057         },
15058
15059         /**
15060          * Unregisters a component.
15061          * @param {Roo.Component} c The component
15062          */
15063         unregister : function(c){
15064             all.remove(c);
15065         },
15066
15067         /**
15068          * Returns a component by id
15069          * @param {String} id The component id
15070          */
15071         get : function(id){
15072             return all.get(id);
15073         },
15074
15075         /**
15076          * Registers a function that will be called when a specified component is added to ComponentMgr
15077          * @param {String} id The component id
15078          * @param {Funtction} fn The callback function
15079          * @param {Object} scope The scope of the callback
15080          */
15081         onAvailable : function(id, fn, scope){
15082             all.on("add", function(index, o){
15083                 if(o.id == id){
15084                     fn.call(scope || o, o);
15085                     all.un("add", fn, scope);
15086                 }
15087             });
15088         }
15089     };
15090 }();/*
15091  * Based on:
15092  * Ext JS Library 1.1.1
15093  * Copyright(c) 2006-2007, Ext JS, LLC.
15094  *
15095  * Originally Released Under LGPL - original licence link has changed is not relivant.
15096  *
15097  * Fork - LGPL
15098  * <script type="text/javascript">
15099  */
15100  
15101 /**
15102  * @class Roo.Component
15103  * @extends Roo.util.Observable
15104  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15105  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15106  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15107  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15108  * All visual components (widgets) that require rendering into a layout should subclass Component.
15109  * @constructor
15110  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15111  * 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
15112  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15113  */
15114 Roo.Component = function(config){
15115     config = config || {};
15116     if(config.tagName || config.dom || typeof config == "string"){ // element object
15117         config = {el: config, id: config.id || config};
15118     }
15119     this.initialConfig = config;
15120
15121     Roo.apply(this, config);
15122     this.addEvents({
15123         /**
15124          * @event disable
15125          * Fires after the component is disabled.
15126              * @param {Roo.Component} this
15127              */
15128         disable : true,
15129         /**
15130          * @event enable
15131          * Fires after the component is enabled.
15132              * @param {Roo.Component} this
15133              */
15134         enable : true,
15135         /**
15136          * @event beforeshow
15137          * Fires before the component is shown.  Return false to stop the show.
15138              * @param {Roo.Component} this
15139              */
15140         beforeshow : true,
15141         /**
15142          * @event show
15143          * Fires after the component is shown.
15144              * @param {Roo.Component} this
15145              */
15146         show : true,
15147         /**
15148          * @event beforehide
15149          * Fires before the component is hidden. Return false to stop the hide.
15150              * @param {Roo.Component} this
15151              */
15152         beforehide : true,
15153         /**
15154          * @event hide
15155          * Fires after the component is hidden.
15156              * @param {Roo.Component} this
15157              */
15158         hide : true,
15159         /**
15160          * @event beforerender
15161          * Fires before the component is rendered. Return false to stop the render.
15162              * @param {Roo.Component} this
15163              */
15164         beforerender : true,
15165         /**
15166          * @event render
15167          * Fires after the component is rendered.
15168              * @param {Roo.Component} this
15169              */
15170         render : true,
15171         /**
15172          * @event beforedestroy
15173          * Fires before the component is destroyed. Return false to stop the destroy.
15174              * @param {Roo.Component} this
15175              */
15176         beforedestroy : true,
15177         /**
15178          * @event destroy
15179          * Fires after the component is destroyed.
15180              * @param {Roo.Component} this
15181              */
15182         destroy : true
15183     });
15184     if(!this.id){
15185         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15186     }
15187     Roo.ComponentMgr.register(this);
15188     Roo.Component.superclass.constructor.call(this);
15189     this.initComponent();
15190     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15191         this.render(this.renderTo);
15192         delete this.renderTo;
15193     }
15194 };
15195
15196 /** @private */
15197 Roo.Component.AUTO_ID = 1000;
15198
15199 Roo.extend(Roo.Component, Roo.util.Observable, {
15200     /**
15201      * @scope Roo.Component.prototype
15202      * @type {Boolean}
15203      * true if this component is hidden. Read-only.
15204      */
15205     hidden : false,
15206     /**
15207      * @type {Boolean}
15208      * true if this component is disabled. Read-only.
15209      */
15210     disabled : false,
15211     /**
15212      * @type {Boolean}
15213      * true if this component has been rendered. Read-only.
15214      */
15215     rendered : false,
15216     
15217     /** @cfg {String} disableClass
15218      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15219      */
15220     disabledClass : "x-item-disabled",
15221         /** @cfg {Boolean} allowDomMove
15222          * Whether the component can move the Dom node when rendering (defaults to true).
15223          */
15224     allowDomMove : true,
15225     /** @cfg {String} hideMode (display|visibility)
15226      * How this component should hidden. Supported values are
15227      * "visibility" (css visibility), "offsets" (negative offset position) and
15228      * "display" (css display) - defaults to "display".
15229      */
15230     hideMode: 'display',
15231
15232     /** @private */
15233     ctype : "Roo.Component",
15234
15235     /**
15236      * @cfg {String} actionMode 
15237      * which property holds the element that used for  hide() / show() / disable() / enable()
15238      * default is 'el' 
15239      */
15240     actionMode : "el",
15241
15242     /** @private */
15243     getActionEl : function(){
15244         return this[this.actionMode];
15245     },
15246
15247     initComponent : Roo.emptyFn,
15248     /**
15249      * If this is a lazy rendering component, render it to its container element.
15250      * @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.
15251      */
15252     render : function(container, position){
15253         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15254             if(!container && this.el){
15255                 this.el = Roo.get(this.el);
15256                 container = this.el.dom.parentNode;
15257                 this.allowDomMove = false;
15258             }
15259             this.container = Roo.get(container);
15260             this.rendered = true;
15261             if(position !== undefined){
15262                 if(typeof position == 'number'){
15263                     position = this.container.dom.childNodes[position];
15264                 }else{
15265                     position = Roo.getDom(position);
15266                 }
15267             }
15268             this.onRender(this.container, position || null);
15269             if(this.cls){
15270                 this.el.addClass(this.cls);
15271                 delete this.cls;
15272             }
15273             if(this.style){
15274                 this.el.applyStyles(this.style);
15275                 delete this.style;
15276             }
15277             this.fireEvent("render", this);
15278             this.afterRender(this.container);
15279             if(this.hidden){
15280                 this.hide();
15281             }
15282             if(this.disabled){
15283                 this.disable();
15284             }
15285         }
15286         return this;
15287     },
15288
15289     /** @private */
15290     // default function is not really useful
15291     onRender : function(ct, position){
15292         if(this.el){
15293             this.el = Roo.get(this.el);
15294             if(this.allowDomMove !== false){
15295                 ct.dom.insertBefore(this.el.dom, position);
15296             }
15297         }
15298     },
15299
15300     /** @private */
15301     getAutoCreate : function(){
15302         var cfg = typeof this.autoCreate == "object" ?
15303                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15304         if(this.id && !cfg.id){
15305             cfg.id = this.id;
15306         }
15307         return cfg;
15308     },
15309
15310     /** @private */
15311     afterRender : Roo.emptyFn,
15312
15313     /**
15314      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15315      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15316      */
15317     destroy : function(){
15318         if(this.fireEvent("beforedestroy", this) !== false){
15319             this.purgeListeners();
15320             this.beforeDestroy();
15321             if(this.rendered){
15322                 this.el.removeAllListeners();
15323                 this.el.remove();
15324                 if(this.actionMode == "container"){
15325                     this.container.remove();
15326                 }
15327             }
15328             this.onDestroy();
15329             Roo.ComponentMgr.unregister(this);
15330             this.fireEvent("destroy", this);
15331         }
15332     },
15333
15334         /** @private */
15335     beforeDestroy : function(){
15336
15337     },
15338
15339         /** @private */
15340         onDestroy : function(){
15341
15342     },
15343
15344     /**
15345      * Returns the underlying {@link Roo.Element}.
15346      * @return {Roo.Element} The element
15347      */
15348     getEl : function(){
15349         return this.el;
15350     },
15351
15352     /**
15353      * Returns the id of this component.
15354      * @return {String}
15355      */
15356     getId : function(){
15357         return this.id;
15358     },
15359
15360     /**
15361      * Try to focus this component.
15362      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15363      * @return {Roo.Component} this
15364      */
15365     focus : function(selectText){
15366         if(this.rendered){
15367             this.el.focus();
15368             if(selectText === true){
15369                 this.el.dom.select();
15370             }
15371         }
15372         return this;
15373     },
15374
15375     /** @private */
15376     blur : function(){
15377         if(this.rendered){
15378             this.el.blur();
15379         }
15380         return this;
15381     },
15382
15383     /**
15384      * Disable this component.
15385      * @return {Roo.Component} this
15386      */
15387     disable : function(){
15388         if(this.rendered){
15389             this.onDisable();
15390         }
15391         this.disabled = true;
15392         this.fireEvent("disable", this);
15393         return this;
15394     },
15395
15396         // private
15397     onDisable : function(){
15398         this.getActionEl().addClass(this.disabledClass);
15399         this.el.dom.disabled = true;
15400     },
15401
15402     /**
15403      * Enable this component.
15404      * @return {Roo.Component} this
15405      */
15406     enable : function(){
15407         if(this.rendered){
15408             this.onEnable();
15409         }
15410         this.disabled = false;
15411         this.fireEvent("enable", this);
15412         return this;
15413     },
15414
15415         // private
15416     onEnable : function(){
15417         this.getActionEl().removeClass(this.disabledClass);
15418         this.el.dom.disabled = false;
15419     },
15420
15421     /**
15422      * Convenience function for setting disabled/enabled by boolean.
15423      * @param {Boolean} disabled
15424      */
15425     setDisabled : function(disabled){
15426         this[disabled ? "disable" : "enable"]();
15427     },
15428
15429     /**
15430      * Show this component.
15431      * @return {Roo.Component} this
15432      */
15433     show: function(){
15434         if(this.fireEvent("beforeshow", this) !== false){
15435             this.hidden = false;
15436             if(this.rendered){
15437                 this.onShow();
15438             }
15439             this.fireEvent("show", this);
15440         }
15441         return this;
15442     },
15443
15444     // private
15445     onShow : function(){
15446         var ae = this.getActionEl();
15447         if(this.hideMode == 'visibility'){
15448             ae.dom.style.visibility = "visible";
15449         }else if(this.hideMode == 'offsets'){
15450             ae.removeClass('x-hidden');
15451         }else{
15452             ae.dom.style.display = "";
15453         }
15454     },
15455
15456     /**
15457      * Hide this component.
15458      * @return {Roo.Component} this
15459      */
15460     hide: function(){
15461         if(this.fireEvent("beforehide", this) !== false){
15462             this.hidden = true;
15463             if(this.rendered){
15464                 this.onHide();
15465             }
15466             this.fireEvent("hide", this);
15467         }
15468         return this;
15469     },
15470
15471     // private
15472     onHide : function(){
15473         var ae = this.getActionEl();
15474         if(this.hideMode == 'visibility'){
15475             ae.dom.style.visibility = "hidden";
15476         }else if(this.hideMode == 'offsets'){
15477             ae.addClass('x-hidden');
15478         }else{
15479             ae.dom.style.display = "none";
15480         }
15481     },
15482
15483     /**
15484      * Convenience function to hide or show this component by boolean.
15485      * @param {Boolean} visible True to show, false to hide
15486      * @return {Roo.Component} this
15487      */
15488     setVisible: function(visible){
15489         if(visible) {
15490             this.show();
15491         }else{
15492             this.hide();
15493         }
15494         return this;
15495     },
15496
15497     /**
15498      * Returns true if this component is visible.
15499      */
15500     isVisible : function(){
15501         return this.getActionEl().isVisible();
15502     },
15503
15504     cloneConfig : function(overrides){
15505         overrides = overrides || {};
15506         var id = overrides.id || Roo.id();
15507         var cfg = Roo.applyIf(overrides, this.initialConfig);
15508         cfg.id = id; // prevent dup id
15509         return new this.constructor(cfg);
15510     }
15511 });/*
15512  * Based on:
15513  * Ext JS Library 1.1.1
15514  * Copyright(c) 2006-2007, Ext JS, LLC.
15515  *
15516  * Originally Released Under LGPL - original licence link has changed is not relivant.
15517  *
15518  * Fork - LGPL
15519  * <script type="text/javascript">
15520  */
15521
15522 /**
15523  * @class Roo.BoxComponent
15524  * @extends Roo.Component
15525  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15526  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15527  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15528  * layout containers.
15529  * @constructor
15530  * @param {Roo.Element/String/Object} config The configuration options.
15531  */
15532 Roo.BoxComponent = function(config){
15533     Roo.Component.call(this, config);
15534     this.addEvents({
15535         /**
15536          * @event resize
15537          * Fires after the component is resized.
15538              * @param {Roo.Component} this
15539              * @param {Number} adjWidth The box-adjusted width that was set
15540              * @param {Number} adjHeight The box-adjusted height that was set
15541              * @param {Number} rawWidth The width that was originally specified
15542              * @param {Number} rawHeight The height that was originally specified
15543              */
15544         resize : true,
15545         /**
15546          * @event move
15547          * Fires after the component is moved.
15548              * @param {Roo.Component} this
15549              * @param {Number} x The new x position
15550              * @param {Number} y The new y position
15551              */
15552         move : true
15553     });
15554 };
15555
15556 Roo.extend(Roo.BoxComponent, Roo.Component, {
15557     // private, set in afterRender to signify that the component has been rendered
15558     boxReady : false,
15559     // private, used to defer height settings to subclasses
15560     deferHeight: false,
15561     /** @cfg {Number} width
15562      * width (optional) size of component
15563      */
15564      /** @cfg {Number} height
15565      * height (optional) size of component
15566      */
15567      
15568     /**
15569      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15570      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15571      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15572      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15573      * @return {Roo.BoxComponent} this
15574      */
15575     setSize : function(w, h){
15576         // support for standard size objects
15577         if(typeof w == 'object'){
15578             h = w.height;
15579             w = w.width;
15580         }
15581         // not rendered
15582         if(!this.boxReady){
15583             this.width = w;
15584             this.height = h;
15585             return this;
15586         }
15587
15588         // prevent recalcs when not needed
15589         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15590             return this;
15591         }
15592         this.lastSize = {width: w, height: h};
15593
15594         var adj = this.adjustSize(w, h);
15595         var aw = adj.width, ah = adj.height;
15596         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15597             var rz = this.getResizeEl();
15598             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15599                 rz.setSize(aw, ah);
15600             }else if(!this.deferHeight && ah !== undefined){
15601                 rz.setHeight(ah);
15602             }else if(aw !== undefined){
15603                 rz.setWidth(aw);
15604             }
15605             this.onResize(aw, ah, w, h);
15606             this.fireEvent('resize', this, aw, ah, w, h);
15607         }
15608         return this;
15609     },
15610
15611     /**
15612      * Gets the current size of the component's underlying element.
15613      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15614      */
15615     getSize : function(){
15616         return this.el.getSize();
15617     },
15618
15619     /**
15620      * Gets the current XY position of the component's underlying element.
15621      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15622      * @return {Array} The XY position of the element (e.g., [100, 200])
15623      */
15624     getPosition : function(local){
15625         if(local === true){
15626             return [this.el.getLeft(true), this.el.getTop(true)];
15627         }
15628         return this.xy || this.el.getXY();
15629     },
15630
15631     /**
15632      * Gets the current box measurements of the component's underlying element.
15633      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15634      * @returns {Object} box An object in the format {x, y, width, height}
15635      */
15636     getBox : function(local){
15637         var s = this.el.getSize();
15638         if(local){
15639             s.x = this.el.getLeft(true);
15640             s.y = this.el.getTop(true);
15641         }else{
15642             var xy = this.xy || this.el.getXY();
15643             s.x = xy[0];
15644             s.y = xy[1];
15645         }
15646         return s;
15647     },
15648
15649     /**
15650      * Sets the current box measurements of the component's underlying element.
15651      * @param {Object} box An object in the format {x, y, width, height}
15652      * @returns {Roo.BoxComponent} this
15653      */
15654     updateBox : function(box){
15655         this.setSize(box.width, box.height);
15656         this.setPagePosition(box.x, box.y);
15657         return this;
15658     },
15659
15660     // protected
15661     getResizeEl : function(){
15662         return this.resizeEl || this.el;
15663     },
15664
15665     // protected
15666     getPositionEl : function(){
15667         return this.positionEl || this.el;
15668     },
15669
15670     /**
15671      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15672      * This method fires the move event.
15673      * @param {Number} left The new left
15674      * @param {Number} top The new top
15675      * @returns {Roo.BoxComponent} this
15676      */
15677     setPosition : function(x, y){
15678         this.x = x;
15679         this.y = y;
15680         if(!this.boxReady){
15681             return this;
15682         }
15683         var adj = this.adjustPosition(x, y);
15684         var ax = adj.x, ay = adj.y;
15685
15686         var el = this.getPositionEl();
15687         if(ax !== undefined || ay !== undefined){
15688             if(ax !== undefined && ay !== undefined){
15689                 el.setLeftTop(ax, ay);
15690             }else if(ax !== undefined){
15691                 el.setLeft(ax);
15692             }else if(ay !== undefined){
15693                 el.setTop(ay);
15694             }
15695             this.onPosition(ax, ay);
15696             this.fireEvent('move', this, ax, ay);
15697         }
15698         return this;
15699     },
15700
15701     /**
15702      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15703      * This method fires the move event.
15704      * @param {Number} x The new x position
15705      * @param {Number} y The new y position
15706      * @returns {Roo.BoxComponent} this
15707      */
15708     setPagePosition : function(x, y){
15709         this.pageX = x;
15710         this.pageY = y;
15711         if(!this.boxReady){
15712             return;
15713         }
15714         if(x === undefined || y === undefined){ // cannot translate undefined points
15715             return;
15716         }
15717         var p = this.el.translatePoints(x, y);
15718         this.setPosition(p.left, p.top);
15719         return this;
15720     },
15721
15722     // private
15723     onRender : function(ct, position){
15724         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15725         if(this.resizeEl){
15726             this.resizeEl = Roo.get(this.resizeEl);
15727         }
15728         if(this.positionEl){
15729             this.positionEl = Roo.get(this.positionEl);
15730         }
15731     },
15732
15733     // private
15734     afterRender : function(){
15735         Roo.BoxComponent.superclass.afterRender.call(this);
15736         this.boxReady = true;
15737         this.setSize(this.width, this.height);
15738         if(this.x || this.y){
15739             this.setPosition(this.x, this.y);
15740         }
15741         if(this.pageX || this.pageY){
15742             this.setPagePosition(this.pageX, this.pageY);
15743         }
15744     },
15745
15746     /**
15747      * Force the component's size to recalculate based on the underlying element's current height and width.
15748      * @returns {Roo.BoxComponent} this
15749      */
15750     syncSize : function(){
15751         delete this.lastSize;
15752         this.setSize(this.el.getWidth(), this.el.getHeight());
15753         return this;
15754     },
15755
15756     /**
15757      * Called after the component is resized, this method is empty by default but can be implemented by any
15758      * subclass that needs to perform custom logic after a resize occurs.
15759      * @param {Number} adjWidth The box-adjusted width that was set
15760      * @param {Number} adjHeight The box-adjusted height that was set
15761      * @param {Number} rawWidth The width that was originally specified
15762      * @param {Number} rawHeight The height that was originally specified
15763      */
15764     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15765
15766     },
15767
15768     /**
15769      * Called after the component is moved, this method is empty by default but can be implemented by any
15770      * subclass that needs to perform custom logic after a move occurs.
15771      * @param {Number} x The new x position
15772      * @param {Number} y The new y position
15773      */
15774     onPosition : function(x, y){
15775
15776     },
15777
15778     // private
15779     adjustSize : function(w, h){
15780         if(this.autoWidth){
15781             w = 'auto';
15782         }
15783         if(this.autoHeight){
15784             h = 'auto';
15785         }
15786         return {width : w, height: h};
15787     },
15788
15789     // private
15790     adjustPosition : function(x, y){
15791         return {x : x, y: y};
15792     }
15793 });/*
15794  * Original code for Roojs - LGPL
15795  * <script type="text/javascript">
15796  */
15797  
15798 /**
15799  * @class Roo.XComponent
15800  * A delayed Element creator...
15801  * Or a way to group chunks of interface together.
15802  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15803  *  used in conjunction with XComponent.build() it will create an instance of each element,
15804  *  then call addxtype() to build the User interface.
15805  * 
15806  * Mypart.xyx = new Roo.XComponent({
15807
15808     parent : 'Mypart.xyz', // empty == document.element.!!
15809     order : '001',
15810     name : 'xxxx'
15811     region : 'xxxx'
15812     disabled : function() {} 
15813      
15814     tree : function() { // return an tree of xtype declared components
15815         var MODULE = this;
15816         return 
15817         {
15818             xtype : 'NestedLayoutPanel',
15819             // technicall
15820         }
15821      ]
15822  *})
15823  *
15824  *
15825  * It can be used to build a big heiracy, with parent etc.
15826  * or you can just use this to render a single compoent to a dom element
15827  * MYPART.render(Roo.Element | String(id) | dom_element )
15828  *
15829  *
15830  * Usage patterns.
15831  *
15832  * Classic Roo
15833  *
15834  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15835  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15836  *
15837  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15838  *
15839  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15840  * - if mulitple topModules exist, the last one is defined as the top module.
15841  *
15842  * Embeded Roo
15843  * 
15844  * When the top level or multiple modules are to embedded into a existing HTML page,
15845  * the parent element can container '#id' of the element where the module will be drawn.
15846  *
15847  * Bootstrap Roo
15848  *
15849  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15850  * it relies more on a include mechanism, where sub modules are included into an outer page.
15851  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15852  * 
15853  * Bootstrap Roo Included elements
15854  *
15855  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15856  * hence confusing the component builder as it thinks there are multiple top level elements. 
15857  *
15858  * 
15859  * 
15860  * @extends Roo.util.Observable
15861  * @constructor
15862  * @param cfg {Object} configuration of component
15863  * 
15864  */
15865 Roo.XComponent = function(cfg) {
15866     Roo.apply(this, cfg);
15867     this.addEvents({ 
15868         /**
15869              * @event built
15870              * Fires when this the componnt is built
15871              * @param {Roo.XComponent} c the component
15872              */
15873         'built' : true
15874         
15875     });
15876     this.region = this.region || 'center'; // default..
15877     Roo.XComponent.register(this);
15878     this.modules = false;
15879     this.el = false; // where the layout goes..
15880     
15881     
15882 }
15883 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15884     /**
15885      * @property el
15886      * The created element (with Roo.factory())
15887      * @type {Roo.Layout}
15888      */
15889     el  : false,
15890     
15891     /**
15892      * @property el
15893      * for BC  - use el in new code
15894      * @type {Roo.Layout}
15895      */
15896     panel : false,
15897     
15898     /**
15899      * @property layout
15900      * for BC  - use el in new code
15901      * @type {Roo.Layout}
15902      */
15903     layout : false,
15904     
15905      /**
15906      * @cfg {Function|boolean} disabled
15907      * If this module is disabled by some rule, return true from the funtion
15908      */
15909     disabled : false,
15910     
15911     /**
15912      * @cfg {String} parent 
15913      * Name of parent element which it get xtype added to..
15914      */
15915     parent: false,
15916     
15917     /**
15918      * @cfg {String} order
15919      * Used to set the order in which elements are created (usefull for multiple tabs)
15920      */
15921     
15922     order : false,
15923     /**
15924      * @cfg {String} name
15925      * String to display while loading.
15926      */
15927     name : false,
15928     /**
15929      * @cfg {String} region
15930      * Region to render component to (defaults to center)
15931      */
15932     region : 'center',
15933     
15934     /**
15935      * @cfg {Array} items
15936      * A single item array - the first element is the root of the tree..
15937      * It's done this way to stay compatible with the Xtype system...
15938      */
15939     items : false,
15940     
15941     /**
15942      * @property _tree
15943      * The method that retuns the tree of parts that make up this compoennt 
15944      * @type {function}
15945      */
15946     _tree  : false,
15947     
15948      /**
15949      * render
15950      * render element to dom or tree
15951      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15952      */
15953     
15954     render : function(el)
15955     {
15956         
15957         el = el || false;
15958         var hp = this.parent ? 1 : 0;
15959         Roo.debug &&  Roo.log(this);
15960         
15961         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15962             // if parent is a '#.....' string, then let's use that..
15963             var ename = this.parent.substr(1);
15964             this.parent = false;
15965             Roo.debug && Roo.log(ename);
15966             switch (ename) {
15967                 case 'bootstrap-body' :
15968                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15969                         this.parent = { el :  new  Roo.bootstrap.Body() };
15970                         Roo.debug && Roo.log("setting el to doc body");
15971                          
15972                     } else {
15973                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15974                     }
15975                     break;
15976                 case 'bootstrap':
15977                     this.parent = { el : true};
15978                     // fall through
15979                 default:
15980                     el = Roo.get(ename);
15981                     break;
15982             }
15983                 
15984             
15985             if (!el && !this.parent) {
15986                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
15987                 return;
15988             }
15989         }
15990         Roo.debug && Roo.log("EL:");
15991         Roo.debug && Roo.log(el);
15992         Roo.debug && Roo.log("this.parent.el:");
15993         Roo.debug && Roo.log(this.parent.el);
15994         
15995         var tree = this._tree ? this._tree() : this.tree();
15996
15997         // altertive root elements ??? - we need a better way to indicate these.
15998         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15999                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16000         
16001         if (!this.parent && is_alt) {
16002             //el = Roo.get(document.body);
16003             this.parent = { el : true };
16004         }
16005             
16006             
16007         
16008         if (!this.parent) {
16009             
16010             Roo.debug && Roo.log("no parent - creating one");
16011             
16012             el = el ? Roo.get(el) : false;      
16013             
16014             // it's a top level one..
16015             this.parent =  {
16016                 el : new Roo.BorderLayout(el || document.body, {
16017                 
16018                      center: {
16019                          titlebar: false,
16020                          autoScroll:false,
16021                          closeOnTab: true,
16022                          tabPosition: 'top',
16023                           //resizeTabs: true,
16024                          alwaysShowTabs: el && hp? false :  true,
16025                          hideTabs: el || !hp ? true :  false,
16026                          minTabWidth: 140
16027                      }
16028                  })
16029             }
16030         }
16031         
16032         if (!this.parent.el) {
16033                 // probably an old style ctor, which has been disabled.
16034                 return;
16035
16036         }
16037                 // The 'tree' method is  '_tree now' 
16038             
16039         tree.region = tree.region || this.region;
16040         
16041         if (this.parent.el === true) {
16042             // bootstrap... - body..
16043             this.parent.el = Roo.factory(tree);
16044         }
16045         
16046         this.el = this.parent.el.addxtype(tree);
16047         this.fireEvent('built', this);
16048         
16049         this.panel = this.el;
16050         this.layout = this.panel.layout;
16051         this.parentLayout = this.parent.layout  || false;  
16052          
16053     }
16054     
16055 });
16056
16057 Roo.apply(Roo.XComponent, {
16058     /**
16059      * @property  hideProgress
16060      * true to disable the building progress bar.. usefull on single page renders.
16061      * @type Boolean
16062      */
16063     hideProgress : false,
16064     /**
16065      * @property  buildCompleted
16066      * True when the builder has completed building the interface.
16067      * @type Boolean
16068      */
16069     buildCompleted : false,
16070      
16071     /**
16072      * @property  topModule
16073      * the upper most module - uses document.element as it's constructor.
16074      * @type Object
16075      */
16076      
16077     topModule  : false,
16078       
16079     /**
16080      * @property  modules
16081      * array of modules to be created by registration system.
16082      * @type {Array} of Roo.XComponent
16083      */
16084     
16085     modules : [],
16086     /**
16087      * @property  elmodules
16088      * array of modules to be created by which use #ID 
16089      * @type {Array} of Roo.XComponent
16090      */
16091      
16092     elmodules : [],
16093
16094      /**
16095      * @property  build_from_html
16096      * Build elements from html - used by bootstrap HTML stuff 
16097      *    - this is cleared after build is completed
16098      * @type {boolean} true  (default false)
16099      */
16100      
16101     build_from_html : false,
16102
16103     /**
16104      * Register components to be built later.
16105      *
16106      * This solves the following issues
16107      * - Building is not done on page load, but after an authentication process has occured.
16108      * - Interface elements are registered on page load
16109      * - Parent Interface elements may not be loaded before child, so this handles that..
16110      * 
16111      *
16112      * example:
16113      * 
16114      * MyApp.register({
16115           order : '000001',
16116           module : 'Pman.Tab.projectMgr',
16117           region : 'center',
16118           parent : 'Pman.layout',
16119           disabled : false,  // or use a function..
16120         })
16121      
16122      * * @param {Object} details about module
16123      */
16124     register : function(obj) {
16125                 
16126         Roo.XComponent.event.fireEvent('register', obj);
16127         switch(typeof(obj.disabled) ) {
16128                 
16129             case 'undefined':
16130                 break;
16131             
16132             case 'function':
16133                 if ( obj.disabled() ) {
16134                         return;
16135                 }
16136                 break;
16137             
16138             default:
16139                 if (obj.disabled) {
16140                         return;
16141                 }
16142                 break;
16143         }
16144                 
16145         this.modules.push(obj);
16146          
16147     },
16148     /**
16149      * convert a string to an object..
16150      * eg. 'AAA.BBB' -> finds AAA.BBB
16151
16152      */
16153     
16154     toObject : function(str)
16155     {
16156         if (!str || typeof(str) == 'object') {
16157             return str;
16158         }
16159         if (str.substring(0,1) == '#') {
16160             return str;
16161         }
16162
16163         var ar = str.split('.');
16164         var rt, o;
16165         rt = ar.shift();
16166             /** eval:var:o */
16167         try {
16168             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16169         } catch (e) {
16170             throw "Module not found : " + str;
16171         }
16172         
16173         if (o === false) {
16174             throw "Module not found : " + str;
16175         }
16176         Roo.each(ar, function(e) {
16177             if (typeof(o[e]) == 'undefined') {
16178                 throw "Module not found : " + str;
16179             }
16180             o = o[e];
16181         });
16182         
16183         return o;
16184         
16185     },
16186     
16187     
16188     /**
16189      * move modules into their correct place in the tree..
16190      * 
16191      */
16192     preBuild : function ()
16193     {
16194         var _t = this;
16195         Roo.each(this.modules , function (obj)
16196         {
16197             Roo.XComponent.event.fireEvent('beforebuild', obj);
16198             
16199             var opar = obj.parent;
16200             try { 
16201                 obj.parent = this.toObject(opar);
16202             } catch(e) {
16203                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16204                 return;
16205             }
16206             
16207             if (!obj.parent) {
16208                 Roo.debug && Roo.log("GOT top level module");
16209                 Roo.debug && Roo.log(obj);
16210                 obj.modules = new Roo.util.MixedCollection(false, 
16211                     function(o) { return o.order + '' }
16212                 );
16213                 this.topModule = obj;
16214                 return;
16215             }
16216                         // parent is a string (usually a dom element name..)
16217             if (typeof(obj.parent) == 'string') {
16218                 this.elmodules.push(obj);
16219                 return;
16220             }
16221             if (obj.parent.constructor != Roo.XComponent) {
16222                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16223             }
16224             if (!obj.parent.modules) {
16225                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16226                     function(o) { return o.order + '' }
16227                 );
16228             }
16229             if (obj.parent.disabled) {
16230                 obj.disabled = true;
16231             }
16232             obj.parent.modules.add(obj);
16233         }, this);
16234     },
16235     
16236      /**
16237      * make a list of modules to build.
16238      * @return {Array} list of modules. 
16239      */ 
16240     
16241     buildOrder : function()
16242     {
16243         var _this = this;
16244         var cmp = function(a,b) {   
16245             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16246         };
16247         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16248             throw "No top level modules to build";
16249         }
16250         
16251         // make a flat list in order of modules to build.
16252         var mods = this.topModule ? [ this.topModule ] : [];
16253                 
16254         
16255         // elmodules (is a list of DOM based modules )
16256         Roo.each(this.elmodules, function(e) {
16257             mods.push(e);
16258             if (!this.topModule &&
16259                 typeof(e.parent) == 'string' &&
16260                 e.parent.substring(0,1) == '#' &&
16261                 Roo.get(e.parent.substr(1))
16262                ) {
16263                 
16264                 _this.topModule = e;
16265             }
16266             
16267         });
16268
16269         
16270         // add modules to their parents..
16271         var addMod = function(m) {
16272             Roo.debug && Roo.log("build Order: add: " + m.name);
16273                 
16274             mods.push(m);
16275             if (m.modules && !m.disabled) {
16276                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16277                 m.modules.keySort('ASC',  cmp );
16278                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16279     
16280                 m.modules.each(addMod);
16281             } else {
16282                 Roo.debug && Roo.log("build Order: no child modules");
16283             }
16284             // not sure if this is used any more..
16285             if (m.finalize) {
16286                 m.finalize.name = m.name + " (clean up) ";
16287                 mods.push(m.finalize);
16288             }
16289             
16290         }
16291         if (this.topModule && this.topModule.modules) { 
16292             this.topModule.modules.keySort('ASC',  cmp );
16293             this.topModule.modules.each(addMod);
16294         } 
16295         return mods;
16296     },
16297     
16298      /**
16299      * Build the registered modules.
16300      * @param {Object} parent element.
16301      * @param {Function} optional method to call after module has been added.
16302      * 
16303      */ 
16304    
16305     build : function(opts) 
16306     {
16307         
16308         if (typeof(opts) != 'undefined') {
16309             Roo.apply(this,opts);
16310         }
16311         
16312         this.preBuild();
16313         var mods = this.buildOrder();
16314       
16315         //this.allmods = mods;
16316         //Roo.debug && Roo.log(mods);
16317         //return;
16318         if (!mods.length) { // should not happen
16319             throw "NO modules!!!";
16320         }
16321         
16322         
16323         var msg = "Building Interface...";
16324         // flash it up as modal - so we store the mask!?
16325         if (!this.hideProgress && Roo.MessageBox) {
16326             Roo.MessageBox.show({ title: 'loading' });
16327             Roo.MessageBox.show({
16328                title: "Please wait...",
16329                msg: msg,
16330                width:450,
16331                progress:true,
16332                closable:false,
16333                modal: false
16334               
16335             });
16336         }
16337         var total = mods.length;
16338         
16339         var _this = this;
16340         var progressRun = function() {
16341             if (!mods.length) {
16342                 Roo.debug && Roo.log('hide?');
16343                 if (!this.hideProgress && Roo.MessageBox) {
16344                     Roo.MessageBox.hide();
16345                 }
16346                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16347                 
16348                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16349                 
16350                 // THE END...
16351                 return false;   
16352             }
16353             
16354             var m = mods.shift();
16355             
16356             
16357             Roo.debug && Roo.log(m);
16358             // not sure if this is supported any more.. - modules that are are just function
16359             if (typeof(m) == 'function') { 
16360                 m.call(this);
16361                 return progressRun.defer(10, _this);
16362             } 
16363             
16364             
16365             msg = "Building Interface " + (total  - mods.length) + 
16366                     " of " + total + 
16367                     (m.name ? (' - ' + m.name) : '');
16368                         Roo.debug && Roo.log(msg);
16369             if (!this.hideProgress &&  Roo.MessageBox) { 
16370                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16371             }
16372             
16373          
16374             // is the module disabled?
16375             var disabled = (typeof(m.disabled) == 'function') ?
16376                 m.disabled.call(m.module.disabled) : m.disabled;    
16377             
16378             
16379             if (disabled) {
16380                 return progressRun(); // we do not update the display!
16381             }
16382             
16383             // now build 
16384             
16385                         
16386                         
16387             m.render();
16388             // it's 10 on top level, and 1 on others??? why...
16389             return progressRun.defer(10, _this);
16390              
16391         }
16392         progressRun.defer(1, _this);
16393      
16394         
16395         
16396     },
16397         
16398         
16399         /**
16400          * Event Object.
16401          *
16402          *
16403          */
16404         event: false, 
16405     /**
16406          * wrapper for event.on - aliased later..  
16407          * Typically use to register a event handler for register:
16408          *
16409          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16410          *
16411          */
16412     on : false
16413    
16414     
16415     
16416 });
16417
16418 Roo.XComponent.event = new Roo.util.Observable({
16419                 events : { 
16420                         /**
16421                          * @event register
16422                          * Fires when an Component is registered,
16423                          * set the disable property on the Component to stop registration.
16424                          * @param {Roo.XComponent} c the component being registerd.
16425                          * 
16426                          */
16427                         'register' : true,
16428             /**
16429                          * @event beforebuild
16430                          * Fires before each Component is built
16431                          * can be used to apply permissions.
16432                          * @param {Roo.XComponent} c the component being registerd.
16433                          * 
16434                          */
16435                         'beforebuild' : true,
16436                         /**
16437                          * @event buildcomplete
16438                          * Fires on the top level element when all elements have been built
16439                          * @param {Roo.XComponent} the top level component.
16440                          */
16441                         'buildcomplete' : true
16442                         
16443                 }
16444 });
16445
16446 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16447  /*
16448  * Based on:
16449  * Ext JS Library 1.1.1
16450  * Copyright(c) 2006-2007, Ext JS, LLC.
16451  *
16452  * Originally Released Under LGPL - original licence link has changed is not relivant.
16453  *
16454  * Fork - LGPL
16455  * <script type="text/javascript">
16456  */
16457
16458
16459
16460 /*
16461  * These classes are derivatives of the similarly named classes in the YUI Library.
16462  * The original license:
16463  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16464  * Code licensed under the BSD License:
16465  * http://developer.yahoo.net/yui/license.txt
16466  */
16467
16468 (function() {
16469
16470 var Event=Roo.EventManager;
16471 var Dom=Roo.lib.Dom;
16472
16473 /**
16474  * @class Roo.dd.DragDrop
16475  * @extends Roo.util.Observable
16476  * Defines the interface and base operation of items that that can be
16477  * dragged or can be drop targets.  It was designed to be extended, overriding
16478  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16479  * Up to three html elements can be associated with a DragDrop instance:
16480  * <ul>
16481  * <li>linked element: the element that is passed into the constructor.
16482  * This is the element which defines the boundaries for interaction with
16483  * other DragDrop objects.</li>
16484  * <li>handle element(s): The drag operation only occurs if the element that
16485  * was clicked matches a handle element.  By default this is the linked
16486  * element, but there are times that you will want only a portion of the
16487  * linked element to initiate the drag operation, and the setHandleElId()
16488  * method provides a way to define this.</li>
16489  * <li>drag element: this represents the element that would be moved along
16490  * with the cursor during a drag operation.  By default, this is the linked
16491  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16492  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16493  * </li>
16494  * </ul>
16495  * This class should not be instantiated until the onload event to ensure that
16496  * the associated elements are available.
16497  * The following would define a DragDrop obj that would interact with any
16498  * other DragDrop obj in the "group1" group:
16499  * <pre>
16500  *  dd = new Roo.dd.DragDrop("div1", "group1");
16501  * </pre>
16502  * Since none of the event handlers have been implemented, nothing would
16503  * actually happen if you were to run the code above.  Normally you would
16504  * override this class or one of the default implementations, but you can
16505  * also override the methods you want on an instance of the class...
16506  * <pre>
16507  *  dd.onDragDrop = function(e, id) {
16508  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16509  *  }
16510  * </pre>
16511  * @constructor
16512  * @param {String} id of the element that is linked to this instance
16513  * @param {String} sGroup the group of related DragDrop objects
16514  * @param {object} config an object containing configurable attributes
16515  *                Valid properties for DragDrop:
16516  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16517  */
16518 Roo.dd.DragDrop = function(id, sGroup, config) {
16519     if (id) {
16520         this.init(id, sGroup, config);
16521     }
16522     
16523 };
16524
16525 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16526
16527     /**
16528      * The id of the element associated with this object.  This is what we
16529      * refer to as the "linked element" because the size and position of
16530      * this element is used to determine when the drag and drop objects have
16531      * interacted.
16532      * @property id
16533      * @type String
16534      */
16535     id: null,
16536
16537     /**
16538      * Configuration attributes passed into the constructor
16539      * @property config
16540      * @type object
16541      */
16542     config: null,
16543
16544     /**
16545      * The id of the element that will be dragged.  By default this is same
16546      * as the linked element , but could be changed to another element. Ex:
16547      * Roo.dd.DDProxy
16548      * @property dragElId
16549      * @type String
16550      * @private
16551      */
16552     dragElId: null,
16553
16554     /**
16555      * the id of the element that initiates the drag operation.  By default
16556      * this is the linked element, but could be changed to be a child of this
16557      * element.  This lets us do things like only starting the drag when the
16558      * header element within the linked html element is clicked.
16559      * @property handleElId
16560      * @type String
16561      * @private
16562      */
16563     handleElId: null,
16564
16565     /**
16566      * An associative array of HTML tags that will be ignored if clicked.
16567      * @property invalidHandleTypes
16568      * @type {string: string}
16569      */
16570     invalidHandleTypes: null,
16571
16572     /**
16573      * An associative array of ids for elements that will be ignored if clicked
16574      * @property invalidHandleIds
16575      * @type {string: string}
16576      */
16577     invalidHandleIds: null,
16578
16579     /**
16580      * An indexted array of css class names for elements that will be ignored
16581      * if clicked.
16582      * @property invalidHandleClasses
16583      * @type string[]
16584      */
16585     invalidHandleClasses: null,
16586
16587     /**
16588      * The linked element's absolute X position at the time the drag was
16589      * started
16590      * @property startPageX
16591      * @type int
16592      * @private
16593      */
16594     startPageX: 0,
16595
16596     /**
16597      * The linked element's absolute X position at the time the drag was
16598      * started
16599      * @property startPageY
16600      * @type int
16601      * @private
16602      */
16603     startPageY: 0,
16604
16605     /**
16606      * The group defines a logical collection of DragDrop objects that are
16607      * related.  Instances only get events when interacting with other
16608      * DragDrop object in the same group.  This lets us define multiple
16609      * groups using a single DragDrop subclass if we want.
16610      * @property groups
16611      * @type {string: string}
16612      */
16613     groups: null,
16614
16615     /**
16616      * Individual drag/drop instances can be locked.  This will prevent
16617      * onmousedown start drag.
16618      * @property locked
16619      * @type boolean
16620      * @private
16621      */
16622     locked: false,
16623
16624     /**
16625      * Lock this instance
16626      * @method lock
16627      */
16628     lock: function() { this.locked = true; },
16629
16630     /**
16631      * Unlock this instace
16632      * @method unlock
16633      */
16634     unlock: function() { this.locked = false; },
16635
16636     /**
16637      * By default, all insances can be a drop target.  This can be disabled by
16638      * setting isTarget to false.
16639      * @method isTarget
16640      * @type boolean
16641      */
16642     isTarget: true,
16643
16644     /**
16645      * The padding configured for this drag and drop object for calculating
16646      * the drop zone intersection with this object.
16647      * @method padding
16648      * @type int[]
16649      */
16650     padding: null,
16651
16652     /**
16653      * Cached reference to the linked element
16654      * @property _domRef
16655      * @private
16656      */
16657     _domRef: null,
16658
16659     /**
16660      * Internal typeof flag
16661      * @property __ygDragDrop
16662      * @private
16663      */
16664     __ygDragDrop: true,
16665
16666     /**
16667      * Set to true when horizontal contraints are applied
16668      * @property constrainX
16669      * @type boolean
16670      * @private
16671      */
16672     constrainX: false,
16673
16674     /**
16675      * Set to true when vertical contraints are applied
16676      * @property constrainY
16677      * @type boolean
16678      * @private
16679      */
16680     constrainY: false,
16681
16682     /**
16683      * The left constraint
16684      * @property minX
16685      * @type int
16686      * @private
16687      */
16688     minX: 0,
16689
16690     /**
16691      * The right constraint
16692      * @property maxX
16693      * @type int
16694      * @private
16695      */
16696     maxX: 0,
16697
16698     /**
16699      * The up constraint
16700      * @property minY
16701      * @type int
16702      * @type int
16703      * @private
16704      */
16705     minY: 0,
16706
16707     /**
16708      * The down constraint
16709      * @property maxY
16710      * @type int
16711      * @private
16712      */
16713     maxY: 0,
16714
16715     /**
16716      * Maintain offsets when we resetconstraints.  Set to true when you want
16717      * the position of the element relative to its parent to stay the same
16718      * when the page changes
16719      *
16720      * @property maintainOffset
16721      * @type boolean
16722      */
16723     maintainOffset: false,
16724
16725     /**
16726      * Array of pixel locations the element will snap to if we specified a
16727      * horizontal graduation/interval.  This array is generated automatically
16728      * when you define a tick interval.
16729      * @property xTicks
16730      * @type int[]
16731      */
16732     xTicks: null,
16733
16734     /**
16735      * Array of pixel locations the element will snap to if we specified a
16736      * vertical graduation/interval.  This array is generated automatically
16737      * when you define a tick interval.
16738      * @property yTicks
16739      * @type int[]
16740      */
16741     yTicks: null,
16742
16743     /**
16744      * By default the drag and drop instance will only respond to the primary
16745      * button click (left button for a right-handed mouse).  Set to true to
16746      * allow drag and drop to start with any mouse click that is propogated
16747      * by the browser
16748      * @property primaryButtonOnly
16749      * @type boolean
16750      */
16751     primaryButtonOnly: true,
16752
16753     /**
16754      * The availabe property is false until the linked dom element is accessible.
16755      * @property available
16756      * @type boolean
16757      */
16758     available: false,
16759
16760     /**
16761      * By default, drags can only be initiated if the mousedown occurs in the
16762      * region the linked element is.  This is done in part to work around a
16763      * bug in some browsers that mis-report the mousedown if the previous
16764      * mouseup happened outside of the window.  This property is set to true
16765      * if outer handles are defined.
16766      *
16767      * @property hasOuterHandles
16768      * @type boolean
16769      * @default false
16770      */
16771     hasOuterHandles: false,
16772
16773     /**
16774      * Code that executes immediately before the startDrag event
16775      * @method b4StartDrag
16776      * @private
16777      */
16778     b4StartDrag: function(x, y) { },
16779
16780     /**
16781      * Abstract method called after a drag/drop object is clicked
16782      * and the drag or mousedown time thresholds have beeen met.
16783      * @method startDrag
16784      * @param {int} X click location
16785      * @param {int} Y click location
16786      */
16787     startDrag: function(x, y) { /* override this */ },
16788
16789     /**
16790      * Code that executes immediately before the onDrag event
16791      * @method b4Drag
16792      * @private
16793      */
16794     b4Drag: function(e) { },
16795
16796     /**
16797      * Abstract method called during the onMouseMove event while dragging an
16798      * object.
16799      * @method onDrag
16800      * @param {Event} e the mousemove event
16801      */
16802     onDrag: function(e) { /* override this */ },
16803
16804     /**
16805      * Abstract method called when this element fist begins hovering over
16806      * another DragDrop obj
16807      * @method onDragEnter
16808      * @param {Event} e the mousemove event
16809      * @param {String|DragDrop[]} id In POINT mode, the element
16810      * id this is hovering over.  In INTERSECT mode, an array of one or more
16811      * dragdrop items being hovered over.
16812      */
16813     onDragEnter: function(e, id) { /* override this */ },
16814
16815     /**
16816      * Code that executes immediately before the onDragOver event
16817      * @method b4DragOver
16818      * @private
16819      */
16820     b4DragOver: function(e) { },
16821
16822     /**
16823      * Abstract method called when this element is hovering over another
16824      * DragDrop obj
16825      * @method onDragOver
16826      * @param {Event} e the mousemove event
16827      * @param {String|DragDrop[]} id In POINT mode, the element
16828      * id this is hovering over.  In INTERSECT mode, an array of dd items
16829      * being hovered over.
16830      */
16831     onDragOver: function(e, id) { /* override this */ },
16832
16833     /**
16834      * Code that executes immediately before the onDragOut event
16835      * @method b4DragOut
16836      * @private
16837      */
16838     b4DragOut: function(e) { },
16839
16840     /**
16841      * Abstract method called when we are no longer hovering over an element
16842      * @method onDragOut
16843      * @param {Event} e the mousemove event
16844      * @param {String|DragDrop[]} id In POINT mode, the element
16845      * id this was hovering over.  In INTERSECT mode, an array of dd items
16846      * that the mouse is no longer over.
16847      */
16848     onDragOut: function(e, id) { /* override this */ },
16849
16850     /**
16851      * Code that executes immediately before the onDragDrop event
16852      * @method b4DragDrop
16853      * @private
16854      */
16855     b4DragDrop: function(e) { },
16856
16857     /**
16858      * Abstract method called when this item is dropped on another DragDrop
16859      * obj
16860      * @method onDragDrop
16861      * @param {Event} e the mouseup event
16862      * @param {String|DragDrop[]} id In POINT mode, the element
16863      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16864      * was dropped on.
16865      */
16866     onDragDrop: function(e, id) { /* override this */ },
16867
16868     /**
16869      * Abstract method called when this item is dropped on an area with no
16870      * drop target
16871      * @method onInvalidDrop
16872      * @param {Event} e the mouseup event
16873      */
16874     onInvalidDrop: function(e) { /* override this */ },
16875
16876     /**
16877      * Code that executes immediately before the endDrag event
16878      * @method b4EndDrag
16879      * @private
16880      */
16881     b4EndDrag: function(e) { },
16882
16883     /**
16884      * Fired when we are done dragging the object
16885      * @method endDrag
16886      * @param {Event} e the mouseup event
16887      */
16888     endDrag: function(e) { /* override this */ },
16889
16890     /**
16891      * Code executed immediately before the onMouseDown event
16892      * @method b4MouseDown
16893      * @param {Event} e the mousedown event
16894      * @private
16895      */
16896     b4MouseDown: function(e) {  },
16897
16898     /**
16899      * Event handler that fires when a drag/drop obj gets a mousedown
16900      * @method onMouseDown
16901      * @param {Event} e the mousedown event
16902      */
16903     onMouseDown: function(e) { /* override this */ },
16904
16905     /**
16906      * Event handler that fires when a drag/drop obj gets a mouseup
16907      * @method onMouseUp
16908      * @param {Event} e the mouseup event
16909      */
16910     onMouseUp: function(e) { /* override this */ },
16911
16912     /**
16913      * Override the onAvailable method to do what is needed after the initial
16914      * position was determined.
16915      * @method onAvailable
16916      */
16917     onAvailable: function () {
16918     },
16919
16920     /*
16921      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16922      * @type Object
16923      */
16924     defaultPadding : {left:0, right:0, top:0, bottom:0},
16925
16926     /*
16927      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16928  *
16929  * Usage:
16930  <pre><code>
16931  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16932                 { dragElId: "existingProxyDiv" });
16933  dd.startDrag = function(){
16934      this.constrainTo("parent-id");
16935  };
16936  </code></pre>
16937  * Or you can initalize it using the {@link Roo.Element} object:
16938  <pre><code>
16939  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16940      startDrag : function(){
16941          this.constrainTo("parent-id");
16942      }
16943  });
16944  </code></pre>
16945      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16946      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16947      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16948      * an object containing the sides to pad. For example: {right:10, bottom:10}
16949      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16950      */
16951     constrainTo : function(constrainTo, pad, inContent){
16952         if(typeof pad == "number"){
16953             pad = {left: pad, right:pad, top:pad, bottom:pad};
16954         }
16955         pad = pad || this.defaultPadding;
16956         var b = Roo.get(this.getEl()).getBox();
16957         var ce = Roo.get(constrainTo);
16958         var s = ce.getScroll();
16959         var c, cd = ce.dom;
16960         if(cd == document.body){
16961             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16962         }else{
16963             xy = ce.getXY();
16964             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16965         }
16966
16967
16968         var topSpace = b.y - c.y;
16969         var leftSpace = b.x - c.x;
16970
16971         this.resetConstraints();
16972         this.setXConstraint(leftSpace - (pad.left||0), // left
16973                 c.width - leftSpace - b.width - (pad.right||0) //right
16974         );
16975         this.setYConstraint(topSpace - (pad.top||0), //top
16976                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16977         );
16978     },
16979
16980     /**
16981      * Returns a reference to the linked element
16982      * @method getEl
16983      * @return {HTMLElement} the html element
16984      */
16985     getEl: function() {
16986         if (!this._domRef) {
16987             this._domRef = Roo.getDom(this.id);
16988         }
16989
16990         return this._domRef;
16991     },
16992
16993     /**
16994      * Returns a reference to the actual element to drag.  By default this is
16995      * the same as the html element, but it can be assigned to another
16996      * element. An example of this can be found in Roo.dd.DDProxy
16997      * @method getDragEl
16998      * @return {HTMLElement} the html element
16999      */
17000     getDragEl: function() {
17001         return Roo.getDom(this.dragElId);
17002     },
17003
17004     /**
17005      * Sets up the DragDrop object.  Must be called in the constructor of any
17006      * Roo.dd.DragDrop subclass
17007      * @method init
17008      * @param id the id of the linked element
17009      * @param {String} sGroup the group of related items
17010      * @param {object} config configuration attributes
17011      */
17012     init: function(id, sGroup, config) {
17013         this.initTarget(id, sGroup, config);
17014         if (!Roo.isTouch) {
17015             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17016         }
17017         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17018         // Event.on(this.id, "selectstart", Event.preventDefault);
17019     },
17020
17021     /**
17022      * Initializes Targeting functionality only... the object does not
17023      * get a mousedown handler.
17024      * @method initTarget
17025      * @param id the id of the linked element
17026      * @param {String} sGroup the group of related items
17027      * @param {object} config configuration attributes
17028      */
17029     initTarget: function(id, sGroup, config) {
17030
17031         // configuration attributes
17032         this.config = config || {};
17033
17034         // create a local reference to the drag and drop manager
17035         this.DDM = Roo.dd.DDM;
17036         // initialize the groups array
17037         this.groups = {};
17038
17039         // assume that we have an element reference instead of an id if the
17040         // parameter is not a string
17041         if (typeof id !== "string") {
17042             id = Roo.id(id);
17043         }
17044
17045         // set the id
17046         this.id = id;
17047
17048         // add to an interaction group
17049         this.addToGroup((sGroup) ? sGroup : "default");
17050
17051         // We don't want to register this as the handle with the manager
17052         // so we just set the id rather than calling the setter.
17053         this.handleElId = id;
17054
17055         // the linked element is the element that gets dragged by default
17056         this.setDragElId(id);
17057
17058         // by default, clicked anchors will not start drag operations.
17059         this.invalidHandleTypes = { A: "A" };
17060         this.invalidHandleIds = {};
17061         this.invalidHandleClasses = [];
17062
17063         this.applyConfig();
17064
17065         this.handleOnAvailable();
17066     },
17067
17068     /**
17069      * Applies the configuration parameters that were passed into the constructor.
17070      * This is supposed to happen at each level through the inheritance chain.  So
17071      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17072      * DragDrop in order to get all of the parameters that are available in
17073      * each object.
17074      * @method applyConfig
17075      */
17076     applyConfig: function() {
17077
17078         // configurable properties:
17079         //    padding, isTarget, maintainOffset, primaryButtonOnly
17080         this.padding           = this.config.padding || [0, 0, 0, 0];
17081         this.isTarget          = (this.config.isTarget !== false);
17082         this.maintainOffset    = (this.config.maintainOffset);
17083         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17084
17085     },
17086
17087     /**
17088      * Executed when the linked element is available
17089      * @method handleOnAvailable
17090      * @private
17091      */
17092     handleOnAvailable: function() {
17093         this.available = true;
17094         this.resetConstraints();
17095         this.onAvailable();
17096     },
17097
17098      /**
17099      * Configures the padding for the target zone in px.  Effectively expands
17100      * (or reduces) the virtual object size for targeting calculations.
17101      * Supports css-style shorthand; if only one parameter is passed, all sides
17102      * will have that padding, and if only two are passed, the top and bottom
17103      * will have the first param, the left and right the second.
17104      * @method setPadding
17105      * @param {int} iTop    Top pad
17106      * @param {int} iRight  Right pad
17107      * @param {int} iBot    Bot pad
17108      * @param {int} iLeft   Left pad
17109      */
17110     setPadding: function(iTop, iRight, iBot, iLeft) {
17111         // this.padding = [iLeft, iRight, iTop, iBot];
17112         if (!iRight && 0 !== iRight) {
17113             this.padding = [iTop, iTop, iTop, iTop];
17114         } else if (!iBot && 0 !== iBot) {
17115             this.padding = [iTop, iRight, iTop, iRight];
17116         } else {
17117             this.padding = [iTop, iRight, iBot, iLeft];
17118         }
17119     },
17120
17121     /**
17122      * Stores the initial placement of the linked element.
17123      * @method setInitialPosition
17124      * @param {int} diffX   the X offset, default 0
17125      * @param {int} diffY   the Y offset, default 0
17126      */
17127     setInitPosition: function(diffX, diffY) {
17128         var el = this.getEl();
17129
17130         if (!this.DDM.verifyEl(el)) {
17131             return;
17132         }
17133
17134         var dx = diffX || 0;
17135         var dy = diffY || 0;
17136
17137         var p = Dom.getXY( el );
17138
17139         this.initPageX = p[0] - dx;
17140         this.initPageY = p[1] - dy;
17141
17142         this.lastPageX = p[0];
17143         this.lastPageY = p[1];
17144
17145
17146         this.setStartPosition(p);
17147     },
17148
17149     /**
17150      * Sets the start position of the element.  This is set when the obj
17151      * is initialized, the reset when a drag is started.
17152      * @method setStartPosition
17153      * @param pos current position (from previous lookup)
17154      * @private
17155      */
17156     setStartPosition: function(pos) {
17157         var p = pos || Dom.getXY( this.getEl() );
17158         this.deltaSetXY = null;
17159
17160         this.startPageX = p[0];
17161         this.startPageY = p[1];
17162     },
17163
17164     /**
17165      * Add this instance to a group of related drag/drop objects.  All
17166      * instances belong to at least one group, and can belong to as many
17167      * groups as needed.
17168      * @method addToGroup
17169      * @param sGroup {string} the name of the group
17170      */
17171     addToGroup: function(sGroup) {
17172         this.groups[sGroup] = true;
17173         this.DDM.regDragDrop(this, sGroup);
17174     },
17175
17176     /**
17177      * Remove's this instance from the supplied interaction group
17178      * @method removeFromGroup
17179      * @param {string}  sGroup  The group to drop
17180      */
17181     removeFromGroup: function(sGroup) {
17182         if (this.groups[sGroup]) {
17183             delete this.groups[sGroup];
17184         }
17185
17186         this.DDM.removeDDFromGroup(this, sGroup);
17187     },
17188
17189     /**
17190      * Allows you to specify that an element other than the linked element
17191      * will be moved with the cursor during a drag
17192      * @method setDragElId
17193      * @param id {string} the id of the element that will be used to initiate the drag
17194      */
17195     setDragElId: function(id) {
17196         this.dragElId = id;
17197     },
17198
17199     /**
17200      * Allows you to specify a child of the linked element that should be
17201      * used to initiate the drag operation.  An example of this would be if
17202      * you have a content div with text and links.  Clicking anywhere in the
17203      * content area would normally start the drag operation.  Use this method
17204      * to specify that an element inside of the content div is the element
17205      * that starts the drag operation.
17206      * @method setHandleElId
17207      * @param id {string} the id of the element that will be used to
17208      * initiate the drag.
17209      */
17210     setHandleElId: function(id) {
17211         if (typeof id !== "string") {
17212             id = Roo.id(id);
17213         }
17214         this.handleElId = id;
17215         this.DDM.regHandle(this.id, id);
17216     },
17217
17218     /**
17219      * Allows you to set an element outside of the linked element as a drag
17220      * handle
17221      * @method setOuterHandleElId
17222      * @param id the id of the element that will be used to initiate the drag
17223      */
17224     setOuterHandleElId: function(id) {
17225         if (typeof id !== "string") {
17226             id = Roo.id(id);
17227         }
17228         Event.on(id, "mousedown",
17229                 this.handleMouseDown, this);
17230         this.setHandleElId(id);
17231
17232         this.hasOuterHandles = true;
17233     },
17234
17235     /**
17236      * Remove all drag and drop hooks for this element
17237      * @method unreg
17238      */
17239     unreg: function() {
17240         Event.un(this.id, "mousedown",
17241                 this.handleMouseDown);
17242         Event.un(this.id, "touchstart",
17243                 this.handleMouseDown);
17244         this._domRef = null;
17245         this.DDM._remove(this);
17246     },
17247
17248     destroy : function(){
17249         this.unreg();
17250     },
17251
17252     /**
17253      * Returns true if this instance is locked, or the drag drop mgr is locked
17254      * (meaning that all drag/drop is disabled on the page.)
17255      * @method isLocked
17256      * @return {boolean} true if this obj or all drag/drop is locked, else
17257      * false
17258      */
17259     isLocked: function() {
17260         return (this.DDM.isLocked() || this.locked);
17261     },
17262
17263     /**
17264      * Fired when this object is clicked
17265      * @method handleMouseDown
17266      * @param {Event} e
17267      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17268      * @private
17269      */
17270     handleMouseDown: function(e, oDD){
17271      
17272         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17273             //Roo.log('not touch/ button !=0');
17274             return;
17275         }
17276         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17277             return; // double touch..
17278         }
17279         
17280
17281         if (this.isLocked()) {
17282             //Roo.log('locked');
17283             return;
17284         }
17285
17286         this.DDM.refreshCache(this.groups);
17287 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17288         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17289         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17290             //Roo.log('no outer handes or not over target');
17291                 // do nothing.
17292         } else {
17293 //            Roo.log('check validator');
17294             if (this.clickValidator(e)) {
17295 //                Roo.log('validate success');
17296                 // set the initial element position
17297                 this.setStartPosition();
17298
17299
17300                 this.b4MouseDown(e);
17301                 this.onMouseDown(e);
17302
17303                 this.DDM.handleMouseDown(e, this);
17304
17305                 this.DDM.stopEvent(e);
17306             } else {
17307
17308
17309             }
17310         }
17311     },
17312
17313     clickValidator: function(e) {
17314         var target = e.getTarget();
17315         return ( this.isValidHandleChild(target) &&
17316                     (this.id == this.handleElId ||
17317                         this.DDM.handleWasClicked(target, this.id)) );
17318     },
17319
17320     /**
17321      * Allows you to specify a tag name that should not start a drag operation
17322      * when clicked.  This is designed to facilitate embedding links within a
17323      * drag handle that do something other than start the drag.
17324      * @method addInvalidHandleType
17325      * @param {string} tagName the type of element to exclude
17326      */
17327     addInvalidHandleType: function(tagName) {
17328         var type = tagName.toUpperCase();
17329         this.invalidHandleTypes[type] = type;
17330     },
17331
17332     /**
17333      * Lets you to specify an element id for a child of a drag handle
17334      * that should not initiate a drag
17335      * @method addInvalidHandleId
17336      * @param {string} id the element id of the element you wish to ignore
17337      */
17338     addInvalidHandleId: function(id) {
17339         if (typeof id !== "string") {
17340             id = Roo.id(id);
17341         }
17342         this.invalidHandleIds[id] = id;
17343     },
17344
17345     /**
17346      * Lets you specify a css class of elements that will not initiate a drag
17347      * @method addInvalidHandleClass
17348      * @param {string} cssClass the class of the elements you wish to ignore
17349      */
17350     addInvalidHandleClass: function(cssClass) {
17351         this.invalidHandleClasses.push(cssClass);
17352     },
17353
17354     /**
17355      * Unsets an excluded tag name set by addInvalidHandleType
17356      * @method removeInvalidHandleType
17357      * @param {string} tagName the type of element to unexclude
17358      */
17359     removeInvalidHandleType: function(tagName) {
17360         var type = tagName.toUpperCase();
17361         // this.invalidHandleTypes[type] = null;
17362         delete this.invalidHandleTypes[type];
17363     },
17364
17365     /**
17366      * Unsets an invalid handle id
17367      * @method removeInvalidHandleId
17368      * @param {string} id the id of the element to re-enable
17369      */
17370     removeInvalidHandleId: function(id) {
17371         if (typeof id !== "string") {
17372             id = Roo.id(id);
17373         }
17374         delete this.invalidHandleIds[id];
17375     },
17376
17377     /**
17378      * Unsets an invalid css class
17379      * @method removeInvalidHandleClass
17380      * @param {string} cssClass the class of the element(s) you wish to
17381      * re-enable
17382      */
17383     removeInvalidHandleClass: function(cssClass) {
17384         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17385             if (this.invalidHandleClasses[i] == cssClass) {
17386                 delete this.invalidHandleClasses[i];
17387             }
17388         }
17389     },
17390
17391     /**
17392      * Checks the tag exclusion list to see if this click should be ignored
17393      * @method isValidHandleChild
17394      * @param {HTMLElement} node the HTMLElement to evaluate
17395      * @return {boolean} true if this is a valid tag type, false if not
17396      */
17397     isValidHandleChild: function(node) {
17398
17399         var valid = true;
17400         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17401         var nodeName;
17402         try {
17403             nodeName = node.nodeName.toUpperCase();
17404         } catch(e) {
17405             nodeName = node.nodeName;
17406         }
17407         valid = valid && !this.invalidHandleTypes[nodeName];
17408         valid = valid && !this.invalidHandleIds[node.id];
17409
17410         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17411             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17412         }
17413
17414
17415         return valid;
17416
17417     },
17418
17419     /**
17420      * Create the array of horizontal tick marks if an interval was specified
17421      * in setXConstraint().
17422      * @method setXTicks
17423      * @private
17424      */
17425     setXTicks: function(iStartX, iTickSize) {
17426         this.xTicks = [];
17427         this.xTickSize = iTickSize;
17428
17429         var tickMap = {};
17430
17431         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17432             if (!tickMap[i]) {
17433                 this.xTicks[this.xTicks.length] = i;
17434                 tickMap[i] = true;
17435             }
17436         }
17437
17438         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17439             if (!tickMap[i]) {
17440                 this.xTicks[this.xTicks.length] = i;
17441                 tickMap[i] = true;
17442             }
17443         }
17444
17445         this.xTicks.sort(this.DDM.numericSort) ;
17446     },
17447
17448     /**
17449      * Create the array of vertical tick marks if an interval was specified in
17450      * setYConstraint().
17451      * @method setYTicks
17452      * @private
17453      */
17454     setYTicks: function(iStartY, iTickSize) {
17455         this.yTicks = [];
17456         this.yTickSize = iTickSize;
17457
17458         var tickMap = {};
17459
17460         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17461             if (!tickMap[i]) {
17462                 this.yTicks[this.yTicks.length] = i;
17463                 tickMap[i] = true;
17464             }
17465         }
17466
17467         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17468             if (!tickMap[i]) {
17469                 this.yTicks[this.yTicks.length] = i;
17470                 tickMap[i] = true;
17471             }
17472         }
17473
17474         this.yTicks.sort(this.DDM.numericSort) ;
17475     },
17476
17477     /**
17478      * By default, the element can be dragged any place on the screen.  Use
17479      * this method to limit the horizontal travel of the element.  Pass in
17480      * 0,0 for the parameters if you want to lock the drag to the y axis.
17481      * @method setXConstraint
17482      * @param {int} iLeft the number of pixels the element can move to the left
17483      * @param {int} iRight the number of pixels the element can move to the
17484      * right
17485      * @param {int} iTickSize optional parameter for specifying that the
17486      * element
17487      * should move iTickSize pixels at a time.
17488      */
17489     setXConstraint: function(iLeft, iRight, iTickSize) {
17490         this.leftConstraint = iLeft;
17491         this.rightConstraint = iRight;
17492
17493         this.minX = this.initPageX - iLeft;
17494         this.maxX = this.initPageX + iRight;
17495         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17496
17497         this.constrainX = true;
17498     },
17499
17500     /**
17501      * Clears any constraints applied to this instance.  Also clears ticks
17502      * since they can't exist independent of a constraint at this time.
17503      * @method clearConstraints
17504      */
17505     clearConstraints: function() {
17506         this.constrainX = false;
17507         this.constrainY = false;
17508         this.clearTicks();
17509     },
17510
17511     /**
17512      * Clears any tick interval defined for this instance
17513      * @method clearTicks
17514      */
17515     clearTicks: function() {
17516         this.xTicks = null;
17517         this.yTicks = null;
17518         this.xTickSize = 0;
17519         this.yTickSize = 0;
17520     },
17521
17522     /**
17523      * By default, the element can be dragged any place on the screen.  Set
17524      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17525      * parameters if you want to lock the drag to the x axis.
17526      * @method setYConstraint
17527      * @param {int} iUp the number of pixels the element can move up
17528      * @param {int} iDown the number of pixels the element can move down
17529      * @param {int} iTickSize optional parameter for specifying that the
17530      * element should move iTickSize pixels at a time.
17531      */
17532     setYConstraint: function(iUp, iDown, iTickSize) {
17533         this.topConstraint = iUp;
17534         this.bottomConstraint = iDown;
17535
17536         this.minY = this.initPageY - iUp;
17537         this.maxY = this.initPageY + iDown;
17538         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17539
17540         this.constrainY = true;
17541
17542     },
17543
17544     /**
17545      * resetConstraints must be called if you manually reposition a dd element.
17546      * @method resetConstraints
17547      * @param {boolean} maintainOffset
17548      */
17549     resetConstraints: function() {
17550
17551
17552         // Maintain offsets if necessary
17553         if (this.initPageX || this.initPageX === 0) {
17554             // figure out how much this thing has moved
17555             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17556             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17557
17558             this.setInitPosition(dx, dy);
17559
17560         // This is the first time we have detected the element's position
17561         } else {
17562             this.setInitPosition();
17563         }
17564
17565         if (this.constrainX) {
17566             this.setXConstraint( this.leftConstraint,
17567                                  this.rightConstraint,
17568                                  this.xTickSize        );
17569         }
17570
17571         if (this.constrainY) {
17572             this.setYConstraint( this.topConstraint,
17573                                  this.bottomConstraint,
17574                                  this.yTickSize         );
17575         }
17576     },
17577
17578     /**
17579      * Normally the drag element is moved pixel by pixel, but we can specify
17580      * that it move a number of pixels at a time.  This method resolves the
17581      * location when we have it set up like this.
17582      * @method getTick
17583      * @param {int} val where we want to place the object
17584      * @param {int[]} tickArray sorted array of valid points
17585      * @return {int} the closest tick
17586      * @private
17587      */
17588     getTick: function(val, tickArray) {
17589
17590         if (!tickArray) {
17591             // If tick interval is not defined, it is effectively 1 pixel,
17592             // so we return the value passed to us.
17593             return val;
17594         } else if (tickArray[0] >= val) {
17595             // The value is lower than the first tick, so we return the first
17596             // tick.
17597             return tickArray[0];
17598         } else {
17599             for (var i=0, len=tickArray.length; i<len; ++i) {
17600                 var next = i + 1;
17601                 if (tickArray[next] && tickArray[next] >= val) {
17602                     var diff1 = val - tickArray[i];
17603                     var diff2 = tickArray[next] - val;
17604                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17605                 }
17606             }
17607
17608             // The value is larger than the last tick, so we return the last
17609             // tick.
17610             return tickArray[tickArray.length - 1];
17611         }
17612     },
17613
17614     /**
17615      * toString method
17616      * @method toString
17617      * @return {string} string representation of the dd obj
17618      */
17619     toString: function() {
17620         return ("DragDrop " + this.id);
17621     }
17622
17623 });
17624
17625 })();
17626 /*
17627  * Based on:
17628  * Ext JS Library 1.1.1
17629  * Copyright(c) 2006-2007, Ext JS, LLC.
17630  *
17631  * Originally Released Under LGPL - original licence link has changed is not relivant.
17632  *
17633  * Fork - LGPL
17634  * <script type="text/javascript">
17635  */
17636
17637
17638 /**
17639  * The drag and drop utility provides a framework for building drag and drop
17640  * applications.  In addition to enabling drag and drop for specific elements,
17641  * the drag and drop elements are tracked by the manager class, and the
17642  * interactions between the various elements are tracked during the drag and
17643  * the implementing code is notified about these important moments.
17644  */
17645
17646 // Only load the library once.  Rewriting the manager class would orphan
17647 // existing drag and drop instances.
17648 if (!Roo.dd.DragDropMgr) {
17649
17650 /**
17651  * @class Roo.dd.DragDropMgr
17652  * DragDropMgr is a singleton that tracks the element interaction for
17653  * all DragDrop items in the window.  Generally, you will not call
17654  * this class directly, but it does have helper methods that could
17655  * be useful in your DragDrop implementations.
17656  * @singleton
17657  */
17658 Roo.dd.DragDropMgr = function() {
17659
17660     var Event = Roo.EventManager;
17661
17662     return {
17663
17664         /**
17665          * Two dimensional Array of registered DragDrop objects.  The first
17666          * dimension is the DragDrop item group, the second the DragDrop
17667          * object.
17668          * @property ids
17669          * @type {string: string}
17670          * @private
17671          * @static
17672          */
17673         ids: {},
17674
17675         /**
17676          * Array of element ids defined as drag handles.  Used to determine
17677          * if the element that generated the mousedown event is actually the
17678          * handle and not the html element itself.
17679          * @property handleIds
17680          * @type {string: string}
17681          * @private
17682          * @static
17683          */
17684         handleIds: {},
17685
17686         /**
17687          * the DragDrop object that is currently being dragged
17688          * @property dragCurrent
17689          * @type DragDrop
17690          * @private
17691          * @static
17692          **/
17693         dragCurrent: null,
17694
17695         /**
17696          * the DragDrop object(s) that are being hovered over
17697          * @property dragOvers
17698          * @type Array
17699          * @private
17700          * @static
17701          */
17702         dragOvers: {},
17703
17704         /**
17705          * the X distance between the cursor and the object being dragged
17706          * @property deltaX
17707          * @type int
17708          * @private
17709          * @static
17710          */
17711         deltaX: 0,
17712
17713         /**
17714          * the Y distance between the cursor and the object being dragged
17715          * @property deltaY
17716          * @type int
17717          * @private
17718          * @static
17719          */
17720         deltaY: 0,
17721
17722         /**
17723          * Flag to determine if we should prevent the default behavior of the
17724          * events we define. By default this is true, but this can be set to
17725          * false if you need the default behavior (not recommended)
17726          * @property preventDefault
17727          * @type boolean
17728          * @static
17729          */
17730         preventDefault: true,
17731
17732         /**
17733          * Flag to determine if we should stop the propagation of the events
17734          * we generate. This is true by default but you may want to set it to
17735          * false if the html element contains other features that require the
17736          * mouse click.
17737          * @property stopPropagation
17738          * @type boolean
17739          * @static
17740          */
17741         stopPropagation: true,
17742
17743         /**
17744          * Internal flag that is set to true when drag and drop has been
17745          * intialized
17746          * @property initialized
17747          * @private
17748          * @static
17749          */
17750         initalized: false,
17751
17752         /**
17753          * All drag and drop can be disabled.
17754          * @property locked
17755          * @private
17756          * @static
17757          */
17758         locked: false,
17759
17760         /**
17761          * Called the first time an element is registered.
17762          * @method init
17763          * @private
17764          * @static
17765          */
17766         init: function() {
17767             this.initialized = true;
17768         },
17769
17770         /**
17771          * In point mode, drag and drop interaction is defined by the
17772          * location of the cursor during the drag/drop
17773          * @property POINT
17774          * @type int
17775          * @static
17776          */
17777         POINT: 0,
17778
17779         /**
17780          * In intersect mode, drag and drop interactio nis defined by the
17781          * overlap of two or more drag and drop objects.
17782          * @property INTERSECT
17783          * @type int
17784          * @static
17785          */
17786         INTERSECT: 1,
17787
17788         /**
17789          * The current drag and drop mode.  Default: POINT
17790          * @property mode
17791          * @type int
17792          * @static
17793          */
17794         mode: 0,
17795
17796         /**
17797          * Runs method on all drag and drop objects
17798          * @method _execOnAll
17799          * @private
17800          * @static
17801          */
17802         _execOnAll: function(sMethod, args) {
17803             for (var i in this.ids) {
17804                 for (var j in this.ids[i]) {
17805                     var oDD = this.ids[i][j];
17806                     if (! this.isTypeOfDD(oDD)) {
17807                         continue;
17808                     }
17809                     oDD[sMethod].apply(oDD, args);
17810                 }
17811             }
17812         },
17813
17814         /**
17815          * Drag and drop initialization.  Sets up the global event handlers
17816          * @method _onLoad
17817          * @private
17818          * @static
17819          */
17820         _onLoad: function() {
17821
17822             this.init();
17823
17824             if (!Roo.isTouch) {
17825                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17826                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17827             }
17828             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17829             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17830             
17831             Event.on(window,   "unload",    this._onUnload, this, true);
17832             Event.on(window,   "resize",    this._onResize, this, true);
17833             // Event.on(window,   "mouseout",    this._test);
17834
17835         },
17836
17837         /**
17838          * Reset constraints on all drag and drop objs
17839          * @method _onResize
17840          * @private
17841          * @static
17842          */
17843         _onResize: function(e) {
17844             this._execOnAll("resetConstraints", []);
17845         },
17846
17847         /**
17848          * Lock all drag and drop functionality
17849          * @method lock
17850          * @static
17851          */
17852         lock: function() { this.locked = true; },
17853
17854         /**
17855          * Unlock all drag and drop functionality
17856          * @method unlock
17857          * @static
17858          */
17859         unlock: function() { this.locked = false; },
17860
17861         /**
17862          * Is drag and drop locked?
17863          * @method isLocked
17864          * @return {boolean} True if drag and drop is locked, false otherwise.
17865          * @static
17866          */
17867         isLocked: function() { return this.locked; },
17868
17869         /**
17870          * Location cache that is set for all drag drop objects when a drag is
17871          * initiated, cleared when the drag is finished.
17872          * @property locationCache
17873          * @private
17874          * @static
17875          */
17876         locationCache: {},
17877
17878         /**
17879          * Set useCache to false if you want to force object the lookup of each
17880          * drag and drop linked element constantly during a drag.
17881          * @property useCache
17882          * @type boolean
17883          * @static
17884          */
17885         useCache: true,
17886
17887         /**
17888          * The number of pixels that the mouse needs to move after the
17889          * mousedown before the drag is initiated.  Default=3;
17890          * @property clickPixelThresh
17891          * @type int
17892          * @static
17893          */
17894         clickPixelThresh: 3,
17895
17896         /**
17897          * The number of milliseconds after the mousedown event to initiate the
17898          * drag if we don't get a mouseup event. Default=1000
17899          * @property clickTimeThresh
17900          * @type int
17901          * @static
17902          */
17903         clickTimeThresh: 350,
17904
17905         /**
17906          * Flag that indicates that either the drag pixel threshold or the
17907          * mousdown time threshold has been met
17908          * @property dragThreshMet
17909          * @type boolean
17910          * @private
17911          * @static
17912          */
17913         dragThreshMet: false,
17914
17915         /**
17916          * Timeout used for the click time threshold
17917          * @property clickTimeout
17918          * @type Object
17919          * @private
17920          * @static
17921          */
17922         clickTimeout: null,
17923
17924         /**
17925          * The X position of the mousedown event stored for later use when a
17926          * drag threshold is met.
17927          * @property startX
17928          * @type int
17929          * @private
17930          * @static
17931          */
17932         startX: 0,
17933
17934         /**
17935          * The Y position of the mousedown event stored for later use when a
17936          * drag threshold is met.
17937          * @property startY
17938          * @type int
17939          * @private
17940          * @static
17941          */
17942         startY: 0,
17943
17944         /**
17945          * Each DragDrop instance must be registered with the DragDropMgr.
17946          * This is executed in DragDrop.init()
17947          * @method regDragDrop
17948          * @param {DragDrop} oDD the DragDrop object to register
17949          * @param {String} sGroup the name of the group this element belongs to
17950          * @static
17951          */
17952         regDragDrop: function(oDD, sGroup) {
17953             if (!this.initialized) { this.init(); }
17954
17955             if (!this.ids[sGroup]) {
17956                 this.ids[sGroup] = {};
17957             }
17958             this.ids[sGroup][oDD.id] = oDD;
17959         },
17960
17961         /**
17962          * Removes the supplied dd instance from the supplied group. Executed
17963          * by DragDrop.removeFromGroup, so don't call this function directly.
17964          * @method removeDDFromGroup
17965          * @private
17966          * @static
17967          */
17968         removeDDFromGroup: function(oDD, sGroup) {
17969             if (!this.ids[sGroup]) {
17970                 this.ids[sGroup] = {};
17971             }
17972
17973             var obj = this.ids[sGroup];
17974             if (obj && obj[oDD.id]) {
17975                 delete obj[oDD.id];
17976             }
17977         },
17978
17979         /**
17980          * Unregisters a drag and drop item.  This is executed in
17981          * DragDrop.unreg, use that method instead of calling this directly.
17982          * @method _remove
17983          * @private
17984          * @static
17985          */
17986         _remove: function(oDD) {
17987             for (var g in oDD.groups) {
17988                 if (g && this.ids[g][oDD.id]) {
17989                     delete this.ids[g][oDD.id];
17990                 }
17991             }
17992             delete this.handleIds[oDD.id];
17993         },
17994
17995         /**
17996          * Each DragDrop handle element must be registered.  This is done
17997          * automatically when executing DragDrop.setHandleElId()
17998          * @method regHandle
17999          * @param {String} sDDId the DragDrop id this element is a handle for
18000          * @param {String} sHandleId the id of the element that is the drag
18001          * handle
18002          * @static
18003          */
18004         regHandle: function(sDDId, sHandleId) {
18005             if (!this.handleIds[sDDId]) {
18006                 this.handleIds[sDDId] = {};
18007             }
18008             this.handleIds[sDDId][sHandleId] = sHandleId;
18009         },
18010
18011         /**
18012          * Utility function to determine if a given element has been
18013          * registered as a drag drop item.
18014          * @method isDragDrop
18015          * @param {String} id the element id to check
18016          * @return {boolean} true if this element is a DragDrop item,
18017          * false otherwise
18018          * @static
18019          */
18020         isDragDrop: function(id) {
18021             return ( this.getDDById(id) ) ? true : false;
18022         },
18023
18024         /**
18025          * Returns the drag and drop instances that are in all groups the
18026          * passed in instance belongs to.
18027          * @method getRelated
18028          * @param {DragDrop} p_oDD the obj to get related data for
18029          * @param {boolean} bTargetsOnly if true, only return targetable objs
18030          * @return {DragDrop[]} the related instances
18031          * @static
18032          */
18033         getRelated: function(p_oDD, bTargetsOnly) {
18034             var oDDs = [];
18035             for (var i in p_oDD.groups) {
18036                 for (j in this.ids[i]) {
18037                     var dd = this.ids[i][j];
18038                     if (! this.isTypeOfDD(dd)) {
18039                         continue;
18040                     }
18041                     if (!bTargetsOnly || dd.isTarget) {
18042                         oDDs[oDDs.length] = dd;
18043                     }
18044                 }
18045             }
18046
18047             return oDDs;
18048         },
18049
18050         /**
18051          * Returns true if the specified dd target is a legal target for
18052          * the specifice drag obj
18053          * @method isLegalTarget
18054          * @param {DragDrop} the drag obj
18055          * @param {DragDrop} the target
18056          * @return {boolean} true if the target is a legal target for the
18057          * dd obj
18058          * @static
18059          */
18060         isLegalTarget: function (oDD, oTargetDD) {
18061             var targets = this.getRelated(oDD, true);
18062             for (var i=0, len=targets.length;i<len;++i) {
18063                 if (targets[i].id == oTargetDD.id) {
18064                     return true;
18065                 }
18066             }
18067
18068             return false;
18069         },
18070
18071         /**
18072          * My goal is to be able to transparently determine if an object is
18073          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18074          * returns "object", oDD.constructor.toString() always returns
18075          * "DragDrop" and not the name of the subclass.  So for now it just
18076          * evaluates a well-known variable in DragDrop.
18077          * @method isTypeOfDD
18078          * @param {Object} the object to evaluate
18079          * @return {boolean} true if typeof oDD = DragDrop
18080          * @static
18081          */
18082         isTypeOfDD: function (oDD) {
18083             return (oDD && oDD.__ygDragDrop);
18084         },
18085
18086         /**
18087          * Utility function to determine if a given element has been
18088          * registered as a drag drop handle for the given Drag Drop object.
18089          * @method isHandle
18090          * @param {String} id the element id to check
18091          * @return {boolean} true if this element is a DragDrop handle, false
18092          * otherwise
18093          * @static
18094          */
18095         isHandle: function(sDDId, sHandleId) {
18096             return ( this.handleIds[sDDId] &&
18097                             this.handleIds[sDDId][sHandleId] );
18098         },
18099
18100         /**
18101          * Returns the DragDrop instance for a given id
18102          * @method getDDById
18103          * @param {String} id the id of the DragDrop object
18104          * @return {DragDrop} the drag drop object, null if it is not found
18105          * @static
18106          */
18107         getDDById: function(id) {
18108             for (var i in this.ids) {
18109                 if (this.ids[i][id]) {
18110                     return this.ids[i][id];
18111                 }
18112             }
18113             return null;
18114         },
18115
18116         /**
18117          * Fired after a registered DragDrop object gets the mousedown event.
18118          * Sets up the events required to track the object being dragged
18119          * @method handleMouseDown
18120          * @param {Event} e the event
18121          * @param oDD the DragDrop object being dragged
18122          * @private
18123          * @static
18124          */
18125         handleMouseDown: function(e, oDD) {
18126             if(Roo.QuickTips){
18127                 Roo.QuickTips.disable();
18128             }
18129             this.currentTarget = e.getTarget();
18130
18131             this.dragCurrent = oDD;
18132
18133             var el = oDD.getEl();
18134
18135             // track start position
18136             this.startX = e.getPageX();
18137             this.startY = e.getPageY();
18138
18139             this.deltaX = this.startX - el.offsetLeft;
18140             this.deltaY = this.startY - el.offsetTop;
18141
18142             this.dragThreshMet = false;
18143
18144             this.clickTimeout = setTimeout(
18145                     function() {
18146                         var DDM = Roo.dd.DDM;
18147                         DDM.startDrag(DDM.startX, DDM.startY);
18148                     },
18149                     this.clickTimeThresh );
18150         },
18151
18152         /**
18153          * Fired when either the drag pixel threshol or the mousedown hold
18154          * time threshold has been met.
18155          * @method startDrag
18156          * @param x {int} the X position of the original mousedown
18157          * @param y {int} the Y position of the original mousedown
18158          * @static
18159          */
18160         startDrag: function(x, y) {
18161             clearTimeout(this.clickTimeout);
18162             if (this.dragCurrent) {
18163                 this.dragCurrent.b4StartDrag(x, y);
18164                 this.dragCurrent.startDrag(x, y);
18165             }
18166             this.dragThreshMet = true;
18167         },
18168
18169         /**
18170          * Internal function to handle the mouseup event.  Will be invoked
18171          * from the context of the document.
18172          * @method handleMouseUp
18173          * @param {Event} e the event
18174          * @private
18175          * @static
18176          */
18177         handleMouseUp: function(e) {
18178
18179             if(Roo.QuickTips){
18180                 Roo.QuickTips.enable();
18181             }
18182             if (! this.dragCurrent) {
18183                 return;
18184             }
18185
18186             clearTimeout(this.clickTimeout);
18187
18188             if (this.dragThreshMet) {
18189                 this.fireEvents(e, true);
18190             } else {
18191             }
18192
18193             this.stopDrag(e);
18194
18195             this.stopEvent(e);
18196         },
18197
18198         /**
18199          * Utility to stop event propagation and event default, if these
18200          * features are turned on.
18201          * @method stopEvent
18202          * @param {Event} e the event as returned by this.getEvent()
18203          * @static
18204          */
18205         stopEvent: function(e){
18206             if(this.stopPropagation) {
18207                 e.stopPropagation();
18208             }
18209
18210             if (this.preventDefault) {
18211                 e.preventDefault();
18212             }
18213         },
18214
18215         /**
18216          * Internal function to clean up event handlers after the drag
18217          * operation is complete
18218          * @method stopDrag
18219          * @param {Event} e the event
18220          * @private
18221          * @static
18222          */
18223         stopDrag: function(e) {
18224             // Fire the drag end event for the item that was dragged
18225             if (this.dragCurrent) {
18226                 if (this.dragThreshMet) {
18227                     this.dragCurrent.b4EndDrag(e);
18228                     this.dragCurrent.endDrag(e);
18229                 }
18230
18231                 this.dragCurrent.onMouseUp(e);
18232             }
18233
18234             this.dragCurrent = null;
18235             this.dragOvers = {};
18236         },
18237
18238         /**
18239          * Internal function to handle the mousemove event.  Will be invoked
18240          * from the context of the html element.
18241          *
18242          * @TODO figure out what we can do about mouse events lost when the
18243          * user drags objects beyond the window boundary.  Currently we can
18244          * detect this in internet explorer by verifying that the mouse is
18245          * down during the mousemove event.  Firefox doesn't give us the
18246          * button state on the mousemove event.
18247          * @method handleMouseMove
18248          * @param {Event} e the event
18249          * @private
18250          * @static
18251          */
18252         handleMouseMove: function(e) {
18253             if (! this.dragCurrent) {
18254                 return true;
18255             }
18256
18257             // var button = e.which || e.button;
18258
18259             // check for IE mouseup outside of page boundary
18260             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18261                 this.stopEvent(e);
18262                 return this.handleMouseUp(e);
18263             }
18264
18265             if (!this.dragThreshMet) {
18266                 var diffX = Math.abs(this.startX - e.getPageX());
18267                 var diffY = Math.abs(this.startY - e.getPageY());
18268                 if (diffX > this.clickPixelThresh ||
18269                             diffY > this.clickPixelThresh) {
18270                     this.startDrag(this.startX, this.startY);
18271                 }
18272             }
18273
18274             if (this.dragThreshMet) {
18275                 this.dragCurrent.b4Drag(e);
18276                 this.dragCurrent.onDrag(e);
18277                 if(!this.dragCurrent.moveOnly){
18278                     this.fireEvents(e, false);
18279                 }
18280             }
18281
18282             this.stopEvent(e);
18283
18284             return true;
18285         },
18286
18287         /**
18288          * Iterates over all of the DragDrop elements to find ones we are
18289          * hovering over or dropping on
18290          * @method fireEvents
18291          * @param {Event} e the event
18292          * @param {boolean} isDrop is this a drop op or a mouseover op?
18293          * @private
18294          * @static
18295          */
18296         fireEvents: function(e, isDrop) {
18297             var dc = this.dragCurrent;
18298
18299             // If the user did the mouse up outside of the window, we could
18300             // get here even though we have ended the drag.
18301             if (!dc || dc.isLocked()) {
18302                 return;
18303             }
18304
18305             var pt = e.getPoint();
18306
18307             // cache the previous dragOver array
18308             var oldOvers = [];
18309
18310             var outEvts   = [];
18311             var overEvts  = [];
18312             var dropEvts  = [];
18313             var enterEvts = [];
18314
18315             // Check to see if the object(s) we were hovering over is no longer
18316             // being hovered over so we can fire the onDragOut event
18317             for (var i in this.dragOvers) {
18318
18319                 var ddo = this.dragOvers[i];
18320
18321                 if (! this.isTypeOfDD(ddo)) {
18322                     continue;
18323                 }
18324
18325                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18326                     outEvts.push( ddo );
18327                 }
18328
18329                 oldOvers[i] = true;
18330                 delete this.dragOvers[i];
18331             }
18332
18333             for (var sGroup in dc.groups) {
18334
18335                 if ("string" != typeof sGroup) {
18336                     continue;
18337                 }
18338
18339                 for (i in this.ids[sGroup]) {
18340                     var oDD = this.ids[sGroup][i];
18341                     if (! this.isTypeOfDD(oDD)) {
18342                         continue;
18343                     }
18344
18345                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18346                         if (this.isOverTarget(pt, oDD, this.mode)) {
18347                             // look for drop interactions
18348                             if (isDrop) {
18349                                 dropEvts.push( oDD );
18350                             // look for drag enter and drag over interactions
18351                             } else {
18352
18353                                 // initial drag over: dragEnter fires
18354                                 if (!oldOvers[oDD.id]) {
18355                                     enterEvts.push( oDD );
18356                                 // subsequent drag overs: dragOver fires
18357                                 } else {
18358                                     overEvts.push( oDD );
18359                                 }
18360
18361                                 this.dragOvers[oDD.id] = oDD;
18362                             }
18363                         }
18364                     }
18365                 }
18366             }
18367
18368             if (this.mode) {
18369                 if (outEvts.length) {
18370                     dc.b4DragOut(e, outEvts);
18371                     dc.onDragOut(e, outEvts);
18372                 }
18373
18374                 if (enterEvts.length) {
18375                     dc.onDragEnter(e, enterEvts);
18376                 }
18377
18378                 if (overEvts.length) {
18379                     dc.b4DragOver(e, overEvts);
18380                     dc.onDragOver(e, overEvts);
18381                 }
18382
18383                 if (dropEvts.length) {
18384                     dc.b4DragDrop(e, dropEvts);
18385                     dc.onDragDrop(e, dropEvts);
18386                 }
18387
18388             } else {
18389                 // fire dragout events
18390                 var len = 0;
18391                 for (i=0, len=outEvts.length; i<len; ++i) {
18392                     dc.b4DragOut(e, outEvts[i].id);
18393                     dc.onDragOut(e, outEvts[i].id);
18394                 }
18395
18396                 // fire enter events
18397                 for (i=0,len=enterEvts.length; i<len; ++i) {
18398                     // dc.b4DragEnter(e, oDD.id);
18399                     dc.onDragEnter(e, enterEvts[i].id);
18400                 }
18401
18402                 // fire over events
18403                 for (i=0,len=overEvts.length; i<len; ++i) {
18404                     dc.b4DragOver(e, overEvts[i].id);
18405                     dc.onDragOver(e, overEvts[i].id);
18406                 }
18407
18408                 // fire drop events
18409                 for (i=0, len=dropEvts.length; i<len; ++i) {
18410                     dc.b4DragDrop(e, dropEvts[i].id);
18411                     dc.onDragDrop(e, dropEvts[i].id);
18412                 }
18413
18414             }
18415
18416             // notify about a drop that did not find a target
18417             if (isDrop && !dropEvts.length) {
18418                 dc.onInvalidDrop(e);
18419             }
18420
18421         },
18422
18423         /**
18424          * Helper function for getting the best match from the list of drag
18425          * and drop objects returned by the drag and drop events when we are
18426          * in INTERSECT mode.  It returns either the first object that the
18427          * cursor is over, or the object that has the greatest overlap with
18428          * the dragged element.
18429          * @method getBestMatch
18430          * @param  {DragDrop[]} dds The array of drag and drop objects
18431          * targeted
18432          * @return {DragDrop}       The best single match
18433          * @static
18434          */
18435         getBestMatch: function(dds) {
18436             var winner = null;
18437             // Return null if the input is not what we expect
18438             //if (!dds || !dds.length || dds.length == 0) {
18439                // winner = null;
18440             // If there is only one item, it wins
18441             //} else if (dds.length == 1) {
18442
18443             var len = dds.length;
18444
18445             if (len == 1) {
18446                 winner = dds[0];
18447             } else {
18448                 // Loop through the targeted items
18449                 for (var i=0; i<len; ++i) {
18450                     var dd = dds[i];
18451                     // If the cursor is over the object, it wins.  If the
18452                     // cursor is over multiple matches, the first one we come
18453                     // to wins.
18454                     if (dd.cursorIsOver) {
18455                         winner = dd;
18456                         break;
18457                     // Otherwise the object with the most overlap wins
18458                     } else {
18459                         if (!winner ||
18460                             winner.overlap.getArea() < dd.overlap.getArea()) {
18461                             winner = dd;
18462                         }
18463                     }
18464                 }
18465             }
18466
18467             return winner;
18468         },
18469
18470         /**
18471          * Refreshes the cache of the top-left and bottom-right points of the
18472          * drag and drop objects in the specified group(s).  This is in the
18473          * format that is stored in the drag and drop instance, so typical
18474          * usage is:
18475          * <code>
18476          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18477          * </code>
18478          * Alternatively:
18479          * <code>
18480          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18481          * </code>
18482          * @TODO this really should be an indexed array.  Alternatively this
18483          * method could accept both.
18484          * @method refreshCache
18485          * @param {Object} groups an associative array of groups to refresh
18486          * @static
18487          */
18488         refreshCache: function(groups) {
18489             for (var sGroup in groups) {
18490                 if ("string" != typeof sGroup) {
18491                     continue;
18492                 }
18493                 for (var i in this.ids[sGroup]) {
18494                     var oDD = this.ids[sGroup][i];
18495
18496                     if (this.isTypeOfDD(oDD)) {
18497                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18498                         var loc = this.getLocation(oDD);
18499                         if (loc) {
18500                             this.locationCache[oDD.id] = loc;
18501                         } else {
18502                             delete this.locationCache[oDD.id];
18503                             // this will unregister the drag and drop object if
18504                             // the element is not in a usable state
18505                             // oDD.unreg();
18506                         }
18507                     }
18508                 }
18509             }
18510         },
18511
18512         /**
18513          * This checks to make sure an element exists and is in the DOM.  The
18514          * main purpose is to handle cases where innerHTML is used to remove
18515          * drag and drop objects from the DOM.  IE provides an 'unspecified
18516          * error' when trying to access the offsetParent of such an element
18517          * @method verifyEl
18518          * @param {HTMLElement} el the element to check
18519          * @return {boolean} true if the element looks usable
18520          * @static
18521          */
18522         verifyEl: function(el) {
18523             if (el) {
18524                 var parent;
18525                 if(Roo.isIE){
18526                     try{
18527                         parent = el.offsetParent;
18528                     }catch(e){}
18529                 }else{
18530                     parent = el.offsetParent;
18531                 }
18532                 if (parent) {
18533                     return true;
18534                 }
18535             }
18536
18537             return false;
18538         },
18539
18540         /**
18541          * Returns a Region object containing the drag and drop element's position
18542          * and size, including the padding configured for it
18543          * @method getLocation
18544          * @param {DragDrop} oDD the drag and drop object to get the
18545          *                       location for
18546          * @return {Roo.lib.Region} a Region object representing the total area
18547          *                             the element occupies, including any padding
18548          *                             the instance is configured for.
18549          * @static
18550          */
18551         getLocation: function(oDD) {
18552             if (! this.isTypeOfDD(oDD)) {
18553                 return null;
18554             }
18555
18556             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18557
18558             try {
18559                 pos= Roo.lib.Dom.getXY(el);
18560             } catch (e) { }
18561
18562             if (!pos) {
18563                 return null;
18564             }
18565
18566             x1 = pos[0];
18567             x2 = x1 + el.offsetWidth;
18568             y1 = pos[1];
18569             y2 = y1 + el.offsetHeight;
18570
18571             t = y1 - oDD.padding[0];
18572             r = x2 + oDD.padding[1];
18573             b = y2 + oDD.padding[2];
18574             l = x1 - oDD.padding[3];
18575
18576             return new Roo.lib.Region( t, r, b, l );
18577         },
18578
18579         /**
18580          * Checks the cursor location to see if it over the target
18581          * @method isOverTarget
18582          * @param {Roo.lib.Point} pt The point to evaluate
18583          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18584          * @return {boolean} true if the mouse is over the target
18585          * @private
18586          * @static
18587          */
18588         isOverTarget: function(pt, oTarget, intersect) {
18589             // use cache if available
18590             var loc = this.locationCache[oTarget.id];
18591             if (!loc || !this.useCache) {
18592                 loc = this.getLocation(oTarget);
18593                 this.locationCache[oTarget.id] = loc;
18594
18595             }
18596
18597             if (!loc) {
18598                 return false;
18599             }
18600
18601             oTarget.cursorIsOver = loc.contains( pt );
18602
18603             // DragDrop is using this as a sanity check for the initial mousedown
18604             // in this case we are done.  In POINT mode, if the drag obj has no
18605             // contraints, we are also done. Otherwise we need to evaluate the
18606             // location of the target as related to the actual location of the
18607             // dragged element.
18608             var dc = this.dragCurrent;
18609             if (!dc || !dc.getTargetCoord ||
18610                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18611                 return oTarget.cursorIsOver;
18612             }
18613
18614             oTarget.overlap = null;
18615
18616             // Get the current location of the drag element, this is the
18617             // location of the mouse event less the delta that represents
18618             // where the original mousedown happened on the element.  We
18619             // need to consider constraints and ticks as well.
18620             var pos = dc.getTargetCoord(pt.x, pt.y);
18621
18622             var el = dc.getDragEl();
18623             var curRegion = new Roo.lib.Region( pos.y,
18624                                                    pos.x + el.offsetWidth,
18625                                                    pos.y + el.offsetHeight,
18626                                                    pos.x );
18627
18628             var overlap = curRegion.intersect(loc);
18629
18630             if (overlap) {
18631                 oTarget.overlap = overlap;
18632                 return (intersect) ? true : oTarget.cursorIsOver;
18633             } else {
18634                 return false;
18635             }
18636         },
18637
18638         /**
18639          * unload event handler
18640          * @method _onUnload
18641          * @private
18642          * @static
18643          */
18644         _onUnload: function(e, me) {
18645             Roo.dd.DragDropMgr.unregAll();
18646         },
18647
18648         /**
18649          * Cleans up the drag and drop events and objects.
18650          * @method unregAll
18651          * @private
18652          * @static
18653          */
18654         unregAll: function() {
18655
18656             if (this.dragCurrent) {
18657                 this.stopDrag();
18658                 this.dragCurrent = null;
18659             }
18660
18661             this._execOnAll("unreg", []);
18662
18663             for (i in this.elementCache) {
18664                 delete this.elementCache[i];
18665             }
18666
18667             this.elementCache = {};
18668             this.ids = {};
18669         },
18670
18671         /**
18672          * A cache of DOM elements
18673          * @property elementCache
18674          * @private
18675          * @static
18676          */
18677         elementCache: {},
18678
18679         /**
18680          * Get the wrapper for the DOM element specified
18681          * @method getElWrapper
18682          * @param {String} id the id of the element to get
18683          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18684          * @private
18685          * @deprecated This wrapper isn't that useful
18686          * @static
18687          */
18688         getElWrapper: function(id) {
18689             var oWrapper = this.elementCache[id];
18690             if (!oWrapper || !oWrapper.el) {
18691                 oWrapper = this.elementCache[id] =
18692                     new this.ElementWrapper(Roo.getDom(id));
18693             }
18694             return oWrapper;
18695         },
18696
18697         /**
18698          * Returns the actual DOM element
18699          * @method getElement
18700          * @param {String} id the id of the elment to get
18701          * @return {Object} The element
18702          * @deprecated use Roo.getDom instead
18703          * @static
18704          */
18705         getElement: function(id) {
18706             return Roo.getDom(id);
18707         },
18708
18709         /**
18710          * Returns the style property for the DOM element (i.e.,
18711          * document.getElById(id).style)
18712          * @method getCss
18713          * @param {String} id the id of the elment to get
18714          * @return {Object} The style property of the element
18715          * @deprecated use Roo.getDom instead
18716          * @static
18717          */
18718         getCss: function(id) {
18719             var el = Roo.getDom(id);
18720             return (el) ? el.style : null;
18721         },
18722
18723         /**
18724          * Inner class for cached elements
18725          * @class DragDropMgr.ElementWrapper
18726          * @for DragDropMgr
18727          * @private
18728          * @deprecated
18729          */
18730         ElementWrapper: function(el) {
18731                 /**
18732                  * The element
18733                  * @property el
18734                  */
18735                 this.el = el || null;
18736                 /**
18737                  * The element id
18738                  * @property id
18739                  */
18740                 this.id = this.el && el.id;
18741                 /**
18742                  * A reference to the style property
18743                  * @property css
18744                  */
18745                 this.css = this.el && el.style;
18746             },
18747
18748         /**
18749          * Returns the X position of an html element
18750          * @method getPosX
18751          * @param el the element for which to get the position
18752          * @return {int} the X coordinate
18753          * @for DragDropMgr
18754          * @deprecated use Roo.lib.Dom.getX instead
18755          * @static
18756          */
18757         getPosX: function(el) {
18758             return Roo.lib.Dom.getX(el);
18759         },
18760
18761         /**
18762          * Returns the Y position of an html element
18763          * @method getPosY
18764          * @param el the element for which to get the position
18765          * @return {int} the Y coordinate
18766          * @deprecated use Roo.lib.Dom.getY instead
18767          * @static
18768          */
18769         getPosY: function(el) {
18770             return Roo.lib.Dom.getY(el);
18771         },
18772
18773         /**
18774          * Swap two nodes.  In IE, we use the native method, for others we
18775          * emulate the IE behavior
18776          * @method swapNode
18777          * @param n1 the first node to swap
18778          * @param n2 the other node to swap
18779          * @static
18780          */
18781         swapNode: function(n1, n2) {
18782             if (n1.swapNode) {
18783                 n1.swapNode(n2);
18784             } else {
18785                 var p = n2.parentNode;
18786                 var s = n2.nextSibling;
18787
18788                 if (s == n1) {
18789                     p.insertBefore(n1, n2);
18790                 } else if (n2 == n1.nextSibling) {
18791                     p.insertBefore(n2, n1);
18792                 } else {
18793                     n1.parentNode.replaceChild(n2, n1);
18794                     p.insertBefore(n1, s);
18795                 }
18796             }
18797         },
18798
18799         /**
18800          * Returns the current scroll position
18801          * @method getScroll
18802          * @private
18803          * @static
18804          */
18805         getScroll: function () {
18806             var t, l, dde=document.documentElement, db=document.body;
18807             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18808                 t = dde.scrollTop;
18809                 l = dde.scrollLeft;
18810             } else if (db) {
18811                 t = db.scrollTop;
18812                 l = db.scrollLeft;
18813             } else {
18814
18815             }
18816             return { top: t, left: l };
18817         },
18818
18819         /**
18820          * Returns the specified element style property
18821          * @method getStyle
18822          * @param {HTMLElement} el          the element
18823          * @param {string}      styleProp   the style property
18824          * @return {string} The value of the style property
18825          * @deprecated use Roo.lib.Dom.getStyle
18826          * @static
18827          */
18828         getStyle: function(el, styleProp) {
18829             return Roo.fly(el).getStyle(styleProp);
18830         },
18831
18832         /**
18833          * Gets the scrollTop
18834          * @method getScrollTop
18835          * @return {int} the document's scrollTop
18836          * @static
18837          */
18838         getScrollTop: function () { return this.getScroll().top; },
18839
18840         /**
18841          * Gets the scrollLeft
18842          * @method getScrollLeft
18843          * @return {int} the document's scrollTop
18844          * @static
18845          */
18846         getScrollLeft: function () { return this.getScroll().left; },
18847
18848         /**
18849          * Sets the x/y position of an element to the location of the
18850          * target element.
18851          * @method moveToEl
18852          * @param {HTMLElement} moveEl      The element to move
18853          * @param {HTMLElement} targetEl    The position reference element
18854          * @static
18855          */
18856         moveToEl: function (moveEl, targetEl) {
18857             var aCoord = Roo.lib.Dom.getXY(targetEl);
18858             Roo.lib.Dom.setXY(moveEl, aCoord);
18859         },
18860
18861         /**
18862          * Numeric array sort function
18863          * @method numericSort
18864          * @static
18865          */
18866         numericSort: function(a, b) { return (a - b); },
18867
18868         /**
18869          * Internal counter
18870          * @property _timeoutCount
18871          * @private
18872          * @static
18873          */
18874         _timeoutCount: 0,
18875
18876         /**
18877          * Trying to make the load order less important.  Without this we get
18878          * an error if this file is loaded before the Event Utility.
18879          * @method _addListeners
18880          * @private
18881          * @static
18882          */
18883         _addListeners: function() {
18884             var DDM = Roo.dd.DDM;
18885             if ( Roo.lib.Event && document ) {
18886                 DDM._onLoad();
18887             } else {
18888                 if (DDM._timeoutCount > 2000) {
18889                 } else {
18890                     setTimeout(DDM._addListeners, 10);
18891                     if (document && document.body) {
18892                         DDM._timeoutCount += 1;
18893                     }
18894                 }
18895             }
18896         },
18897
18898         /**
18899          * Recursively searches the immediate parent and all child nodes for
18900          * the handle element in order to determine wheter or not it was
18901          * clicked.
18902          * @method handleWasClicked
18903          * @param node the html element to inspect
18904          * @static
18905          */
18906         handleWasClicked: function(node, id) {
18907             if (this.isHandle(id, node.id)) {
18908                 return true;
18909             } else {
18910                 // check to see if this is a text node child of the one we want
18911                 var p = node.parentNode;
18912
18913                 while (p) {
18914                     if (this.isHandle(id, p.id)) {
18915                         return true;
18916                     } else {
18917                         p = p.parentNode;
18918                     }
18919                 }
18920             }
18921
18922             return false;
18923         }
18924
18925     };
18926
18927 }();
18928
18929 // shorter alias, save a few bytes
18930 Roo.dd.DDM = Roo.dd.DragDropMgr;
18931 Roo.dd.DDM._addListeners();
18932
18933 }/*
18934  * Based on:
18935  * Ext JS Library 1.1.1
18936  * Copyright(c) 2006-2007, Ext JS, LLC.
18937  *
18938  * Originally Released Under LGPL - original licence link has changed is not relivant.
18939  *
18940  * Fork - LGPL
18941  * <script type="text/javascript">
18942  */
18943
18944 /**
18945  * @class Roo.dd.DD
18946  * A DragDrop implementation where the linked element follows the
18947  * mouse cursor during a drag.
18948  * @extends Roo.dd.DragDrop
18949  * @constructor
18950  * @param {String} id the id of the linked element
18951  * @param {String} sGroup the group of related DragDrop items
18952  * @param {object} config an object containing configurable attributes
18953  *                Valid properties for DD:
18954  *                    scroll
18955  */
18956 Roo.dd.DD = function(id, sGroup, config) {
18957     if (id) {
18958         this.init(id, sGroup, config);
18959     }
18960 };
18961
18962 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18963
18964     /**
18965      * When set to true, the utility automatically tries to scroll the browser
18966      * window wehn a drag and drop element is dragged near the viewport boundary.
18967      * Defaults to true.
18968      * @property scroll
18969      * @type boolean
18970      */
18971     scroll: true,
18972
18973     /**
18974      * Sets the pointer offset to the distance between the linked element's top
18975      * left corner and the location the element was clicked
18976      * @method autoOffset
18977      * @param {int} iPageX the X coordinate of the click
18978      * @param {int} iPageY the Y coordinate of the click
18979      */
18980     autoOffset: function(iPageX, iPageY) {
18981         var x = iPageX - this.startPageX;
18982         var y = iPageY - this.startPageY;
18983         this.setDelta(x, y);
18984     },
18985
18986     /**
18987      * Sets the pointer offset.  You can call this directly to force the
18988      * offset to be in a particular location (e.g., pass in 0,0 to set it
18989      * to the center of the object)
18990      * @method setDelta
18991      * @param {int} iDeltaX the distance from the left
18992      * @param {int} iDeltaY the distance from the top
18993      */
18994     setDelta: function(iDeltaX, iDeltaY) {
18995         this.deltaX = iDeltaX;
18996         this.deltaY = iDeltaY;
18997     },
18998
18999     /**
19000      * Sets the drag element to the location of the mousedown or click event,
19001      * maintaining the cursor location relative to the location on the element
19002      * that was clicked.  Override this if you want to place the element in a
19003      * location other than where the cursor is.
19004      * @method setDragElPos
19005      * @param {int} iPageX the X coordinate of the mousedown or drag event
19006      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19007      */
19008     setDragElPos: function(iPageX, iPageY) {
19009         // the first time we do this, we are going to check to make sure
19010         // the element has css positioning
19011
19012         var el = this.getDragEl();
19013         this.alignElWithMouse(el, iPageX, iPageY);
19014     },
19015
19016     /**
19017      * Sets the element to the location of the mousedown or click event,
19018      * maintaining the cursor location relative to the location on the element
19019      * that was clicked.  Override this if you want to place the element in a
19020      * location other than where the cursor is.
19021      * @method alignElWithMouse
19022      * @param {HTMLElement} el the element to move
19023      * @param {int} iPageX the X coordinate of the mousedown or drag event
19024      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19025      */
19026     alignElWithMouse: function(el, iPageX, iPageY) {
19027         var oCoord = this.getTargetCoord(iPageX, iPageY);
19028         var fly = el.dom ? el : Roo.fly(el);
19029         if (!this.deltaSetXY) {
19030             var aCoord = [oCoord.x, oCoord.y];
19031             fly.setXY(aCoord);
19032             var newLeft = fly.getLeft(true);
19033             var newTop  = fly.getTop(true);
19034             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19035         } else {
19036             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19037         }
19038
19039         this.cachePosition(oCoord.x, oCoord.y);
19040         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19041         return oCoord;
19042     },
19043
19044     /**
19045      * Saves the most recent position so that we can reset the constraints and
19046      * tick marks on-demand.  We need to know this so that we can calculate the
19047      * number of pixels the element is offset from its original position.
19048      * @method cachePosition
19049      * @param iPageX the current x position (optional, this just makes it so we
19050      * don't have to look it up again)
19051      * @param iPageY the current y position (optional, this just makes it so we
19052      * don't have to look it up again)
19053      */
19054     cachePosition: function(iPageX, iPageY) {
19055         if (iPageX) {
19056             this.lastPageX = iPageX;
19057             this.lastPageY = iPageY;
19058         } else {
19059             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19060             this.lastPageX = aCoord[0];
19061             this.lastPageY = aCoord[1];
19062         }
19063     },
19064
19065     /**
19066      * Auto-scroll the window if the dragged object has been moved beyond the
19067      * visible window boundary.
19068      * @method autoScroll
19069      * @param {int} x the drag element's x position
19070      * @param {int} y the drag element's y position
19071      * @param {int} h the height of the drag element
19072      * @param {int} w the width of the drag element
19073      * @private
19074      */
19075     autoScroll: function(x, y, h, w) {
19076
19077         if (this.scroll) {
19078             // The client height
19079             var clientH = Roo.lib.Dom.getViewWidth();
19080
19081             // The client width
19082             var clientW = Roo.lib.Dom.getViewHeight();
19083
19084             // The amt scrolled down
19085             var st = this.DDM.getScrollTop();
19086
19087             // The amt scrolled right
19088             var sl = this.DDM.getScrollLeft();
19089
19090             // Location of the bottom of the element
19091             var bot = h + y;
19092
19093             // Location of the right of the element
19094             var right = w + x;
19095
19096             // The distance from the cursor to the bottom of the visible area,
19097             // adjusted so that we don't scroll if the cursor is beyond the
19098             // element drag constraints
19099             var toBot = (clientH + st - y - this.deltaY);
19100
19101             // The distance from the cursor to the right of the visible area
19102             var toRight = (clientW + sl - x - this.deltaX);
19103
19104
19105             // How close to the edge the cursor must be before we scroll
19106             // var thresh = (document.all) ? 100 : 40;
19107             var thresh = 40;
19108
19109             // How many pixels to scroll per autoscroll op.  This helps to reduce
19110             // clunky scrolling. IE is more sensitive about this ... it needs this
19111             // value to be higher.
19112             var scrAmt = (document.all) ? 80 : 30;
19113
19114             // Scroll down if we are near the bottom of the visible page and the
19115             // obj extends below the crease
19116             if ( bot > clientH && toBot < thresh ) {
19117                 window.scrollTo(sl, st + scrAmt);
19118             }
19119
19120             // Scroll up if the window is scrolled down and the top of the object
19121             // goes above the top border
19122             if ( y < st && st > 0 && y - st < thresh ) {
19123                 window.scrollTo(sl, st - scrAmt);
19124             }
19125
19126             // Scroll right if the obj is beyond the right border and the cursor is
19127             // near the border.
19128             if ( right > clientW && toRight < thresh ) {
19129                 window.scrollTo(sl + scrAmt, st);
19130             }
19131
19132             // Scroll left if the window has been scrolled to the right and the obj
19133             // extends past the left border
19134             if ( x < sl && sl > 0 && x - sl < thresh ) {
19135                 window.scrollTo(sl - scrAmt, st);
19136             }
19137         }
19138     },
19139
19140     /**
19141      * Finds the location the element should be placed if we want to move
19142      * it to where the mouse location less the click offset would place us.
19143      * @method getTargetCoord
19144      * @param {int} iPageX the X coordinate of the click
19145      * @param {int} iPageY the Y coordinate of the click
19146      * @return an object that contains the coordinates (Object.x and Object.y)
19147      * @private
19148      */
19149     getTargetCoord: function(iPageX, iPageY) {
19150
19151
19152         var x = iPageX - this.deltaX;
19153         var y = iPageY - this.deltaY;
19154
19155         if (this.constrainX) {
19156             if (x < this.minX) { x = this.minX; }
19157             if (x > this.maxX) { x = this.maxX; }
19158         }
19159
19160         if (this.constrainY) {
19161             if (y < this.minY) { y = this.minY; }
19162             if (y > this.maxY) { y = this.maxY; }
19163         }
19164
19165         x = this.getTick(x, this.xTicks);
19166         y = this.getTick(y, this.yTicks);
19167
19168
19169         return {x:x, y:y};
19170     },
19171
19172     /*
19173      * Sets up config options specific to this class. Overrides
19174      * Roo.dd.DragDrop, but all versions of this method through the
19175      * inheritance chain are called
19176      */
19177     applyConfig: function() {
19178         Roo.dd.DD.superclass.applyConfig.call(this);
19179         this.scroll = (this.config.scroll !== false);
19180     },
19181
19182     /*
19183      * Event that fires prior to the onMouseDown event.  Overrides
19184      * Roo.dd.DragDrop.
19185      */
19186     b4MouseDown: function(e) {
19187         // this.resetConstraints();
19188         this.autoOffset(e.getPageX(),
19189                             e.getPageY());
19190     },
19191
19192     /*
19193      * Event that fires prior to the onDrag event.  Overrides
19194      * Roo.dd.DragDrop.
19195      */
19196     b4Drag: function(e) {
19197         this.setDragElPos(e.getPageX(),
19198                             e.getPageY());
19199     },
19200
19201     toString: function() {
19202         return ("DD " + this.id);
19203     }
19204
19205     //////////////////////////////////////////////////////////////////////////
19206     // Debugging ygDragDrop events that can be overridden
19207     //////////////////////////////////////////////////////////////////////////
19208     /*
19209     startDrag: function(x, y) {
19210     },
19211
19212     onDrag: function(e) {
19213     },
19214
19215     onDragEnter: function(e, id) {
19216     },
19217
19218     onDragOver: function(e, id) {
19219     },
19220
19221     onDragOut: function(e, id) {
19222     },
19223
19224     onDragDrop: function(e, id) {
19225     },
19226
19227     endDrag: function(e) {
19228     }
19229
19230     */
19231
19232 });/*
19233  * Based on:
19234  * Ext JS Library 1.1.1
19235  * Copyright(c) 2006-2007, Ext JS, LLC.
19236  *
19237  * Originally Released Under LGPL - original licence link has changed is not relivant.
19238  *
19239  * Fork - LGPL
19240  * <script type="text/javascript">
19241  */
19242
19243 /**
19244  * @class Roo.dd.DDProxy
19245  * A DragDrop implementation that inserts an empty, bordered div into
19246  * the document that follows the cursor during drag operations.  At the time of
19247  * the click, the frame div is resized to the dimensions of the linked html
19248  * element, and moved to the exact location of the linked element.
19249  *
19250  * References to the "frame" element refer to the single proxy element that
19251  * was created to be dragged in place of all DDProxy elements on the
19252  * page.
19253  *
19254  * @extends Roo.dd.DD
19255  * @constructor
19256  * @param {String} id the id of the linked html element
19257  * @param {String} sGroup the group of related DragDrop objects
19258  * @param {object} config an object containing configurable attributes
19259  *                Valid properties for DDProxy in addition to those in DragDrop:
19260  *                   resizeFrame, centerFrame, dragElId
19261  */
19262 Roo.dd.DDProxy = function(id, sGroup, config) {
19263     if (id) {
19264         this.init(id, sGroup, config);
19265         this.initFrame();
19266     }
19267 };
19268
19269 /**
19270  * The default drag frame div id
19271  * @property Roo.dd.DDProxy.dragElId
19272  * @type String
19273  * @static
19274  */
19275 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19276
19277 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19278
19279     /**
19280      * By default we resize the drag frame to be the same size as the element
19281      * we want to drag (this is to get the frame effect).  We can turn it off
19282      * if we want a different behavior.
19283      * @property resizeFrame
19284      * @type boolean
19285      */
19286     resizeFrame: true,
19287
19288     /**
19289      * By default the frame is positioned exactly where the drag element is, so
19290      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19291      * you do not have constraints on the obj is to have the drag frame centered
19292      * around the cursor.  Set centerFrame to true for this effect.
19293      * @property centerFrame
19294      * @type boolean
19295      */
19296     centerFrame: false,
19297
19298     /**
19299      * Creates the proxy element if it does not yet exist
19300      * @method createFrame
19301      */
19302     createFrame: function() {
19303         var self = this;
19304         var body = document.body;
19305
19306         if (!body || !body.firstChild) {
19307             setTimeout( function() { self.createFrame(); }, 50 );
19308             return;
19309         }
19310
19311         var div = this.getDragEl();
19312
19313         if (!div) {
19314             div    = document.createElement("div");
19315             div.id = this.dragElId;
19316             var s  = div.style;
19317
19318             s.position   = "absolute";
19319             s.visibility = "hidden";
19320             s.cursor     = "move";
19321             s.border     = "2px solid #aaa";
19322             s.zIndex     = 999;
19323
19324             // appendChild can blow up IE if invoked prior to the window load event
19325             // while rendering a table.  It is possible there are other scenarios
19326             // that would cause this to happen as well.
19327             body.insertBefore(div, body.firstChild);
19328         }
19329     },
19330
19331     /**
19332      * Initialization for the drag frame element.  Must be called in the
19333      * constructor of all subclasses
19334      * @method initFrame
19335      */
19336     initFrame: function() {
19337         this.createFrame();
19338     },
19339
19340     applyConfig: function() {
19341         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19342
19343         this.resizeFrame = (this.config.resizeFrame !== false);
19344         this.centerFrame = (this.config.centerFrame);
19345         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19346     },
19347
19348     /**
19349      * Resizes the drag frame to the dimensions of the clicked object, positions
19350      * it over the object, and finally displays it
19351      * @method showFrame
19352      * @param {int} iPageX X click position
19353      * @param {int} iPageY Y click position
19354      * @private
19355      */
19356     showFrame: function(iPageX, iPageY) {
19357         var el = this.getEl();
19358         var dragEl = this.getDragEl();
19359         var s = dragEl.style;
19360
19361         this._resizeProxy();
19362
19363         if (this.centerFrame) {
19364             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19365                            Math.round(parseInt(s.height, 10)/2) );
19366         }
19367
19368         this.setDragElPos(iPageX, iPageY);
19369
19370         Roo.fly(dragEl).show();
19371     },
19372
19373     /**
19374      * The proxy is automatically resized to the dimensions of the linked
19375      * element when a drag is initiated, unless resizeFrame is set to false
19376      * @method _resizeProxy
19377      * @private
19378      */
19379     _resizeProxy: function() {
19380         if (this.resizeFrame) {
19381             var el = this.getEl();
19382             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19383         }
19384     },
19385
19386     // overrides Roo.dd.DragDrop
19387     b4MouseDown: function(e) {
19388         var x = e.getPageX();
19389         var y = e.getPageY();
19390         this.autoOffset(x, y);
19391         this.setDragElPos(x, y);
19392     },
19393
19394     // overrides Roo.dd.DragDrop
19395     b4StartDrag: function(x, y) {
19396         // show the drag frame
19397         this.showFrame(x, y);
19398     },
19399
19400     // overrides Roo.dd.DragDrop
19401     b4EndDrag: function(e) {
19402         Roo.fly(this.getDragEl()).hide();
19403     },
19404
19405     // overrides Roo.dd.DragDrop
19406     // By default we try to move the element to the last location of the frame.
19407     // This is so that the default behavior mirrors that of Roo.dd.DD.
19408     endDrag: function(e) {
19409
19410         var lel = this.getEl();
19411         var del = this.getDragEl();
19412
19413         // Show the drag frame briefly so we can get its position
19414         del.style.visibility = "";
19415
19416         this.beforeMove();
19417         // Hide the linked element before the move to get around a Safari
19418         // rendering bug.
19419         lel.style.visibility = "hidden";
19420         Roo.dd.DDM.moveToEl(lel, del);
19421         del.style.visibility = "hidden";
19422         lel.style.visibility = "";
19423
19424         this.afterDrag();
19425     },
19426
19427     beforeMove : function(){
19428
19429     },
19430
19431     afterDrag : function(){
19432
19433     },
19434
19435     toString: function() {
19436         return ("DDProxy " + this.id);
19437     }
19438
19439 });
19440 /*
19441  * Based on:
19442  * Ext JS Library 1.1.1
19443  * Copyright(c) 2006-2007, Ext JS, LLC.
19444  *
19445  * Originally Released Under LGPL - original licence link has changed is not relivant.
19446  *
19447  * Fork - LGPL
19448  * <script type="text/javascript">
19449  */
19450
19451  /**
19452  * @class Roo.dd.DDTarget
19453  * A DragDrop implementation that does not move, but can be a drop
19454  * target.  You would get the same result by simply omitting implementation
19455  * for the event callbacks, but this way we reduce the processing cost of the
19456  * event listener and the callbacks.
19457  * @extends Roo.dd.DragDrop
19458  * @constructor
19459  * @param {String} id the id of the element that is a drop target
19460  * @param {String} sGroup the group of related DragDrop objects
19461  * @param {object} config an object containing configurable attributes
19462  *                 Valid properties for DDTarget in addition to those in
19463  *                 DragDrop:
19464  *                    none
19465  */
19466 Roo.dd.DDTarget = function(id, sGroup, config) {
19467     if (id) {
19468         this.initTarget(id, sGroup, config);
19469     }
19470     if (config.listeners || config.events) { 
19471        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19472             listeners : config.listeners || {}, 
19473             events : config.events || {} 
19474         });    
19475     }
19476 };
19477
19478 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19479 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19480     toString: function() {
19481         return ("DDTarget " + this.id);
19482     }
19483 });
19484 /*
19485  * Based on:
19486  * Ext JS Library 1.1.1
19487  * Copyright(c) 2006-2007, Ext JS, LLC.
19488  *
19489  * Originally Released Under LGPL - original licence link has changed is not relivant.
19490  *
19491  * Fork - LGPL
19492  * <script type="text/javascript">
19493  */
19494  
19495
19496 /**
19497  * @class Roo.dd.ScrollManager
19498  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19499  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19500  * @singleton
19501  */
19502 Roo.dd.ScrollManager = function(){
19503     var ddm = Roo.dd.DragDropMgr;
19504     var els = {};
19505     var dragEl = null;
19506     var proc = {};
19507     
19508     
19509     
19510     var onStop = function(e){
19511         dragEl = null;
19512         clearProc();
19513     };
19514     
19515     var triggerRefresh = function(){
19516         if(ddm.dragCurrent){
19517              ddm.refreshCache(ddm.dragCurrent.groups);
19518         }
19519     };
19520     
19521     var doScroll = function(){
19522         if(ddm.dragCurrent){
19523             var dds = Roo.dd.ScrollManager;
19524             if(!dds.animate){
19525                 if(proc.el.scroll(proc.dir, dds.increment)){
19526                     triggerRefresh();
19527                 }
19528             }else{
19529                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19530             }
19531         }
19532     };
19533     
19534     var clearProc = function(){
19535         if(proc.id){
19536             clearInterval(proc.id);
19537         }
19538         proc.id = 0;
19539         proc.el = null;
19540         proc.dir = "";
19541     };
19542     
19543     var startProc = function(el, dir){
19544          Roo.log('scroll startproc');
19545         clearProc();
19546         proc.el = el;
19547         proc.dir = dir;
19548         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19549     };
19550     
19551     var onFire = function(e, isDrop){
19552        
19553         if(isDrop || !ddm.dragCurrent){ return; }
19554         var dds = Roo.dd.ScrollManager;
19555         if(!dragEl || dragEl != ddm.dragCurrent){
19556             dragEl = ddm.dragCurrent;
19557             // refresh regions on drag start
19558             dds.refreshCache();
19559         }
19560         
19561         var xy = Roo.lib.Event.getXY(e);
19562         var pt = new Roo.lib.Point(xy[0], xy[1]);
19563         for(var id in els){
19564             var el = els[id], r = el._region;
19565             if(r && r.contains(pt) && el.isScrollable()){
19566                 if(r.bottom - pt.y <= dds.thresh){
19567                     if(proc.el != el){
19568                         startProc(el, "down");
19569                     }
19570                     return;
19571                 }else if(r.right - pt.x <= dds.thresh){
19572                     if(proc.el != el){
19573                         startProc(el, "left");
19574                     }
19575                     return;
19576                 }else if(pt.y - r.top <= dds.thresh){
19577                     if(proc.el != el){
19578                         startProc(el, "up");
19579                     }
19580                     return;
19581                 }else if(pt.x - r.left <= dds.thresh){
19582                     if(proc.el != el){
19583                         startProc(el, "right");
19584                     }
19585                     return;
19586                 }
19587             }
19588         }
19589         clearProc();
19590     };
19591     
19592     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19593     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19594     
19595     return {
19596         /**
19597          * Registers new overflow element(s) to auto scroll
19598          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19599          */
19600         register : function(el){
19601             if(el instanceof Array){
19602                 for(var i = 0, len = el.length; i < len; i++) {
19603                         this.register(el[i]);
19604                 }
19605             }else{
19606                 el = Roo.get(el);
19607                 els[el.id] = el;
19608             }
19609             Roo.dd.ScrollManager.els = els;
19610         },
19611         
19612         /**
19613          * Unregisters overflow element(s) so they are no longer scrolled
19614          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19615          */
19616         unregister : function(el){
19617             if(el instanceof Array){
19618                 for(var i = 0, len = el.length; i < len; i++) {
19619                         this.unregister(el[i]);
19620                 }
19621             }else{
19622                 el = Roo.get(el);
19623                 delete els[el.id];
19624             }
19625         },
19626         
19627         /**
19628          * The number of pixels from the edge of a container the pointer needs to be to 
19629          * trigger scrolling (defaults to 25)
19630          * @type Number
19631          */
19632         thresh : 25,
19633         
19634         /**
19635          * The number of pixels to scroll in each scroll increment (defaults to 50)
19636          * @type Number
19637          */
19638         increment : 100,
19639         
19640         /**
19641          * The frequency of scrolls in milliseconds (defaults to 500)
19642          * @type Number
19643          */
19644         frequency : 500,
19645         
19646         /**
19647          * True to animate the scroll (defaults to true)
19648          * @type Boolean
19649          */
19650         animate: true,
19651         
19652         /**
19653          * The animation duration in seconds - 
19654          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19655          * @type Number
19656          */
19657         animDuration: .4,
19658         
19659         /**
19660          * Manually trigger a cache refresh.
19661          */
19662         refreshCache : function(){
19663             for(var id in els){
19664                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19665                     els[id]._region = els[id].getRegion();
19666                 }
19667             }
19668         }
19669     };
19670 }();/*
19671  * Based on:
19672  * Ext JS Library 1.1.1
19673  * Copyright(c) 2006-2007, Ext JS, LLC.
19674  *
19675  * Originally Released Under LGPL - original licence link has changed is not relivant.
19676  *
19677  * Fork - LGPL
19678  * <script type="text/javascript">
19679  */
19680  
19681
19682 /**
19683  * @class Roo.dd.Registry
19684  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19685  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19686  * @singleton
19687  */
19688 Roo.dd.Registry = function(){
19689     var elements = {}; 
19690     var handles = {}; 
19691     var autoIdSeed = 0;
19692
19693     var getId = function(el, autogen){
19694         if(typeof el == "string"){
19695             return el;
19696         }
19697         var id = el.id;
19698         if(!id && autogen !== false){
19699             id = "roodd-" + (++autoIdSeed);
19700             el.id = id;
19701         }
19702         return id;
19703     };
19704     
19705     return {
19706     /**
19707      * Register a drag drop element
19708      * @param {String|HTMLElement} element The id or DOM node to register
19709      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19710      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19711      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19712      * populated in the data object (if applicable):
19713      * <pre>
19714 Value      Description<br />
19715 ---------  ------------------------------------------<br />
19716 handles    Array of DOM nodes that trigger dragging<br />
19717            for the element being registered<br />
19718 isHandle   True if the element passed in triggers<br />
19719            dragging itself, else false
19720 </pre>
19721      */
19722         register : function(el, data){
19723             data = data || {};
19724             if(typeof el == "string"){
19725                 el = document.getElementById(el);
19726             }
19727             data.ddel = el;
19728             elements[getId(el)] = data;
19729             if(data.isHandle !== false){
19730                 handles[data.ddel.id] = data;
19731             }
19732             if(data.handles){
19733                 var hs = data.handles;
19734                 for(var i = 0, len = hs.length; i < len; i++){
19735                         handles[getId(hs[i])] = data;
19736                 }
19737             }
19738         },
19739
19740     /**
19741      * Unregister a drag drop element
19742      * @param {String|HTMLElement}  element The id or DOM node to unregister
19743      */
19744         unregister : function(el){
19745             var id = getId(el, false);
19746             var data = elements[id];
19747             if(data){
19748                 delete elements[id];
19749                 if(data.handles){
19750                     var hs = data.handles;
19751                     for(var i = 0, len = hs.length; i < len; i++){
19752                         delete handles[getId(hs[i], false)];
19753                     }
19754                 }
19755             }
19756         },
19757
19758     /**
19759      * Returns the handle registered for a DOM Node by id
19760      * @param {String|HTMLElement} id The DOM node or id to look up
19761      * @return {Object} handle The custom handle data
19762      */
19763         getHandle : function(id){
19764             if(typeof id != "string"){ // must be element?
19765                 id = id.id;
19766             }
19767             return handles[id];
19768         },
19769
19770     /**
19771      * Returns the handle that is registered for the DOM node that is the target of the event
19772      * @param {Event} e The event
19773      * @return {Object} handle The custom handle data
19774      */
19775         getHandleFromEvent : function(e){
19776             var t = Roo.lib.Event.getTarget(e);
19777             return t ? handles[t.id] : null;
19778         },
19779
19780     /**
19781      * Returns a custom data object that is registered for a DOM node by id
19782      * @param {String|HTMLElement} id The DOM node or id to look up
19783      * @return {Object} data The custom data
19784      */
19785         getTarget : function(id){
19786             if(typeof id != "string"){ // must be element?
19787                 id = id.id;
19788             }
19789             return elements[id];
19790         },
19791
19792     /**
19793      * Returns a custom data object that is registered for the DOM node that is the target of the event
19794      * @param {Event} e The event
19795      * @return {Object} data The custom data
19796      */
19797         getTargetFromEvent : function(e){
19798             var t = Roo.lib.Event.getTarget(e);
19799             return t ? elements[t.id] || handles[t.id] : null;
19800         }
19801     };
19802 }();/*
19803  * Based on:
19804  * Ext JS Library 1.1.1
19805  * Copyright(c) 2006-2007, Ext JS, LLC.
19806  *
19807  * Originally Released Under LGPL - original licence link has changed is not relivant.
19808  *
19809  * Fork - LGPL
19810  * <script type="text/javascript">
19811  */
19812  
19813
19814 /**
19815  * @class Roo.dd.StatusProxy
19816  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19817  * default drag proxy used by all Roo.dd components.
19818  * @constructor
19819  * @param {Object} config
19820  */
19821 Roo.dd.StatusProxy = function(config){
19822     Roo.apply(this, config);
19823     this.id = this.id || Roo.id();
19824     this.el = new Roo.Layer({
19825         dh: {
19826             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19827                 {tag: "div", cls: "x-dd-drop-icon"},
19828                 {tag: "div", cls: "x-dd-drag-ghost"}
19829             ]
19830         }, 
19831         shadow: !config || config.shadow !== false
19832     });
19833     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19834     this.dropStatus = this.dropNotAllowed;
19835 };
19836
19837 Roo.dd.StatusProxy.prototype = {
19838     /**
19839      * @cfg {String} dropAllowed
19840      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19841      */
19842     dropAllowed : "x-dd-drop-ok",
19843     /**
19844      * @cfg {String} dropNotAllowed
19845      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19846      */
19847     dropNotAllowed : "x-dd-drop-nodrop",
19848
19849     /**
19850      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19851      * over the current target element.
19852      * @param {String} cssClass The css class for the new drop status indicator image
19853      */
19854     setStatus : function(cssClass){
19855         cssClass = cssClass || this.dropNotAllowed;
19856         if(this.dropStatus != cssClass){
19857             this.el.replaceClass(this.dropStatus, cssClass);
19858             this.dropStatus = cssClass;
19859         }
19860     },
19861
19862     /**
19863      * Resets the status indicator to the default dropNotAllowed value
19864      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19865      */
19866     reset : function(clearGhost){
19867         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19868         this.dropStatus = this.dropNotAllowed;
19869         if(clearGhost){
19870             this.ghost.update("");
19871         }
19872     },
19873
19874     /**
19875      * Updates the contents of the ghost element
19876      * @param {String} html The html that will replace the current innerHTML of the ghost element
19877      */
19878     update : function(html){
19879         if(typeof html == "string"){
19880             this.ghost.update(html);
19881         }else{
19882             this.ghost.update("");
19883             html.style.margin = "0";
19884             this.ghost.dom.appendChild(html);
19885         }
19886         // ensure float = none set?? cant remember why though.
19887         var el = this.ghost.dom.firstChild;
19888                 if(el){
19889                         Roo.fly(el).setStyle('float', 'none');
19890                 }
19891     },
19892     
19893     /**
19894      * Returns the underlying proxy {@link Roo.Layer}
19895      * @return {Roo.Layer} el
19896     */
19897     getEl : function(){
19898         return this.el;
19899     },
19900
19901     /**
19902      * Returns the ghost element
19903      * @return {Roo.Element} el
19904      */
19905     getGhost : function(){
19906         return this.ghost;
19907     },
19908
19909     /**
19910      * Hides the proxy
19911      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19912      */
19913     hide : function(clear){
19914         this.el.hide();
19915         if(clear){
19916             this.reset(true);
19917         }
19918     },
19919
19920     /**
19921      * Stops the repair animation if it's currently running
19922      */
19923     stop : function(){
19924         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19925             this.anim.stop();
19926         }
19927     },
19928
19929     /**
19930      * Displays this proxy
19931      */
19932     show : function(){
19933         this.el.show();
19934     },
19935
19936     /**
19937      * Force the Layer to sync its shadow and shim positions to the element
19938      */
19939     sync : function(){
19940         this.el.sync();
19941     },
19942
19943     /**
19944      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19945      * invalid drop operation by the item being dragged.
19946      * @param {Array} xy The XY position of the element ([x, y])
19947      * @param {Function} callback The function to call after the repair is complete
19948      * @param {Object} scope The scope in which to execute the callback
19949      */
19950     repair : function(xy, callback, scope){
19951         this.callback = callback;
19952         this.scope = scope;
19953         if(xy && this.animRepair !== false){
19954             this.el.addClass("x-dd-drag-repair");
19955             this.el.hideUnders(true);
19956             this.anim = this.el.shift({
19957                 duration: this.repairDuration || .5,
19958                 easing: 'easeOut',
19959                 xy: xy,
19960                 stopFx: true,
19961                 callback: this.afterRepair,
19962                 scope: this
19963             });
19964         }else{
19965             this.afterRepair();
19966         }
19967     },
19968
19969     // private
19970     afterRepair : function(){
19971         this.hide(true);
19972         if(typeof this.callback == "function"){
19973             this.callback.call(this.scope || this);
19974         }
19975         this.callback = null;
19976         this.scope = null;
19977     }
19978 };/*
19979  * Based on:
19980  * Ext JS Library 1.1.1
19981  * Copyright(c) 2006-2007, Ext JS, LLC.
19982  *
19983  * Originally Released Under LGPL - original licence link has changed is not relivant.
19984  *
19985  * Fork - LGPL
19986  * <script type="text/javascript">
19987  */
19988
19989 /**
19990  * @class Roo.dd.DragSource
19991  * @extends Roo.dd.DDProxy
19992  * A simple class that provides the basic implementation needed to make any element draggable.
19993  * @constructor
19994  * @param {String/HTMLElement/Element} el The container element
19995  * @param {Object} config
19996  */
19997 Roo.dd.DragSource = function(el, config){
19998     this.el = Roo.get(el);
19999     this.dragData = {};
20000     
20001     Roo.apply(this, config);
20002     
20003     if(!this.proxy){
20004         this.proxy = new Roo.dd.StatusProxy();
20005     }
20006
20007     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20008           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20009     
20010     this.dragging = false;
20011 };
20012
20013 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20014     /**
20015      * @cfg {String} dropAllowed
20016      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20017      */
20018     dropAllowed : "x-dd-drop-ok",
20019     /**
20020      * @cfg {String} dropNotAllowed
20021      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20022      */
20023     dropNotAllowed : "x-dd-drop-nodrop",
20024
20025     /**
20026      * Returns the data object associated with this drag source
20027      * @return {Object} data An object containing arbitrary data
20028      */
20029     getDragData : function(e){
20030         return this.dragData;
20031     },
20032
20033     // private
20034     onDragEnter : function(e, id){
20035         var target = Roo.dd.DragDropMgr.getDDById(id);
20036         this.cachedTarget = target;
20037         if(this.beforeDragEnter(target, e, id) !== false){
20038             if(target.isNotifyTarget){
20039                 var status = target.notifyEnter(this, e, this.dragData);
20040                 this.proxy.setStatus(status);
20041             }else{
20042                 this.proxy.setStatus(this.dropAllowed);
20043             }
20044             
20045             if(this.afterDragEnter){
20046                 /**
20047                  * An empty function by default, but provided so that you can perform a custom action
20048                  * when the dragged item enters the drop target by providing an implementation.
20049                  * @param {Roo.dd.DragDrop} target The drop target
20050                  * @param {Event} e The event object
20051                  * @param {String} id The id of the dragged element
20052                  * @method afterDragEnter
20053                  */
20054                 this.afterDragEnter(target, e, id);
20055             }
20056         }
20057     },
20058
20059     /**
20060      * An empty function by default, but provided so that you can perform a custom action
20061      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20062      * @param {Roo.dd.DragDrop} target The drop target
20063      * @param {Event} e The event object
20064      * @param {String} id The id of the dragged element
20065      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20066      */
20067     beforeDragEnter : function(target, e, id){
20068         return true;
20069     },
20070
20071     // private
20072     alignElWithMouse: function() {
20073         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20074         this.proxy.sync();
20075     },
20076
20077     // private
20078     onDragOver : function(e, id){
20079         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20080         if(this.beforeDragOver(target, e, id) !== false){
20081             if(target.isNotifyTarget){
20082                 var status = target.notifyOver(this, e, this.dragData);
20083                 Roo.log('run here??????????????');
20084                 Roo.log(status);
20085                 this.proxy.setStatus(status);
20086             }
20087
20088             if(this.afterDragOver){
20089                 /**
20090                  * An empty function by default, but provided so that you can perform a custom action
20091                  * while the dragged item is over the drop target by providing an implementation.
20092                  * @param {Roo.dd.DragDrop} target The drop target
20093                  * @param {Event} e The event object
20094                  * @param {String} id The id of the dragged element
20095                  * @method afterDragOver
20096                  */
20097                 this.afterDragOver(target, e, id);
20098             }
20099         }
20100     },
20101
20102     /**
20103      * An empty function by default, but provided so that you can perform a custom action
20104      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20105      * @param {Roo.dd.DragDrop} target The drop target
20106      * @param {Event} e The event object
20107      * @param {String} id The id of the dragged element
20108      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20109      */
20110     beforeDragOver : function(target, e, id){
20111         return true;
20112     },
20113
20114     // private
20115     onDragOut : function(e, id){
20116         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20117         if(this.beforeDragOut(target, e, id) !== false){
20118             if(target.isNotifyTarget){
20119                 target.notifyOut(this, e, this.dragData);
20120             }
20121             this.proxy.reset();
20122             if(this.afterDragOut){
20123                 /**
20124                  * An empty function by default, but provided so that you can perform a custom action
20125                  * after the dragged item is dragged out of the target without dropping.
20126                  * @param {Roo.dd.DragDrop} target The drop target
20127                  * @param {Event} e The event object
20128                  * @param {String} id The id of the dragged element
20129                  * @method afterDragOut
20130                  */
20131                 this.afterDragOut(target, e, id);
20132             }
20133         }
20134         this.cachedTarget = null;
20135     },
20136
20137     /**
20138      * An empty function by default, but provided so that you can perform a custom action before the dragged
20139      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20140      * @param {Roo.dd.DragDrop} target The drop target
20141      * @param {Event} e The event object
20142      * @param {String} id The id of the dragged element
20143      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20144      */
20145     beforeDragOut : function(target, e, id){
20146         return true;
20147     },
20148     
20149     // private
20150     onDragDrop : function(e, id){
20151         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20152         if(this.beforeDragDrop(target, e, id) !== false){
20153             if(target.isNotifyTarget){
20154                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20155                     this.onValidDrop(target, e, id);
20156                 }else{
20157                     this.onInvalidDrop(target, e, id);
20158                 }
20159             }else{
20160                 this.onValidDrop(target, e, id);
20161             }
20162             
20163             if(this.afterDragDrop){
20164                 /**
20165                  * An empty function by default, but provided so that you can perform a custom action
20166                  * after a valid drag drop has occurred by providing an implementation.
20167                  * @param {Roo.dd.DragDrop} target The drop target
20168                  * @param {Event} e The event object
20169                  * @param {String} id The id of the dropped element
20170                  * @method afterDragDrop
20171                  */
20172                 this.afterDragDrop(target, e, id);
20173             }
20174         }
20175         delete this.cachedTarget;
20176     },
20177
20178     /**
20179      * An empty function by default, but provided so that you can perform a custom action before the dragged
20180      * item is dropped onto the target and optionally cancel the onDragDrop.
20181      * @param {Roo.dd.DragDrop} target The drop target
20182      * @param {Event} e The event object
20183      * @param {String} id The id of the dragged element
20184      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20185      */
20186     beforeDragDrop : function(target, e, id){
20187         return true;
20188     },
20189
20190     // private
20191     onValidDrop : function(target, e, id){
20192         this.hideProxy();
20193         if(this.afterValidDrop){
20194             /**
20195              * An empty function by default, but provided so that you can perform a custom action
20196              * after a valid drop has occurred by providing an implementation.
20197              * @param {Object} target The target DD 
20198              * @param {Event} e The event object
20199              * @param {String} id The id of the dropped element
20200              * @method afterInvalidDrop
20201              */
20202             this.afterValidDrop(target, e, id);
20203         }
20204     },
20205
20206     // private
20207     getRepairXY : function(e, data){
20208         return this.el.getXY();  
20209     },
20210
20211     // private
20212     onInvalidDrop : function(target, e, id){
20213         this.beforeInvalidDrop(target, e, id);
20214         if(this.cachedTarget){
20215             if(this.cachedTarget.isNotifyTarget){
20216                 this.cachedTarget.notifyOut(this, e, this.dragData);
20217             }
20218             this.cacheTarget = null;
20219         }
20220         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20221
20222         if(this.afterInvalidDrop){
20223             /**
20224              * An empty function by default, but provided so that you can perform a custom action
20225              * after an invalid drop has occurred by providing an implementation.
20226              * @param {Event} e The event object
20227              * @param {String} id The id of the dropped element
20228              * @method afterInvalidDrop
20229              */
20230             this.afterInvalidDrop(e, id);
20231         }
20232     },
20233
20234     // private
20235     afterRepair : function(){
20236         if(Roo.enableFx){
20237             this.el.highlight(this.hlColor || "c3daf9");
20238         }
20239         this.dragging = false;
20240     },
20241
20242     /**
20243      * An empty function by default, but provided so that you can perform a custom action after an invalid
20244      * drop has occurred.
20245      * @param {Roo.dd.DragDrop} target The drop target
20246      * @param {Event} e The event object
20247      * @param {String} id The id of the dragged element
20248      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20249      */
20250     beforeInvalidDrop : function(target, e, id){
20251         return true;
20252     },
20253
20254     // private
20255     handleMouseDown : function(e){
20256         if(this.dragging) {
20257             return;
20258         }
20259         var data = this.getDragData(e);
20260         if(data && this.onBeforeDrag(data, e) !== false){
20261             this.dragData = data;
20262             this.proxy.stop();
20263             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20264         } 
20265     },
20266
20267     /**
20268      * An empty function by default, but provided so that you can perform a custom action before the initial
20269      * drag event begins and optionally cancel it.
20270      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20271      * @param {Event} e The event object
20272      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20273      */
20274     onBeforeDrag : function(data, e){
20275         return true;
20276     },
20277
20278     /**
20279      * An empty function by default, but provided so that you can perform a custom action once the initial
20280      * drag event has begun.  The drag cannot be canceled from this function.
20281      * @param {Number} x The x position of the click on the dragged object
20282      * @param {Number} y The y position of the click on the dragged object
20283      */
20284     onStartDrag : Roo.emptyFn,
20285
20286     // private - YUI override
20287     startDrag : function(x, y){
20288         this.proxy.reset();
20289         this.dragging = true;
20290         this.proxy.update("");
20291         this.onInitDrag(x, y);
20292         this.proxy.show();
20293     },
20294
20295     // private
20296     onInitDrag : function(x, y){
20297         var clone = this.el.dom.cloneNode(true);
20298         clone.id = Roo.id(); // prevent duplicate ids
20299         this.proxy.update(clone);
20300         this.onStartDrag(x, y);
20301         return true;
20302     },
20303
20304     /**
20305      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20306      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20307      */
20308     getProxy : function(){
20309         return this.proxy;  
20310     },
20311
20312     /**
20313      * Hides the drag source's {@link Roo.dd.StatusProxy}
20314      */
20315     hideProxy : function(){
20316         this.proxy.hide();  
20317         this.proxy.reset(true);
20318         this.dragging = false;
20319     },
20320
20321     // private
20322     triggerCacheRefresh : function(){
20323         Roo.dd.DDM.refreshCache(this.groups);
20324     },
20325
20326     // private - override to prevent hiding
20327     b4EndDrag: function(e) {
20328     },
20329
20330     // private - override to prevent moving
20331     endDrag : function(e){
20332         this.onEndDrag(this.dragData, e);
20333     },
20334
20335     // private
20336     onEndDrag : function(data, e){
20337     },
20338     
20339     // private - pin to cursor
20340     autoOffset : function(x, y) {
20341         this.setDelta(-12, -20);
20342     }    
20343 });/*
20344  * Based on:
20345  * Ext JS Library 1.1.1
20346  * Copyright(c) 2006-2007, Ext JS, LLC.
20347  *
20348  * Originally Released Under LGPL - original licence link has changed is not relivant.
20349  *
20350  * Fork - LGPL
20351  * <script type="text/javascript">
20352  */
20353
20354
20355 /**
20356  * @class Roo.dd.DropTarget
20357  * @extends Roo.dd.DDTarget
20358  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20359  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20360  * @constructor
20361  * @param {String/HTMLElement/Element} el The container element
20362  * @param {Object} config
20363  */
20364 Roo.dd.DropTarget = function(el, config){
20365     this.el = Roo.get(el);
20366     
20367     var listeners = false; ;
20368     if (config && config.listeners) {
20369         listeners= config.listeners;
20370         delete config.listeners;
20371     }
20372     Roo.apply(this, config);
20373     
20374     if(this.containerScroll){
20375         Roo.dd.ScrollManager.register(this.el);
20376     }
20377     this.addEvents( {
20378          /**
20379          * @scope Roo.dd.DropTarget
20380          */
20381          
20382          /**
20383          * @event enter
20384          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20385          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20386          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20387          * 
20388          * IMPORTANT : it should set this.overClass and this.dropAllowed
20389          * 
20390          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20391          * @param {Event} e The event
20392          * @param {Object} data An object containing arbitrary data supplied by the drag source
20393          */
20394         "enter" : true,
20395         
20396          /**
20397          * @event over
20398          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20399          * This method will be called on every mouse movement while the drag source is over the drop target.
20400          * This default implementation simply returns the dropAllowed config value.
20401          * 
20402          * IMPORTANT : it should set this.dropAllowed
20403          * 
20404          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20405          * @param {Event} e The event
20406          * @param {Object} data An object containing arbitrary data supplied by the drag source
20407          
20408          */
20409         "over" : true,
20410         /**
20411          * @event out
20412          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20413          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20414          * overClass (if any) from the drop element.
20415          * 
20416          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20417          * @param {Event} e The event
20418          * @param {Object} data An object containing arbitrary data supplied by the drag source
20419          */
20420          "out" : true,
20421          
20422         /**
20423          * @event drop
20424          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20425          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20426          * implementation that does something to process the drop event and returns true so that the drag source's
20427          * repair action does not run.
20428          * 
20429          * IMPORTANT : it should set this.success
20430          * 
20431          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20432          * @param {Event} e The event
20433          * @param {Object} data An object containing arbitrary data supplied by the drag source
20434         */
20435          "drop" : true
20436     });
20437             
20438      
20439     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20440         this.el.dom, 
20441         this.ddGroup || this.group,
20442         {
20443             isTarget: true,
20444             listeners : listeners || {} 
20445            
20446         
20447         }
20448     );
20449
20450 };
20451
20452 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20453     /**
20454      * @cfg {String} overClass
20455      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20456      */
20457      /**
20458      * @cfg {String} ddGroup
20459      * The drag drop group to handle drop events for
20460      */
20461      
20462     /**
20463      * @cfg {String} dropAllowed
20464      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20465      */
20466     dropAllowed : "x-dd-drop-ok",
20467     /**
20468      * @cfg {String} dropNotAllowed
20469      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20470      */
20471     dropNotAllowed : "x-dd-drop-nodrop",
20472     /**
20473      * @cfg {boolean} success
20474      * set this after drop listener.. 
20475      */
20476     success : false,
20477     /**
20478      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20479      * if the drop point is valid for over/enter..
20480      */
20481     valid : false,
20482     // private
20483     isTarget : true,
20484
20485     // private
20486     isNotifyTarget : true,
20487     
20488     /**
20489      * @hide
20490      */
20491     notifyEnter : function(dd, e, data)
20492     {
20493         this.valid = true;
20494         this.fireEvent('enter', dd, e, data);
20495         if(this.overClass){
20496             this.el.addClass(this.overClass);
20497         }
20498         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20499             this.valid ? this.dropAllowed : this.dropNotAllowed
20500         );
20501     },
20502
20503     /**
20504      * @hide
20505      */
20506     notifyOver : function(dd, e, data)
20507     {
20508         this.valid = true;
20509         this.fireEvent('over', dd, e, data);
20510         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20511             this.valid ? this.dropAllowed : this.dropNotAllowed
20512         );
20513     },
20514
20515     /**
20516      * @hide
20517      */
20518     notifyOut : function(dd, e, data)
20519     {
20520         this.fireEvent('out', dd, e, data);
20521         if(this.overClass){
20522             this.el.removeClass(this.overClass);
20523         }
20524     },
20525
20526     /**
20527      * @hide
20528      */
20529     notifyDrop : function(dd, e, data)
20530     {
20531         this.success = false;
20532         this.fireEvent('drop', dd, e, data);
20533         return this.success;
20534     }
20535 });/*
20536  * Based on:
20537  * Ext JS Library 1.1.1
20538  * Copyright(c) 2006-2007, Ext JS, LLC.
20539  *
20540  * Originally Released Under LGPL - original licence link has changed is not relivant.
20541  *
20542  * Fork - LGPL
20543  * <script type="text/javascript">
20544  */
20545
20546
20547 /**
20548  * @class Roo.dd.DragZone
20549  * @extends Roo.dd.DragSource
20550  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20551  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20552  * @constructor
20553  * @param {String/HTMLElement/Element} el The container element
20554  * @param {Object} config
20555  */
20556 Roo.dd.DragZone = function(el, config){
20557     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20558     if(this.containerScroll){
20559         Roo.dd.ScrollManager.register(this.el);
20560     }
20561 };
20562
20563 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20564     /**
20565      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20566      * for auto scrolling during drag operations.
20567      */
20568     /**
20569      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20570      * method after a failed drop (defaults to "c3daf9" - light blue)
20571      */
20572
20573     /**
20574      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20575      * for a valid target to drag based on the mouse down. Override this method
20576      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20577      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20578      * @param {EventObject} e The mouse down event
20579      * @return {Object} The dragData
20580      */
20581     getDragData : function(e){
20582         return Roo.dd.Registry.getHandleFromEvent(e);
20583     },
20584     
20585     /**
20586      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20587      * this.dragData.ddel
20588      * @param {Number} x The x position of the click on the dragged object
20589      * @param {Number} y The y position of the click on the dragged object
20590      * @return {Boolean} true to continue the drag, false to cancel
20591      */
20592     onInitDrag : function(x, y){
20593         this.proxy.update(this.dragData.ddel.cloneNode(true));
20594         this.onStartDrag(x, y);
20595         return true;
20596     },
20597     
20598     /**
20599      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20600      */
20601     afterRepair : function(){
20602         if(Roo.enableFx){
20603             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20604         }
20605         this.dragging = false;
20606     },
20607
20608     /**
20609      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20610      * the XY of this.dragData.ddel
20611      * @param {EventObject} e The mouse up event
20612      * @return {Array} The xy location (e.g. [100, 200])
20613      */
20614     getRepairXY : function(e){
20615         return Roo.Element.fly(this.dragData.ddel).getXY();  
20616     }
20617 });/*
20618  * Based on:
20619  * Ext JS Library 1.1.1
20620  * Copyright(c) 2006-2007, Ext JS, LLC.
20621  *
20622  * Originally Released Under LGPL - original licence link has changed is not relivant.
20623  *
20624  * Fork - LGPL
20625  * <script type="text/javascript">
20626  */
20627 /**
20628  * @class Roo.dd.DropZone
20629  * @extends Roo.dd.DropTarget
20630  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20631  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20632  * @constructor
20633  * @param {String/HTMLElement/Element} el The container element
20634  * @param {Object} config
20635  */
20636 Roo.dd.DropZone = function(el, config){
20637     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20638 };
20639
20640 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20641     /**
20642      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20643      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20644      * provide your own custom lookup.
20645      * @param {Event} e The event
20646      * @return {Object} data The custom data
20647      */
20648     getTargetFromEvent : function(e){
20649         return Roo.dd.Registry.getTargetFromEvent(e);
20650     },
20651
20652     /**
20653      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20654      * that it has registered.  This method has no default implementation and should be overridden to provide
20655      * node-specific processing if necessary.
20656      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20657      * {@link #getTargetFromEvent} for this node)
20658      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20659      * @param {Event} e The event
20660      * @param {Object} data An object containing arbitrary data supplied by the drag source
20661      */
20662     onNodeEnter : function(n, dd, e, data){
20663         
20664     },
20665
20666     /**
20667      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20668      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20669      * overridden to provide the proper feedback.
20670      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20671      * {@link #getTargetFromEvent} for this node)
20672      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20673      * @param {Event} e The event
20674      * @param {Object} data An object containing arbitrary data supplied by the drag source
20675      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20676      * underlying {@link Roo.dd.StatusProxy} can be updated
20677      */
20678     onNodeOver : function(n, dd, e, data){
20679         return this.dropAllowed;
20680     },
20681
20682     /**
20683      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20684      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20685      * node-specific processing if necessary.
20686      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20687      * {@link #getTargetFromEvent} for this node)
20688      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20689      * @param {Event} e The event
20690      * @param {Object} data An object containing arbitrary data supplied by the drag source
20691      */
20692     onNodeOut : function(n, dd, e, data){
20693         
20694     },
20695
20696     /**
20697      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20698      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20699      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20700      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20701      * {@link #getTargetFromEvent} for this node)
20702      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20703      * @param {Event} e The event
20704      * @param {Object} data An object containing arbitrary data supplied by the drag source
20705      * @return {Boolean} True if the drop was valid, else false
20706      */
20707     onNodeDrop : function(n, dd, e, data){
20708         return false;
20709     },
20710
20711     /**
20712      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20713      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20714      * it should be overridden to provide the proper feedback if necessary.
20715      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20716      * @param {Event} e The event
20717      * @param {Object} data An object containing arbitrary data supplied by the drag source
20718      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20719      * underlying {@link Roo.dd.StatusProxy} can be updated
20720      */
20721     onContainerOver : function(dd, e, data){
20722         return this.dropNotAllowed;
20723     },
20724
20725     /**
20726      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20727      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20728      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20729      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20730      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20731      * @param {Event} e The event
20732      * @param {Object} data An object containing arbitrary data supplied by the drag source
20733      * @return {Boolean} True if the drop was valid, else false
20734      */
20735     onContainerDrop : function(dd, e, data){
20736         return false;
20737     },
20738
20739     /**
20740      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20741      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20742      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20743      * you should override this method and provide a custom implementation.
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     notifyEnter : function(dd, e, data){
20751         return this.dropNotAllowed;
20752     },
20753
20754     /**
20755      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20756      * This method will be called on every mouse movement while the drag source is over the drop zone.
20757      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20758      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20759      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20760      * registered node, it will call {@link #onContainerOver}.
20761      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20762      * @param {Event} e The event
20763      * @param {Object} data An object containing arbitrary data supplied by the drag source
20764      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20765      * underlying {@link Roo.dd.StatusProxy} can be updated
20766      */
20767     notifyOver : function(dd, e, data){
20768         var n = this.getTargetFromEvent(e);
20769         if(!n){ // not over valid drop target
20770             if(this.lastOverNode){
20771                 this.onNodeOut(this.lastOverNode, dd, e, data);
20772                 this.lastOverNode = null;
20773             }
20774             return this.onContainerOver(dd, e, data);
20775         }
20776         if(this.lastOverNode != n){
20777             if(this.lastOverNode){
20778                 this.onNodeOut(this.lastOverNode, dd, e, data);
20779             }
20780             this.onNodeEnter(n, dd, e, data);
20781             this.lastOverNode = n;
20782         }
20783         return this.onNodeOver(n, dd, e, data);
20784     },
20785
20786     /**
20787      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20788      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20789      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20790      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20791      * @param {Event} e The event
20792      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20793      */
20794     notifyOut : function(dd, e, data){
20795         if(this.lastOverNode){
20796             this.onNodeOut(this.lastOverNode, dd, e, data);
20797             this.lastOverNode = null;
20798         }
20799     },
20800
20801     /**
20802      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20803      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20804      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20805      * otherwise it will call {@link #onContainerDrop}.
20806      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20807      * @param {Event} e The event
20808      * @param {Object} data An object containing arbitrary data supplied by the drag source
20809      * @return {Boolean} True if the drop was valid, else false
20810      */
20811     notifyDrop : function(dd, e, data){
20812         if(this.lastOverNode){
20813             this.onNodeOut(this.lastOverNode, dd, e, data);
20814             this.lastOverNode = null;
20815         }
20816         var n = this.getTargetFromEvent(e);
20817         return n ?
20818             this.onNodeDrop(n, dd, e, data) :
20819             this.onContainerDrop(dd, e, data);
20820     },
20821
20822     // private
20823     triggerCacheRefresh : function(){
20824         Roo.dd.DDM.refreshCache(this.groups);
20825     }  
20826 });/*
20827  * Based on:
20828  * Ext JS Library 1.1.1
20829  * Copyright(c) 2006-2007, Ext JS, LLC.
20830  *
20831  * Originally Released Under LGPL - original licence link has changed is not relivant.
20832  *
20833  * Fork - LGPL
20834  * <script type="text/javascript">
20835  */
20836
20837
20838 /**
20839  * @class Roo.data.SortTypes
20840  * @singleton
20841  * Defines the default sorting (casting?) comparison functions used when sorting data.
20842  */
20843 Roo.data.SortTypes = {
20844     /**
20845      * Default sort that does nothing
20846      * @param {Mixed} s The value being converted
20847      * @return {Mixed} The comparison value
20848      */
20849     none : function(s){
20850         return s;
20851     },
20852     
20853     /**
20854      * The regular expression used to strip tags
20855      * @type {RegExp}
20856      * @property
20857      */
20858     stripTagsRE : /<\/?[^>]+>/gi,
20859     
20860     /**
20861      * Strips all HTML tags to sort on text only
20862      * @param {Mixed} s The value being converted
20863      * @return {String} The comparison value
20864      */
20865     asText : function(s){
20866         return String(s).replace(this.stripTagsRE, "");
20867     },
20868     
20869     /**
20870      * Strips all HTML tags to sort on text only - Case insensitive
20871      * @param {Mixed} s The value being converted
20872      * @return {String} The comparison value
20873      */
20874     asUCText : function(s){
20875         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20876     },
20877     
20878     /**
20879      * Case insensitive string
20880      * @param {Mixed} s The value being converted
20881      * @return {String} The comparison value
20882      */
20883     asUCString : function(s) {
20884         return String(s).toUpperCase();
20885     },
20886     
20887     /**
20888      * Date sorting
20889      * @param {Mixed} s The value being converted
20890      * @return {Number} The comparison value
20891      */
20892     asDate : function(s) {
20893         if(!s){
20894             return 0;
20895         }
20896         if(s instanceof Date){
20897             return s.getTime();
20898         }
20899         return Date.parse(String(s));
20900     },
20901     
20902     /**
20903      * Float sorting
20904      * @param {Mixed} s The value being converted
20905      * @return {Float} The comparison value
20906      */
20907     asFloat : function(s) {
20908         var val = parseFloat(String(s).replace(/,/g, ""));
20909         if(isNaN(val)) val = 0;
20910         return val;
20911     },
20912     
20913     /**
20914      * Integer sorting
20915      * @param {Mixed} s The value being converted
20916      * @return {Number} The comparison value
20917      */
20918     asInt : function(s) {
20919         var val = parseInt(String(s).replace(/,/g, ""));
20920         if(isNaN(val)) val = 0;
20921         return val;
20922     }
20923 };/*
20924  * Based on:
20925  * Ext JS Library 1.1.1
20926  * Copyright(c) 2006-2007, Ext JS, LLC.
20927  *
20928  * Originally Released Under LGPL - original licence link has changed is not relivant.
20929  *
20930  * Fork - LGPL
20931  * <script type="text/javascript">
20932  */
20933
20934 /**
20935 * @class Roo.data.Record
20936  * Instances of this class encapsulate both record <em>definition</em> information, and record
20937  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20938  * to access Records cached in an {@link Roo.data.Store} object.<br>
20939  * <p>
20940  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20941  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20942  * objects.<br>
20943  * <p>
20944  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20945  * @constructor
20946  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20947  * {@link #create}. The parameters are the same.
20948  * @param {Array} data An associative Array of data values keyed by the field name.
20949  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20950  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20951  * not specified an integer id is generated.
20952  */
20953 Roo.data.Record = function(data, id){
20954     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20955     this.data = data;
20956 };
20957
20958 /**
20959  * Generate a constructor for a specific record layout.
20960  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20961  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20962  * Each field definition object may contain the following properties: <ul>
20963  * <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,
20964  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20965  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20966  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20967  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20968  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20969  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20970  * this may be omitted.</p></li>
20971  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20972  * <ul><li>auto (Default, implies no conversion)</li>
20973  * <li>string</li>
20974  * <li>int</li>
20975  * <li>float</li>
20976  * <li>boolean</li>
20977  * <li>date</li></ul></p></li>
20978  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20979  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20980  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20981  * by the Reader into an object that will be stored in the Record. It is passed the
20982  * following parameters:<ul>
20983  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20984  * </ul></p></li>
20985  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20986  * </ul>
20987  * <br>usage:<br><pre><code>
20988 var TopicRecord = Roo.data.Record.create(
20989     {name: 'title', mapping: 'topic_title'},
20990     {name: 'author', mapping: 'username'},
20991     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20992     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20993     {name: 'lastPoster', mapping: 'user2'},
20994     {name: 'excerpt', mapping: 'post_text'}
20995 );
20996
20997 var myNewRecord = new TopicRecord({
20998     title: 'Do my job please',
20999     author: 'noobie',
21000     totalPosts: 1,
21001     lastPost: new Date(),
21002     lastPoster: 'Animal',
21003     excerpt: 'No way dude!'
21004 });
21005 myStore.add(myNewRecord);
21006 </code></pre>
21007  * @method create
21008  * @static
21009  */
21010 Roo.data.Record.create = function(o){
21011     var f = function(){
21012         f.superclass.constructor.apply(this, arguments);
21013     };
21014     Roo.extend(f, Roo.data.Record);
21015     var p = f.prototype;
21016     p.fields = new Roo.util.MixedCollection(false, function(field){
21017         return field.name;
21018     });
21019     for(var i = 0, len = o.length; i < len; i++){
21020         p.fields.add(new Roo.data.Field(o[i]));
21021     }
21022     f.getField = function(name){
21023         return p.fields.get(name);  
21024     };
21025     return f;
21026 };
21027
21028 Roo.data.Record.AUTO_ID = 1000;
21029 Roo.data.Record.EDIT = 'edit';
21030 Roo.data.Record.REJECT = 'reject';
21031 Roo.data.Record.COMMIT = 'commit';
21032
21033 Roo.data.Record.prototype = {
21034     /**
21035      * Readonly flag - true if this record has been modified.
21036      * @type Boolean
21037      */
21038     dirty : false,
21039     editing : false,
21040     error: null,
21041     modified: null,
21042
21043     // private
21044     join : function(store){
21045         this.store = store;
21046     },
21047
21048     /**
21049      * Set the named field to the specified value.
21050      * @param {String} name The name of the field to set.
21051      * @param {Object} value The value to set the field to.
21052      */
21053     set : function(name, value){
21054         if(this.data[name] == value){
21055             return;
21056         }
21057         this.dirty = true;
21058         if(!this.modified){
21059             this.modified = {};
21060         }
21061         if(typeof this.modified[name] == 'undefined'){
21062             this.modified[name] = this.data[name];
21063         }
21064         this.data[name] = value;
21065         if(!this.editing && this.store){
21066             this.store.afterEdit(this);
21067         }       
21068     },
21069
21070     /**
21071      * Get the value of the named field.
21072      * @param {String} name The name of the field to get the value of.
21073      * @return {Object} The value of the field.
21074      */
21075     get : function(name){
21076         return this.data[name]; 
21077     },
21078
21079     // private
21080     beginEdit : function(){
21081         this.editing = true;
21082         this.modified = {}; 
21083     },
21084
21085     // private
21086     cancelEdit : function(){
21087         this.editing = false;
21088         delete this.modified;
21089     },
21090
21091     // private
21092     endEdit : function(){
21093         this.editing = false;
21094         if(this.dirty && this.store){
21095             this.store.afterEdit(this);
21096         }
21097     },
21098
21099     /**
21100      * Usually called by the {@link Roo.data.Store} which owns the Record.
21101      * Rejects all changes made to the Record since either creation, or the last commit operation.
21102      * Modified fields are reverted to their original values.
21103      * <p>
21104      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21105      * of reject operations.
21106      */
21107     reject : function(){
21108         var m = this.modified;
21109         for(var n in m){
21110             if(typeof m[n] != "function"){
21111                 this.data[n] = m[n];
21112             }
21113         }
21114         this.dirty = false;
21115         delete this.modified;
21116         this.editing = false;
21117         if(this.store){
21118             this.store.afterReject(this);
21119         }
21120     },
21121
21122     /**
21123      * Usually called by the {@link Roo.data.Store} which owns the Record.
21124      * Commits all changes made to the Record since either creation, or the last commit operation.
21125      * <p>
21126      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21127      * of commit operations.
21128      */
21129     commit : function(){
21130         this.dirty = false;
21131         delete this.modified;
21132         this.editing = false;
21133         if(this.store){
21134             this.store.afterCommit(this);
21135         }
21136     },
21137
21138     // private
21139     hasError : function(){
21140         return this.error != null;
21141     },
21142
21143     // private
21144     clearError : function(){
21145         this.error = null;
21146     },
21147
21148     /**
21149      * Creates a copy of this record.
21150      * @param {String} id (optional) A new record id if you don't want to use this record's id
21151      * @return {Record}
21152      */
21153     copy : function(newId) {
21154         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21155     }
21156 };/*
21157  * Based on:
21158  * Ext JS Library 1.1.1
21159  * Copyright(c) 2006-2007, Ext JS, LLC.
21160  *
21161  * Originally Released Under LGPL - original licence link has changed is not relivant.
21162  *
21163  * Fork - LGPL
21164  * <script type="text/javascript">
21165  */
21166
21167
21168
21169 /**
21170  * @class Roo.data.Store
21171  * @extends Roo.util.Observable
21172  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21173  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21174  * <p>
21175  * 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
21176  * has no knowledge of the format of the data returned by the Proxy.<br>
21177  * <p>
21178  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21179  * instances from the data object. These records are cached and made available through accessor functions.
21180  * @constructor
21181  * Creates a new Store.
21182  * @param {Object} config A config object containing the objects needed for the Store to access data,
21183  * and read the data into Records.
21184  */
21185 Roo.data.Store = function(config){
21186     this.data = new Roo.util.MixedCollection(false);
21187     this.data.getKey = function(o){
21188         return o.id;
21189     };
21190     this.baseParams = {};
21191     // private
21192     this.paramNames = {
21193         "start" : "start",
21194         "limit" : "limit",
21195         "sort" : "sort",
21196         "dir" : "dir",
21197         "multisort" : "_multisort"
21198     };
21199
21200     if(config && config.data){
21201         this.inlineData = config.data;
21202         delete config.data;
21203     }
21204
21205     Roo.apply(this, config);
21206     
21207     if(this.reader){ // reader passed
21208         this.reader = Roo.factory(this.reader, Roo.data);
21209         this.reader.xmodule = this.xmodule || false;
21210         if(!this.recordType){
21211             this.recordType = this.reader.recordType;
21212         }
21213         if(this.reader.onMetaChange){
21214             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21215         }
21216     }
21217
21218     if(this.recordType){
21219         this.fields = this.recordType.prototype.fields;
21220     }
21221     this.modified = [];
21222
21223     this.addEvents({
21224         /**
21225          * @event datachanged
21226          * Fires when the data cache has changed, and a widget which is using this Store
21227          * as a Record cache should refresh its view.
21228          * @param {Store} this
21229          */
21230         datachanged : true,
21231         /**
21232          * @event metachange
21233          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21234          * @param {Store} this
21235          * @param {Object} meta The JSON metadata
21236          */
21237         metachange : true,
21238         /**
21239          * @event add
21240          * Fires when Records have been added to the Store
21241          * @param {Store} this
21242          * @param {Roo.data.Record[]} records The array of Records added
21243          * @param {Number} index The index at which the record(s) were added
21244          */
21245         add : true,
21246         /**
21247          * @event remove
21248          * Fires when a Record has been removed from the Store
21249          * @param {Store} this
21250          * @param {Roo.data.Record} record The Record that was removed
21251          * @param {Number} index The index at which the record was removed
21252          */
21253         remove : true,
21254         /**
21255          * @event update
21256          * Fires when a Record has been updated
21257          * @param {Store} this
21258          * @param {Roo.data.Record} record The Record that was updated
21259          * @param {String} operation The update operation being performed.  Value may be one of:
21260          * <pre><code>
21261  Roo.data.Record.EDIT
21262  Roo.data.Record.REJECT
21263  Roo.data.Record.COMMIT
21264          * </code></pre>
21265          */
21266         update : true,
21267         /**
21268          * @event clear
21269          * Fires when the data cache has been cleared.
21270          * @param {Store} this
21271          */
21272         clear : true,
21273         /**
21274          * @event beforeload
21275          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21276          * the load action will be canceled.
21277          * @param {Store} this
21278          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21279          */
21280         beforeload : true,
21281         /**
21282          * @event beforeloadadd
21283          * Fires after a new set of Records has been loaded.
21284          * @param {Store} this
21285          * @param {Roo.data.Record[]} records The Records that were loaded
21286          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21287          */
21288         beforeloadadd : true,
21289         /**
21290          * @event load
21291          * Fires after a new set of Records has been loaded, before they are added to the store.
21292          * @param {Store} this
21293          * @param {Roo.data.Record[]} records The Records that were loaded
21294          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21295          * @params {Object} return from reader
21296          */
21297         load : true,
21298         /**
21299          * @event loadexception
21300          * Fires if an exception occurs in the Proxy during loading.
21301          * Called with the signature of the Proxy's "loadexception" event.
21302          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21303          * 
21304          * @param {Proxy} 
21305          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21306          * @param {Object} load options 
21307          * @param {Object} jsonData from your request (normally this contains the Exception)
21308          */
21309         loadexception : true
21310     });
21311     
21312     if(this.proxy){
21313         this.proxy = Roo.factory(this.proxy, Roo.data);
21314         this.proxy.xmodule = this.xmodule || false;
21315         this.relayEvents(this.proxy,  ["loadexception"]);
21316     }
21317     this.sortToggle = {};
21318     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21319
21320     Roo.data.Store.superclass.constructor.call(this);
21321
21322     if(this.inlineData){
21323         this.loadData(this.inlineData);
21324         delete this.inlineData;
21325     }
21326 };
21327
21328 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21329      /**
21330     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21331     * without a remote query - used by combo/forms at present.
21332     */
21333     
21334     /**
21335     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21336     */
21337     /**
21338     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21339     */
21340     /**
21341     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21342     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21343     */
21344     /**
21345     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21346     * on any HTTP request
21347     */
21348     /**
21349     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21350     */
21351     /**
21352     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21353     */
21354     multiSort: false,
21355     /**
21356     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21357     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21358     */
21359     remoteSort : false,
21360
21361     /**
21362     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21363      * loaded or when a record is removed. (defaults to false).
21364     */
21365     pruneModifiedRecords : false,
21366
21367     // private
21368     lastOptions : null,
21369
21370     /**
21371      * Add Records to the Store and fires the add event.
21372      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21373      */
21374     add : function(records){
21375         records = [].concat(records);
21376         for(var i = 0, len = records.length; i < len; i++){
21377             records[i].join(this);
21378         }
21379         var index = this.data.length;
21380         this.data.addAll(records);
21381         this.fireEvent("add", this, records, index);
21382     },
21383
21384     /**
21385      * Remove a Record from the Store and fires the remove event.
21386      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21387      */
21388     remove : function(record){
21389         var index = this.data.indexOf(record);
21390         this.data.removeAt(index);
21391         if(this.pruneModifiedRecords){
21392             this.modified.remove(record);
21393         }
21394         this.fireEvent("remove", this, record, index);
21395     },
21396
21397     /**
21398      * Remove all Records from the Store and fires the clear event.
21399      */
21400     removeAll : function(){
21401         this.data.clear();
21402         if(this.pruneModifiedRecords){
21403             this.modified = [];
21404         }
21405         this.fireEvent("clear", this);
21406     },
21407
21408     /**
21409      * Inserts Records to the Store at the given index and fires the add event.
21410      * @param {Number} index The start index at which to insert the passed Records.
21411      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21412      */
21413     insert : function(index, records){
21414         records = [].concat(records);
21415         for(var i = 0, len = records.length; i < len; i++){
21416             this.data.insert(index, records[i]);
21417             records[i].join(this);
21418         }
21419         this.fireEvent("add", this, records, index);
21420     },
21421
21422     /**
21423      * Get the index within the cache of the passed Record.
21424      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21425      * @return {Number} The index of the passed Record. Returns -1 if not found.
21426      */
21427     indexOf : function(record){
21428         return this.data.indexOf(record);
21429     },
21430
21431     /**
21432      * Get the index within the cache of the Record with the passed id.
21433      * @param {String} id The id of the Record to find.
21434      * @return {Number} The index of the Record. Returns -1 if not found.
21435      */
21436     indexOfId : function(id){
21437         return this.data.indexOfKey(id);
21438     },
21439
21440     /**
21441      * Get the Record with the specified id.
21442      * @param {String} id The id of the Record to find.
21443      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21444      */
21445     getById : function(id){
21446         return this.data.key(id);
21447     },
21448
21449     /**
21450      * Get the Record at the specified index.
21451      * @param {Number} index The index of the Record to find.
21452      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21453      */
21454     getAt : function(index){
21455         return this.data.itemAt(index);
21456     },
21457
21458     /**
21459      * Returns a range of Records between specified indices.
21460      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21461      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21462      * @return {Roo.data.Record[]} An array of Records
21463      */
21464     getRange : function(start, end){
21465         return this.data.getRange(start, end);
21466     },
21467
21468     // private
21469     storeOptions : function(o){
21470         o = Roo.apply({}, o);
21471         delete o.callback;
21472         delete o.scope;
21473         this.lastOptions = o;
21474     },
21475
21476     /**
21477      * Loads the Record cache from the configured Proxy using the configured Reader.
21478      * <p>
21479      * If using remote paging, then the first load call must specify the <em>start</em>
21480      * and <em>limit</em> properties in the options.params property to establish the initial
21481      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21482      * <p>
21483      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21484      * and this call will return before the new data has been loaded. Perform any post-processing
21485      * in a callback function, or in a "load" event handler.</strong>
21486      * <p>
21487      * @param {Object} options An object containing properties which control loading options:<ul>
21488      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21489      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21490      * passed the following arguments:<ul>
21491      * <li>r : Roo.data.Record[]</li>
21492      * <li>options: Options object from the load call</li>
21493      * <li>success: Boolean success indicator</li></ul></li>
21494      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21495      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21496      * </ul>
21497      */
21498     load : function(options){
21499         options = options || {};
21500         if(this.fireEvent("beforeload", this, options) !== false){
21501             this.storeOptions(options);
21502             var p = Roo.apply(options.params || {}, this.baseParams);
21503             // if meta was not loaded from remote source.. try requesting it.
21504             if (!this.reader.metaFromRemote) {
21505                 p._requestMeta = 1;
21506             }
21507             if(this.sortInfo && this.remoteSort){
21508                 var pn = this.paramNames;
21509                 p[pn["sort"]] = this.sortInfo.field;
21510                 p[pn["dir"]] = this.sortInfo.direction;
21511             }
21512             if (this.multiSort) {
21513                 var pn = this.paramNames;
21514                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21515             }
21516             
21517             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21518         }
21519     },
21520
21521     /**
21522      * Reloads the Record cache from the configured Proxy using the configured Reader and
21523      * the options from the last load operation performed.
21524      * @param {Object} options (optional) An object containing properties which may override the options
21525      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21526      * the most recently used options are reused).
21527      */
21528     reload : function(options){
21529         this.load(Roo.applyIf(options||{}, this.lastOptions));
21530     },
21531
21532     // private
21533     // Called as a callback by the Reader during a load operation.
21534     loadRecords : function(o, options, success){
21535         if(!o || success === false){
21536             if(success !== false){
21537                 this.fireEvent("load", this, [], options, o);
21538             }
21539             if(options.callback){
21540                 options.callback.call(options.scope || this, [], options, false);
21541             }
21542             return;
21543         }
21544         // if data returned failure - throw an exception.
21545         if (o.success === false) {
21546             // show a message if no listener is registered.
21547             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21548                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21549             }
21550             // loadmask wil be hooked into this..
21551             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21552             return;
21553         }
21554         var r = o.records, t = o.totalRecords || r.length;
21555         
21556         this.fireEvent("beforeloadadd", this, r, options, o);
21557         
21558         if(!options || options.add !== true){
21559             if(this.pruneModifiedRecords){
21560                 this.modified = [];
21561             }
21562             for(var i = 0, len = r.length; i < len; i++){
21563                 r[i].join(this);
21564             }
21565             if(this.snapshot){
21566                 this.data = this.snapshot;
21567                 delete this.snapshot;
21568             }
21569             this.data.clear();
21570             this.data.addAll(r);
21571             this.totalLength = t;
21572             this.applySort();
21573             this.fireEvent("datachanged", this);
21574         }else{
21575             this.totalLength = Math.max(t, this.data.length+r.length);
21576             this.add(r);
21577         }
21578         this.fireEvent("load", this, r, options, o);
21579         if(options.callback){
21580             options.callback.call(options.scope || this, r, options, true);
21581         }
21582     },
21583
21584
21585     /**
21586      * Loads data from a passed data block. A Reader which understands the format of the data
21587      * must have been configured in the constructor.
21588      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21589      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21590      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21591      */
21592     loadData : function(o, append){
21593         var r = this.reader.readRecords(o);
21594         this.loadRecords(r, {add: append}, true);
21595     },
21596
21597     /**
21598      * Gets the number of cached records.
21599      * <p>
21600      * <em>If using paging, this may not be the total size of the dataset. If the data object
21601      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21602      * the data set size</em>
21603      */
21604     getCount : function(){
21605         return this.data.length || 0;
21606     },
21607
21608     /**
21609      * Gets the total number of records in the dataset as returned by the server.
21610      * <p>
21611      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21612      * the dataset size</em>
21613      */
21614     getTotalCount : function(){
21615         return this.totalLength || 0;
21616     },
21617
21618     /**
21619      * Returns the sort state of the Store as an object with two properties:
21620      * <pre><code>
21621  field {String} The name of the field by which the Records are sorted
21622  direction {String} The sort order, "ASC" or "DESC"
21623      * </code></pre>
21624      */
21625     getSortState : function(){
21626         return this.sortInfo;
21627     },
21628
21629     // private
21630     applySort : function(){
21631         if(this.sortInfo && !this.remoteSort){
21632             var s = this.sortInfo, f = s.field;
21633             var st = this.fields.get(f).sortType;
21634             var fn = function(r1, r2){
21635                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21636                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21637             };
21638             this.data.sort(s.direction, fn);
21639             if(this.snapshot && this.snapshot != this.data){
21640                 this.snapshot.sort(s.direction, fn);
21641             }
21642         }
21643     },
21644
21645     /**
21646      * Sets the default sort column and order to be used by the next load operation.
21647      * @param {String} fieldName The name of the field to sort by.
21648      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21649      */
21650     setDefaultSort : function(field, dir){
21651         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21652     },
21653
21654     /**
21655      * Sort the Records.
21656      * If remote sorting is used, the sort is performed on the server, and the cache is
21657      * reloaded. If local sorting is used, the cache is sorted internally.
21658      * @param {String} fieldName The name of the field to sort by.
21659      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21660      */
21661     sort : function(fieldName, dir){
21662         var f = this.fields.get(fieldName);
21663         if(!dir){
21664             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21665             
21666             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21667                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21668             }else{
21669                 dir = f.sortDir;
21670             }
21671         }
21672         this.sortToggle[f.name] = dir;
21673         this.sortInfo = {field: f.name, direction: dir};
21674         if(!this.remoteSort){
21675             this.applySort();
21676             this.fireEvent("datachanged", this);
21677         }else{
21678             this.load(this.lastOptions);
21679         }
21680     },
21681
21682     /**
21683      * Calls the specified function for each of the Records in the cache.
21684      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21685      * Returning <em>false</em> aborts and exits the iteration.
21686      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21687      */
21688     each : function(fn, scope){
21689         this.data.each(fn, scope);
21690     },
21691
21692     /**
21693      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21694      * (e.g., during paging).
21695      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21696      */
21697     getModifiedRecords : function(){
21698         return this.modified;
21699     },
21700
21701     // private
21702     createFilterFn : function(property, value, anyMatch){
21703         if(!value.exec){ // not a regex
21704             value = String(value);
21705             if(value.length == 0){
21706                 return false;
21707             }
21708             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21709         }
21710         return function(r){
21711             return value.test(r.data[property]);
21712         };
21713     },
21714
21715     /**
21716      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21717      * @param {String} property A field on your records
21718      * @param {Number} start The record index to start at (defaults to 0)
21719      * @param {Number} end The last record index to include (defaults to length - 1)
21720      * @return {Number} The sum
21721      */
21722     sum : function(property, start, end){
21723         var rs = this.data.items, v = 0;
21724         start = start || 0;
21725         end = (end || end === 0) ? end : rs.length-1;
21726
21727         for(var i = start; i <= end; i++){
21728             v += (rs[i].data[property] || 0);
21729         }
21730         return v;
21731     },
21732
21733     /**
21734      * Filter the records by a specified property.
21735      * @param {String} field A field on your records
21736      * @param {String/RegExp} value Either a string that the field
21737      * should start with or a RegExp to test against the field
21738      * @param {Boolean} anyMatch True to match any part not just the beginning
21739      */
21740     filter : function(property, value, anyMatch){
21741         var fn = this.createFilterFn(property, value, anyMatch);
21742         return fn ? this.filterBy(fn) : this.clearFilter();
21743     },
21744
21745     /**
21746      * Filter by a function. The specified function will be called with each
21747      * record in this data source. If the function returns true the record is included,
21748      * otherwise it is filtered.
21749      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21750      * @param {Object} scope (optional) The scope of the function (defaults to this)
21751      */
21752     filterBy : function(fn, scope){
21753         this.snapshot = this.snapshot || this.data;
21754         this.data = this.queryBy(fn, scope||this);
21755         this.fireEvent("datachanged", this);
21756     },
21757
21758     /**
21759      * Query the records by a specified property.
21760      * @param {String} field A field on your records
21761      * @param {String/RegExp} value Either a string that the field
21762      * should start with or a RegExp to test against the field
21763      * @param {Boolean} anyMatch True to match any part not just the beginning
21764      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21765      */
21766     query : function(property, value, anyMatch){
21767         var fn = this.createFilterFn(property, value, anyMatch);
21768         return fn ? this.queryBy(fn) : this.data.clone();
21769     },
21770
21771     /**
21772      * Query by a function. The specified function will be called with each
21773      * record in this data source. If the function returns true the record is included
21774      * in the results.
21775      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21776      * @param {Object} scope (optional) The scope of the function (defaults to this)
21777       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21778      **/
21779     queryBy : function(fn, scope){
21780         var data = this.snapshot || this.data;
21781         return data.filterBy(fn, scope||this);
21782     },
21783
21784     /**
21785      * Collects unique values for a particular dataIndex from this store.
21786      * @param {String} dataIndex The property to collect
21787      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21788      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21789      * @return {Array} An array of the unique values
21790      **/
21791     collect : function(dataIndex, allowNull, bypassFilter){
21792         var d = (bypassFilter === true && this.snapshot) ?
21793                 this.snapshot.items : this.data.items;
21794         var v, sv, r = [], l = {};
21795         for(var i = 0, len = d.length; i < len; i++){
21796             v = d[i].data[dataIndex];
21797             sv = String(v);
21798             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21799                 l[sv] = true;
21800                 r[r.length] = v;
21801             }
21802         }
21803         return r;
21804     },
21805
21806     /**
21807      * Revert to a view of the Record cache with no filtering applied.
21808      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21809      */
21810     clearFilter : function(suppressEvent){
21811         if(this.snapshot && this.snapshot != this.data){
21812             this.data = this.snapshot;
21813             delete this.snapshot;
21814             if(suppressEvent !== true){
21815                 this.fireEvent("datachanged", this);
21816             }
21817         }
21818     },
21819
21820     // private
21821     afterEdit : function(record){
21822         if(this.modified.indexOf(record) == -1){
21823             this.modified.push(record);
21824         }
21825         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21826     },
21827     
21828     // private
21829     afterReject : function(record){
21830         this.modified.remove(record);
21831         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21832     },
21833
21834     // private
21835     afterCommit : function(record){
21836         this.modified.remove(record);
21837         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21838     },
21839
21840     /**
21841      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21842      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21843      */
21844     commitChanges : function(){
21845         var m = this.modified.slice(0);
21846         this.modified = [];
21847         for(var i = 0, len = m.length; i < len; i++){
21848             m[i].commit();
21849         }
21850     },
21851
21852     /**
21853      * Cancel outstanding changes on all changed records.
21854      */
21855     rejectChanges : function(){
21856         var m = this.modified.slice(0);
21857         this.modified = [];
21858         for(var i = 0, len = m.length; i < len; i++){
21859             m[i].reject();
21860         }
21861     },
21862
21863     onMetaChange : function(meta, rtype, o){
21864         this.recordType = rtype;
21865         this.fields = rtype.prototype.fields;
21866         delete this.snapshot;
21867         this.sortInfo = meta.sortInfo || this.sortInfo;
21868         this.modified = [];
21869         this.fireEvent('metachange', this, this.reader.meta);
21870     },
21871     
21872     moveIndex : function(data, type)
21873     {
21874         var index = this.indexOf(data);
21875         
21876         var newIndex = index + type;
21877         
21878         this.remove(data);
21879         
21880         this.insert(newIndex, data);
21881         
21882     }
21883 });/*
21884  * Based on:
21885  * Ext JS Library 1.1.1
21886  * Copyright(c) 2006-2007, Ext JS, LLC.
21887  *
21888  * Originally Released Under LGPL - original licence link has changed is not relivant.
21889  *
21890  * Fork - LGPL
21891  * <script type="text/javascript">
21892  */
21893
21894 /**
21895  * @class Roo.data.SimpleStore
21896  * @extends Roo.data.Store
21897  * Small helper class to make creating Stores from Array data easier.
21898  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21899  * @cfg {Array} fields An array of field definition objects, or field name strings.
21900  * @cfg {Array} data The multi-dimensional array of data
21901  * @constructor
21902  * @param {Object} config
21903  */
21904 Roo.data.SimpleStore = function(config){
21905     Roo.data.SimpleStore.superclass.constructor.call(this, {
21906         isLocal : true,
21907         reader: new Roo.data.ArrayReader({
21908                 id: config.id
21909             },
21910             Roo.data.Record.create(config.fields)
21911         ),
21912         proxy : new Roo.data.MemoryProxy(config.data)
21913     });
21914     this.load();
21915 };
21916 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21917  * Based on:
21918  * Ext JS Library 1.1.1
21919  * Copyright(c) 2006-2007, Ext JS, LLC.
21920  *
21921  * Originally Released Under LGPL - original licence link has changed is not relivant.
21922  *
21923  * Fork - LGPL
21924  * <script type="text/javascript">
21925  */
21926
21927 /**
21928 /**
21929  * @extends Roo.data.Store
21930  * @class Roo.data.JsonStore
21931  * Small helper class to make creating Stores for JSON data easier. <br/>
21932 <pre><code>
21933 var store = new Roo.data.JsonStore({
21934     url: 'get-images.php',
21935     root: 'images',
21936     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21937 });
21938 </code></pre>
21939  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21940  * JsonReader and HttpProxy (unless inline data is provided).</b>
21941  * @cfg {Array} fields An array of field definition objects, or field name strings.
21942  * @constructor
21943  * @param {Object} config
21944  */
21945 Roo.data.JsonStore = function(c){
21946     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21947         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21948         reader: new Roo.data.JsonReader(c, c.fields)
21949     }));
21950 };
21951 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21952  * Based on:
21953  * Ext JS Library 1.1.1
21954  * Copyright(c) 2006-2007, Ext JS, LLC.
21955  *
21956  * Originally Released Under LGPL - original licence link has changed is not relivant.
21957  *
21958  * Fork - LGPL
21959  * <script type="text/javascript">
21960  */
21961
21962  
21963 Roo.data.Field = function(config){
21964     if(typeof config == "string"){
21965         config = {name: config};
21966     }
21967     Roo.apply(this, config);
21968     
21969     if(!this.type){
21970         this.type = "auto";
21971     }
21972     
21973     var st = Roo.data.SortTypes;
21974     // named sortTypes are supported, here we look them up
21975     if(typeof this.sortType == "string"){
21976         this.sortType = st[this.sortType];
21977     }
21978     
21979     // set default sortType for strings and dates
21980     if(!this.sortType){
21981         switch(this.type){
21982             case "string":
21983                 this.sortType = st.asUCString;
21984                 break;
21985             case "date":
21986                 this.sortType = st.asDate;
21987                 break;
21988             default:
21989                 this.sortType = st.none;
21990         }
21991     }
21992
21993     // define once
21994     var stripRe = /[\$,%]/g;
21995
21996     // prebuilt conversion function for this field, instead of
21997     // switching every time we're reading a value
21998     if(!this.convert){
21999         var cv, dateFormat = this.dateFormat;
22000         switch(this.type){
22001             case "":
22002             case "auto":
22003             case undefined:
22004                 cv = function(v){ return v; };
22005                 break;
22006             case "string":
22007                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22008                 break;
22009             case "int":
22010                 cv = function(v){
22011                     return v !== undefined && v !== null && v !== '' ?
22012                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22013                     };
22014                 break;
22015             case "float":
22016                 cv = function(v){
22017                     return v !== undefined && v !== null && v !== '' ?
22018                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22019                     };
22020                 break;
22021             case "bool":
22022             case "boolean":
22023                 cv = function(v){ return v === true || v === "true" || v == 1; };
22024                 break;
22025             case "date":
22026                 cv = function(v){
22027                     if(!v){
22028                         return '';
22029                     }
22030                     if(v instanceof Date){
22031                         return v;
22032                     }
22033                     if(dateFormat){
22034                         if(dateFormat == "timestamp"){
22035                             return new Date(v*1000);
22036                         }
22037                         return Date.parseDate(v, dateFormat);
22038                     }
22039                     var parsed = Date.parse(v);
22040                     return parsed ? new Date(parsed) : null;
22041                 };
22042              break;
22043             
22044         }
22045         this.convert = cv;
22046     }
22047 };
22048
22049 Roo.data.Field.prototype = {
22050     dateFormat: null,
22051     defaultValue: "",
22052     mapping: null,
22053     sortType : null,
22054     sortDir : "ASC"
22055 };/*
22056  * Based on:
22057  * Ext JS Library 1.1.1
22058  * Copyright(c) 2006-2007, Ext JS, LLC.
22059  *
22060  * Originally Released Under LGPL - original licence link has changed is not relivant.
22061  *
22062  * Fork - LGPL
22063  * <script type="text/javascript">
22064  */
22065  
22066 // Base class for reading structured data from a data source.  This class is intended to be
22067 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22068
22069 /**
22070  * @class Roo.data.DataReader
22071  * Base class for reading structured data from a data source.  This class is intended to be
22072  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22073  */
22074
22075 Roo.data.DataReader = function(meta, recordType){
22076     
22077     this.meta = meta;
22078     
22079     this.recordType = recordType instanceof Array ? 
22080         Roo.data.Record.create(recordType) : recordType;
22081 };
22082
22083 Roo.data.DataReader.prototype = {
22084      /**
22085      * Create an empty record
22086      * @param {Object} data (optional) - overlay some values
22087      * @return {Roo.data.Record} record created.
22088      */
22089     newRow :  function(d) {
22090         var da =  {};
22091         this.recordType.prototype.fields.each(function(c) {
22092             switch( c.type) {
22093                 case 'int' : da[c.name] = 0; break;
22094                 case 'date' : da[c.name] = new Date(); break;
22095                 case 'float' : da[c.name] = 0.0; break;
22096                 case 'boolean' : da[c.name] = false; break;
22097                 default : da[c.name] = ""; break;
22098             }
22099             
22100         });
22101         return new this.recordType(Roo.apply(da, d));
22102     }
22103     
22104 };/*
22105  * Based on:
22106  * Ext JS Library 1.1.1
22107  * Copyright(c) 2006-2007, Ext JS, LLC.
22108  *
22109  * Originally Released Under LGPL - original licence link has changed is not relivant.
22110  *
22111  * Fork - LGPL
22112  * <script type="text/javascript">
22113  */
22114
22115 /**
22116  * @class Roo.data.DataProxy
22117  * @extends Roo.data.Observable
22118  * This class is an abstract base class for implementations which provide retrieval of
22119  * unformatted data objects.<br>
22120  * <p>
22121  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22122  * (of the appropriate type which knows how to parse the data object) to provide a block of
22123  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22124  * <p>
22125  * Custom implementations must implement the load method as described in
22126  * {@link Roo.data.HttpProxy#load}.
22127  */
22128 Roo.data.DataProxy = function(){
22129     this.addEvents({
22130         /**
22131          * @event beforeload
22132          * Fires before a network request is made to retrieve a data object.
22133          * @param {Object} This DataProxy object.
22134          * @param {Object} params The params parameter to the load function.
22135          */
22136         beforeload : true,
22137         /**
22138          * @event load
22139          * Fires before the load method's callback is called.
22140          * @param {Object} This DataProxy object.
22141          * @param {Object} o The data object.
22142          * @param {Object} arg The callback argument object passed to the load function.
22143          */
22144         load : true,
22145         /**
22146          * @event loadexception
22147          * Fires if an Exception occurs during data retrieval.
22148          * @param {Object} This DataProxy object.
22149          * @param {Object} o The data object.
22150          * @param {Object} arg The callback argument object passed to the load function.
22151          * @param {Object} e The Exception.
22152          */
22153         loadexception : true
22154     });
22155     Roo.data.DataProxy.superclass.constructor.call(this);
22156 };
22157
22158 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22159
22160     /**
22161      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22162      */
22163 /*
22164  * Based on:
22165  * Ext JS Library 1.1.1
22166  * Copyright(c) 2006-2007, Ext JS, LLC.
22167  *
22168  * Originally Released Under LGPL - original licence link has changed is not relivant.
22169  *
22170  * Fork - LGPL
22171  * <script type="text/javascript">
22172  */
22173 /**
22174  * @class Roo.data.MemoryProxy
22175  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22176  * to the Reader when its load method is called.
22177  * @constructor
22178  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22179  */
22180 Roo.data.MemoryProxy = function(data){
22181     if (data.data) {
22182         data = data.data;
22183     }
22184     Roo.data.MemoryProxy.superclass.constructor.call(this);
22185     this.data = data;
22186 };
22187
22188 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22189     /**
22190      * Load data from the requested source (in this case an in-memory
22191      * data object passed to the constructor), read the data object into
22192      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22193      * process that block using the passed callback.
22194      * @param {Object} params This parameter is not used by the MemoryProxy class.
22195      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22196      * object into a block of Roo.data.Records.
22197      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22198      * The function must be passed <ul>
22199      * <li>The Record block object</li>
22200      * <li>The "arg" argument from the load function</li>
22201      * <li>A boolean success indicator</li>
22202      * </ul>
22203      * @param {Object} scope The scope in which to call the callback
22204      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22205      */
22206     load : function(params, reader, callback, scope, arg){
22207         params = params || {};
22208         var result;
22209         try {
22210             result = reader.readRecords(this.data);
22211         }catch(e){
22212             this.fireEvent("loadexception", this, arg, null, e);
22213             callback.call(scope, null, arg, false);
22214             return;
22215         }
22216         callback.call(scope, result, arg, true);
22217     },
22218     
22219     // private
22220     update : function(params, records){
22221         
22222     }
22223 });/*
22224  * Based on:
22225  * Ext JS Library 1.1.1
22226  * Copyright(c) 2006-2007, Ext JS, LLC.
22227  *
22228  * Originally Released Under LGPL - original licence link has changed is not relivant.
22229  *
22230  * Fork - LGPL
22231  * <script type="text/javascript">
22232  */
22233 /**
22234  * @class Roo.data.HttpProxy
22235  * @extends Roo.data.DataProxy
22236  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22237  * configured to reference a certain URL.<br><br>
22238  * <p>
22239  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22240  * from which the running page was served.<br><br>
22241  * <p>
22242  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22243  * <p>
22244  * Be aware that to enable the browser to parse an XML document, the server must set
22245  * the Content-Type header in the HTTP response to "text/xml".
22246  * @constructor
22247  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22248  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22249  * will be used to make the request.
22250  */
22251 Roo.data.HttpProxy = function(conn){
22252     Roo.data.HttpProxy.superclass.constructor.call(this);
22253     // is conn a conn config or a real conn?
22254     this.conn = conn;
22255     this.useAjax = !conn || !conn.events;
22256   
22257 };
22258
22259 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22260     // thse are take from connection...
22261     
22262     /**
22263      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22264      */
22265     /**
22266      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22267      * extra parameters to each request made by this object. (defaults to undefined)
22268      */
22269     /**
22270      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22271      *  to each request made by this object. (defaults to undefined)
22272      */
22273     /**
22274      * @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)
22275      */
22276     /**
22277      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22278      */
22279      /**
22280      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22281      * @type Boolean
22282      */
22283   
22284
22285     /**
22286      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22287      * @type Boolean
22288      */
22289     /**
22290      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22291      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22292      * a finer-grained basis than the DataProxy events.
22293      */
22294     getConnection : function(){
22295         return this.useAjax ? Roo.Ajax : this.conn;
22296     },
22297
22298     /**
22299      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22300      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22301      * process that block using the passed callback.
22302      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22303      * for the request to the remote server.
22304      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22305      * object into a block of Roo.data.Records.
22306      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22307      * The function must be passed <ul>
22308      * <li>The Record block object</li>
22309      * <li>The "arg" argument from the load function</li>
22310      * <li>A boolean success indicator</li>
22311      * </ul>
22312      * @param {Object} scope The scope in which to call the callback
22313      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22314      */
22315     load : function(params, reader, callback, scope, arg){
22316         if(this.fireEvent("beforeload", this, params) !== false){
22317             var  o = {
22318                 params : params || {},
22319                 request: {
22320                     callback : callback,
22321                     scope : scope,
22322                     arg : arg
22323                 },
22324                 reader: reader,
22325                 callback : this.loadResponse,
22326                 scope: this
22327             };
22328             if(this.useAjax){
22329                 Roo.applyIf(o, this.conn);
22330                 if(this.activeRequest){
22331                     Roo.Ajax.abort(this.activeRequest);
22332                 }
22333                 this.activeRequest = Roo.Ajax.request(o);
22334             }else{
22335                 this.conn.request(o);
22336             }
22337         }else{
22338             callback.call(scope||this, null, arg, false);
22339         }
22340     },
22341
22342     // private
22343     loadResponse : function(o, success, response){
22344         delete this.activeRequest;
22345         if(!success){
22346             this.fireEvent("loadexception", this, o, response);
22347             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22348             return;
22349         }
22350         var result;
22351         try {
22352             result = o.reader.read(response);
22353         }catch(e){
22354             this.fireEvent("loadexception", this, o, response, e);
22355             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22356             return;
22357         }
22358         
22359         this.fireEvent("load", this, o, o.request.arg);
22360         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22361     },
22362
22363     // private
22364     update : function(dataSet){
22365
22366     },
22367
22368     // private
22369     updateResponse : function(dataSet){
22370
22371     }
22372 });/*
22373  * Based on:
22374  * Ext JS Library 1.1.1
22375  * Copyright(c) 2006-2007, Ext JS, LLC.
22376  *
22377  * Originally Released Under LGPL - original licence link has changed is not relivant.
22378  *
22379  * Fork - LGPL
22380  * <script type="text/javascript">
22381  */
22382
22383 /**
22384  * @class Roo.data.ScriptTagProxy
22385  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22386  * other than the originating domain of the running page.<br><br>
22387  * <p>
22388  * <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
22389  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22390  * <p>
22391  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22392  * source code that is used as the source inside a &lt;script> tag.<br><br>
22393  * <p>
22394  * In order for the browser to process the returned data, the server must wrap the data object
22395  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22396  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22397  * depending on whether the callback name was passed:
22398  * <p>
22399  * <pre><code>
22400 boolean scriptTag = false;
22401 String cb = request.getParameter("callback");
22402 if (cb != null) {
22403     scriptTag = true;
22404     response.setContentType("text/javascript");
22405 } else {
22406     response.setContentType("application/x-json");
22407 }
22408 Writer out = response.getWriter();
22409 if (scriptTag) {
22410     out.write(cb + "(");
22411 }
22412 out.print(dataBlock.toJsonString());
22413 if (scriptTag) {
22414     out.write(");");
22415 }
22416 </pre></code>
22417  *
22418  * @constructor
22419  * @param {Object} config A configuration object.
22420  */
22421 Roo.data.ScriptTagProxy = function(config){
22422     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22423     Roo.apply(this, config);
22424     this.head = document.getElementsByTagName("head")[0];
22425 };
22426
22427 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22428
22429 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22430     /**
22431      * @cfg {String} url The URL from which to request the data object.
22432      */
22433     /**
22434      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22435      */
22436     timeout : 30000,
22437     /**
22438      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22439      * the server the name of the callback function set up by the load call to process the returned data object.
22440      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22441      * javascript output which calls this named function passing the data object as its only parameter.
22442      */
22443     callbackParam : "callback",
22444     /**
22445      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22446      * name to the request.
22447      */
22448     nocache : true,
22449
22450     /**
22451      * Load data from the configured URL, read the data object into
22452      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22453      * process that block using the passed callback.
22454      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22455      * for the request to the remote server.
22456      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22457      * object into a block of Roo.data.Records.
22458      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22459      * The function must be passed <ul>
22460      * <li>The Record block object</li>
22461      * <li>The "arg" argument from the load function</li>
22462      * <li>A boolean success indicator</li>
22463      * </ul>
22464      * @param {Object} scope The scope in which to call the callback
22465      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22466      */
22467     load : function(params, reader, callback, scope, arg){
22468         if(this.fireEvent("beforeload", this, params) !== false){
22469
22470             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22471
22472             var url = this.url;
22473             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22474             if(this.nocache){
22475                 url += "&_dc=" + (new Date().getTime());
22476             }
22477             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22478             var trans = {
22479                 id : transId,
22480                 cb : "stcCallback"+transId,
22481                 scriptId : "stcScript"+transId,
22482                 params : params,
22483                 arg : arg,
22484                 url : url,
22485                 callback : callback,
22486                 scope : scope,
22487                 reader : reader
22488             };
22489             var conn = this;
22490
22491             window[trans.cb] = function(o){
22492                 conn.handleResponse(o, trans);
22493             };
22494
22495             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22496
22497             if(this.autoAbort !== false){
22498                 this.abort();
22499             }
22500
22501             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22502
22503             var script = document.createElement("script");
22504             script.setAttribute("src", url);
22505             script.setAttribute("type", "text/javascript");
22506             script.setAttribute("id", trans.scriptId);
22507             this.head.appendChild(script);
22508
22509             this.trans = trans;
22510         }else{
22511             callback.call(scope||this, null, arg, false);
22512         }
22513     },
22514
22515     // private
22516     isLoading : function(){
22517         return this.trans ? true : false;
22518     },
22519
22520     /**
22521      * Abort the current server request.
22522      */
22523     abort : function(){
22524         if(this.isLoading()){
22525             this.destroyTrans(this.trans);
22526         }
22527     },
22528
22529     // private
22530     destroyTrans : function(trans, isLoaded){
22531         this.head.removeChild(document.getElementById(trans.scriptId));
22532         clearTimeout(trans.timeoutId);
22533         if(isLoaded){
22534             window[trans.cb] = undefined;
22535             try{
22536                 delete window[trans.cb];
22537             }catch(e){}
22538         }else{
22539             // if hasn't been loaded, wait for load to remove it to prevent script error
22540             window[trans.cb] = function(){
22541                 window[trans.cb] = undefined;
22542                 try{
22543                     delete window[trans.cb];
22544                 }catch(e){}
22545             };
22546         }
22547     },
22548
22549     // private
22550     handleResponse : function(o, trans){
22551         this.trans = false;
22552         this.destroyTrans(trans, true);
22553         var result;
22554         try {
22555             result = trans.reader.readRecords(o);
22556         }catch(e){
22557             this.fireEvent("loadexception", this, o, trans.arg, e);
22558             trans.callback.call(trans.scope||window, null, trans.arg, false);
22559             return;
22560         }
22561         this.fireEvent("load", this, o, trans.arg);
22562         trans.callback.call(trans.scope||window, result, trans.arg, true);
22563     },
22564
22565     // private
22566     handleFailure : function(trans){
22567         this.trans = false;
22568         this.destroyTrans(trans, false);
22569         this.fireEvent("loadexception", this, null, trans.arg);
22570         trans.callback.call(trans.scope||window, null, trans.arg, false);
22571     }
22572 });/*
22573  * Based on:
22574  * Ext JS Library 1.1.1
22575  * Copyright(c) 2006-2007, Ext JS, LLC.
22576  *
22577  * Originally Released Under LGPL - original licence link has changed is not relivant.
22578  *
22579  * Fork - LGPL
22580  * <script type="text/javascript">
22581  */
22582
22583 /**
22584  * @class Roo.data.JsonReader
22585  * @extends Roo.data.DataReader
22586  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22587  * based on mappings in a provided Roo.data.Record constructor.
22588  * 
22589  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22590  * in the reply previously. 
22591  * 
22592  * <p>
22593  * Example code:
22594  * <pre><code>
22595 var RecordDef = Roo.data.Record.create([
22596     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22597     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22598 ]);
22599 var myReader = new Roo.data.JsonReader({
22600     totalProperty: "results",    // The property which contains the total dataset size (optional)
22601     root: "rows",                // The property which contains an Array of row objects
22602     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22603 }, RecordDef);
22604 </code></pre>
22605  * <p>
22606  * This would consume a JSON file like this:
22607  * <pre><code>
22608 { 'results': 2, 'rows': [
22609     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22610     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22611 }
22612 </code></pre>
22613  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22614  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22615  * paged from the remote server.
22616  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22617  * @cfg {String} root name of the property which contains the Array of row objects.
22618  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22619  * @cfg {Array} fields Array of field definition objects
22620  * @constructor
22621  * Create a new JsonReader
22622  * @param {Object} meta Metadata configuration options
22623  * @param {Object} recordType Either an Array of field definition objects,
22624  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22625  */
22626 Roo.data.JsonReader = function(meta, recordType){
22627     
22628     meta = meta || {};
22629     // set some defaults:
22630     Roo.applyIf(meta, {
22631         totalProperty: 'total',
22632         successProperty : 'success',
22633         root : 'data',
22634         id : 'id'
22635     });
22636     
22637     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22638 };
22639 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22640     
22641     /**
22642      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22643      * Used by Store query builder to append _requestMeta to params.
22644      * 
22645      */
22646     metaFromRemote : false,
22647     /**
22648      * This method is only used by a DataProxy which has retrieved data from a remote server.
22649      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22650      * @return {Object} data A data block which is used by an Roo.data.Store object as
22651      * a cache of Roo.data.Records.
22652      */
22653     read : function(response){
22654         var json = response.responseText;
22655        
22656         var o = /* eval:var:o */ eval("("+json+")");
22657         if(!o) {
22658             throw {message: "JsonReader.read: Json object not found"};
22659         }
22660         
22661         if(o.metaData){
22662             
22663             delete this.ef;
22664             this.metaFromRemote = true;
22665             this.meta = o.metaData;
22666             this.recordType = Roo.data.Record.create(o.metaData.fields);
22667             this.onMetaChange(this.meta, this.recordType, o);
22668         }
22669         return this.readRecords(o);
22670     },
22671
22672     // private function a store will implement
22673     onMetaChange : function(meta, recordType, o){
22674
22675     },
22676
22677     /**
22678          * @ignore
22679          */
22680     simpleAccess: function(obj, subsc) {
22681         return obj[subsc];
22682     },
22683
22684         /**
22685          * @ignore
22686          */
22687     getJsonAccessor: function(){
22688         var re = /[\[\.]/;
22689         return function(expr) {
22690             try {
22691                 return(re.test(expr))
22692                     ? new Function("obj", "return obj." + expr)
22693                     : function(obj){
22694                         return obj[expr];
22695                     };
22696             } catch(e){}
22697             return Roo.emptyFn;
22698         };
22699     }(),
22700
22701     /**
22702      * Create a data block containing Roo.data.Records from an XML document.
22703      * @param {Object} o An object which contains an Array of row objects in the property specified
22704      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22705      * which contains the total size of the dataset.
22706      * @return {Object} data A data block which is used by an Roo.data.Store object as
22707      * a cache of Roo.data.Records.
22708      */
22709     readRecords : function(o){
22710         /**
22711          * After any data loads, the raw JSON data is available for further custom processing.
22712          * @type Object
22713          */
22714         this.o = o;
22715         var s = this.meta, Record = this.recordType,
22716             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22717
22718 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22719         if (!this.ef) {
22720             if(s.totalProperty) {
22721                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22722                 }
22723                 if(s.successProperty) {
22724                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22725                 }
22726                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22727                 if (s.id) {
22728                         var g = this.getJsonAccessor(s.id);
22729                         this.getId = function(rec) {
22730                                 var r = g(rec);  
22731                                 return (r === undefined || r === "") ? null : r;
22732                         };
22733                 } else {
22734                         this.getId = function(){return null;};
22735                 }
22736             this.ef = [];
22737             for(var jj = 0; jj < fl; jj++){
22738                 f = fi[jj];
22739                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22740                 this.ef[jj] = this.getJsonAccessor(map);
22741             }
22742         }
22743
22744         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22745         if(s.totalProperty){
22746             var vt = parseInt(this.getTotal(o), 10);
22747             if(!isNaN(vt)){
22748                 totalRecords = vt;
22749             }
22750         }
22751         if(s.successProperty){
22752             var vs = this.getSuccess(o);
22753             if(vs === false || vs === 'false'){
22754                 success = false;
22755             }
22756         }
22757         var records = [];
22758         for(var i = 0; i < c; i++){
22759                 var n = root[i];
22760             var values = {};
22761             var id = this.getId(n);
22762             for(var j = 0; j < fl; j++){
22763                 f = fi[j];
22764             var v = this.ef[j](n);
22765             if (!f.convert) {
22766                 Roo.log('missing convert for ' + f.name);
22767                 Roo.log(f);
22768                 continue;
22769             }
22770             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22771             }
22772             var record = new Record(values, id);
22773             record.json = n;
22774             records[i] = record;
22775         }
22776         return {
22777             raw : o,
22778             success : success,
22779             records : records,
22780             totalRecords : totalRecords
22781         };
22782     }
22783 });/*
22784  * Based on:
22785  * Ext JS Library 1.1.1
22786  * Copyright(c) 2006-2007, Ext JS, LLC.
22787  *
22788  * Originally Released Under LGPL - original licence link has changed is not relivant.
22789  *
22790  * Fork - LGPL
22791  * <script type="text/javascript">
22792  */
22793
22794 /**
22795  * @class Roo.data.XmlReader
22796  * @extends Roo.data.DataReader
22797  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22798  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22799  * <p>
22800  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22801  * header in the HTTP response must be set to "text/xml".</em>
22802  * <p>
22803  * Example code:
22804  * <pre><code>
22805 var RecordDef = Roo.data.Record.create([
22806    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22807    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22808 ]);
22809 var myReader = new Roo.data.XmlReader({
22810    totalRecords: "results", // The element which contains the total dataset size (optional)
22811    record: "row",           // The repeated element which contains row information
22812    id: "id"                 // The element within the row that provides an ID for the record (optional)
22813 }, RecordDef);
22814 </code></pre>
22815  * <p>
22816  * This would consume an XML file like this:
22817  * <pre><code>
22818 &lt;?xml?>
22819 &lt;dataset>
22820  &lt;results>2&lt;/results>
22821  &lt;row>
22822    &lt;id>1&lt;/id>
22823    &lt;name>Bill&lt;/name>
22824    &lt;occupation>Gardener&lt;/occupation>
22825  &lt;/row>
22826  &lt;row>
22827    &lt;id>2&lt;/id>
22828    &lt;name>Ben&lt;/name>
22829    &lt;occupation>Horticulturalist&lt;/occupation>
22830  &lt;/row>
22831 &lt;/dataset>
22832 </code></pre>
22833  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22834  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22835  * paged from the remote server.
22836  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22837  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22838  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22839  * a record identifier value.
22840  * @constructor
22841  * Create a new XmlReader
22842  * @param {Object} meta Metadata configuration options
22843  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22844  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22845  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22846  */
22847 Roo.data.XmlReader = function(meta, recordType){
22848     meta = meta || {};
22849     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22850 };
22851 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22852     /**
22853      * This method is only used by a DataProxy which has retrieved data from a remote server.
22854          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22855          * to contain a method called 'responseXML' that returns an XML document object.
22856      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22857      * a cache of Roo.data.Records.
22858      */
22859     read : function(response){
22860         var doc = response.responseXML;
22861         if(!doc) {
22862             throw {message: "XmlReader.read: XML Document not available"};
22863         }
22864         return this.readRecords(doc);
22865     },
22866
22867     /**
22868      * Create a data block containing Roo.data.Records from an XML document.
22869          * @param {Object} doc A parsed XML document.
22870      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22871      * a cache of Roo.data.Records.
22872      */
22873     readRecords : function(doc){
22874         /**
22875          * After any data loads/reads, the raw XML Document is available for further custom processing.
22876          * @type XMLDocument
22877          */
22878         this.xmlData = doc;
22879         var root = doc.documentElement || doc;
22880         var q = Roo.DomQuery;
22881         var recordType = this.recordType, fields = recordType.prototype.fields;
22882         var sid = this.meta.id;
22883         var totalRecords = 0, success = true;
22884         if(this.meta.totalRecords){
22885             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22886         }
22887         
22888         if(this.meta.success){
22889             var sv = q.selectValue(this.meta.success, root, true);
22890             success = sv !== false && sv !== 'false';
22891         }
22892         var records = [];
22893         var ns = q.select(this.meta.record, root);
22894         for(var i = 0, len = ns.length; i < len; i++) {
22895                 var n = ns[i];
22896                 var values = {};
22897                 var id = sid ? q.selectValue(sid, n) : undefined;
22898                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22899                     var f = fields.items[j];
22900                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22901                     v = f.convert(v);
22902                     values[f.name] = v;
22903                 }
22904                 var record = new recordType(values, id);
22905                 record.node = n;
22906                 records[records.length] = record;
22907             }
22908
22909             return {
22910                 success : success,
22911                 records : records,
22912                 totalRecords : totalRecords || records.length
22913             };
22914     }
22915 });/*
22916  * Based on:
22917  * Ext JS Library 1.1.1
22918  * Copyright(c) 2006-2007, Ext JS, LLC.
22919  *
22920  * Originally Released Under LGPL - original licence link has changed is not relivant.
22921  *
22922  * Fork - LGPL
22923  * <script type="text/javascript">
22924  */
22925
22926 /**
22927  * @class Roo.data.ArrayReader
22928  * @extends Roo.data.DataReader
22929  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22930  * Each element of that Array represents a row of data fields. The
22931  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22932  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22933  * <p>
22934  * Example code:.
22935  * <pre><code>
22936 var RecordDef = Roo.data.Record.create([
22937     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22938     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22939 ]);
22940 var myReader = new Roo.data.ArrayReader({
22941     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22942 }, RecordDef);
22943 </code></pre>
22944  * <p>
22945  * This would consume an Array like this:
22946  * <pre><code>
22947 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22948   </code></pre>
22949  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22950  * @constructor
22951  * Create a new JsonReader
22952  * @param {Object} meta Metadata configuration options.
22953  * @param {Object} recordType Either an Array of field definition objects
22954  * as specified to {@link Roo.data.Record#create},
22955  * or an {@link Roo.data.Record} object
22956  * created using {@link Roo.data.Record#create}.
22957  */
22958 Roo.data.ArrayReader = function(meta, recordType){
22959     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22960 };
22961
22962 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22963     /**
22964      * Create a data block containing Roo.data.Records from an XML document.
22965      * @param {Object} o An Array of row objects which represents the dataset.
22966      * @return {Object} data A data block which is used by an Roo.data.Store object as
22967      * a cache of Roo.data.Records.
22968      */
22969     readRecords : function(o){
22970         var sid = this.meta ? this.meta.id : null;
22971         var recordType = this.recordType, fields = recordType.prototype.fields;
22972         var records = [];
22973         var root = o;
22974             for(var i = 0; i < root.length; i++){
22975                     var n = root[i];
22976                 var values = {};
22977                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22978                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22979                 var f = fields.items[j];
22980                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22981                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22982                 v = f.convert(v);
22983                 values[f.name] = v;
22984             }
22985                 var record = new recordType(values, id);
22986                 record.json = n;
22987                 records[records.length] = record;
22988             }
22989             return {
22990                 records : records,
22991                 totalRecords : records.length
22992             };
22993     }
22994 });/*
22995  * Based on:
22996  * Ext JS Library 1.1.1
22997  * Copyright(c) 2006-2007, Ext JS, LLC.
22998  *
22999  * Originally Released Under LGPL - original licence link has changed is not relivant.
23000  *
23001  * Fork - LGPL
23002  * <script type="text/javascript">
23003  */
23004
23005
23006 /**
23007  * @class Roo.data.Tree
23008  * @extends Roo.util.Observable
23009  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23010  * in the tree have most standard DOM functionality.
23011  * @constructor
23012  * @param {Node} root (optional) The root node
23013  */
23014 Roo.data.Tree = function(root){
23015    this.nodeHash = {};
23016    /**
23017     * The root node for this tree
23018     * @type Node
23019     */
23020    this.root = null;
23021    if(root){
23022        this.setRootNode(root);
23023    }
23024    this.addEvents({
23025        /**
23026         * @event append
23027         * Fires when a new child node is appended to a node in this tree.
23028         * @param {Tree} tree The owner tree
23029         * @param {Node} parent The parent node
23030         * @param {Node} node The newly appended node
23031         * @param {Number} index The index of the newly appended node
23032         */
23033        "append" : true,
23034        /**
23035         * @event remove
23036         * Fires when a child node is removed from a node in this tree.
23037         * @param {Tree} tree The owner tree
23038         * @param {Node} parent The parent node
23039         * @param {Node} node The child node removed
23040         */
23041        "remove" : true,
23042        /**
23043         * @event move
23044         * Fires when a node is moved to a new location in the tree
23045         * @param {Tree} tree The owner tree
23046         * @param {Node} node The node moved
23047         * @param {Node} oldParent The old parent of this node
23048         * @param {Node} newParent The new parent of this node
23049         * @param {Number} index The index it was moved to
23050         */
23051        "move" : true,
23052        /**
23053         * @event insert
23054         * Fires when a new child node is inserted in a node in this tree.
23055         * @param {Tree} tree The owner tree
23056         * @param {Node} parent The parent node
23057         * @param {Node} node The child node inserted
23058         * @param {Node} refNode The child node the node was inserted before
23059         */
23060        "insert" : true,
23061        /**
23062         * @event beforeappend
23063         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23064         * @param {Tree} tree The owner tree
23065         * @param {Node} parent The parent node
23066         * @param {Node} node The child node to be appended
23067         */
23068        "beforeappend" : true,
23069        /**
23070         * @event beforeremove
23071         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23072         * @param {Tree} tree The owner tree
23073         * @param {Node} parent The parent node
23074         * @param {Node} node The child node to be removed
23075         */
23076        "beforeremove" : true,
23077        /**
23078         * @event beforemove
23079         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23080         * @param {Tree} tree The owner tree
23081         * @param {Node} node The node being moved
23082         * @param {Node} oldParent The parent of the node
23083         * @param {Node} newParent The new parent the node is moving to
23084         * @param {Number} index The index it is being moved to
23085         */
23086        "beforemove" : true,
23087        /**
23088         * @event beforeinsert
23089         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23090         * @param {Tree} tree The owner tree
23091         * @param {Node} parent The parent node
23092         * @param {Node} node The child node to be inserted
23093         * @param {Node} refNode The child node the node is being inserted before
23094         */
23095        "beforeinsert" : true
23096    });
23097
23098     Roo.data.Tree.superclass.constructor.call(this);
23099 };
23100
23101 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23102     pathSeparator: "/",
23103
23104     proxyNodeEvent : function(){
23105         return this.fireEvent.apply(this, arguments);
23106     },
23107
23108     /**
23109      * Returns the root node for this tree.
23110      * @return {Node}
23111      */
23112     getRootNode : function(){
23113         return this.root;
23114     },
23115
23116     /**
23117      * Sets the root node for this tree.
23118      * @param {Node} node
23119      * @return {Node}
23120      */
23121     setRootNode : function(node){
23122         this.root = node;
23123         node.ownerTree = this;
23124         node.isRoot = true;
23125         this.registerNode(node);
23126         return node;
23127     },
23128
23129     /**
23130      * Gets a node in this tree by its id.
23131      * @param {String} id
23132      * @return {Node}
23133      */
23134     getNodeById : function(id){
23135         return this.nodeHash[id];
23136     },
23137
23138     registerNode : function(node){
23139         this.nodeHash[node.id] = node;
23140     },
23141
23142     unregisterNode : function(node){
23143         delete this.nodeHash[node.id];
23144     },
23145
23146     toString : function(){
23147         return "[Tree"+(this.id?" "+this.id:"")+"]";
23148     }
23149 });
23150
23151 /**
23152  * @class Roo.data.Node
23153  * @extends Roo.util.Observable
23154  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23155  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23156  * @constructor
23157  * @param {Object} attributes The attributes/config for the node
23158  */
23159 Roo.data.Node = function(attributes){
23160     /**
23161      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23162      * @type {Object}
23163      */
23164     this.attributes = attributes || {};
23165     this.leaf = this.attributes.leaf;
23166     /**
23167      * The node id. @type String
23168      */
23169     this.id = this.attributes.id;
23170     if(!this.id){
23171         this.id = Roo.id(null, "ynode-");
23172         this.attributes.id = this.id;
23173     }
23174      
23175     
23176     /**
23177      * All child nodes of this node. @type Array
23178      */
23179     this.childNodes = [];
23180     if(!this.childNodes.indexOf){ // indexOf is a must
23181         this.childNodes.indexOf = function(o){
23182             for(var i = 0, len = this.length; i < len; i++){
23183                 if(this[i] == o) {
23184                     return i;
23185                 }
23186             }
23187             return -1;
23188         };
23189     }
23190     /**
23191      * The parent node for this node. @type Node
23192      */
23193     this.parentNode = null;
23194     /**
23195      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23196      */
23197     this.firstChild = null;
23198     /**
23199      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23200      */
23201     this.lastChild = null;
23202     /**
23203      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23204      */
23205     this.previousSibling = null;
23206     /**
23207      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23208      */
23209     this.nextSibling = null;
23210
23211     this.addEvents({
23212        /**
23213         * @event append
23214         * Fires when a new child node is appended
23215         * @param {Tree} tree The owner tree
23216         * @param {Node} this This node
23217         * @param {Node} node The newly appended node
23218         * @param {Number} index The index of the newly appended node
23219         */
23220        "append" : true,
23221        /**
23222         * @event remove
23223         * Fires when a child node is removed
23224         * @param {Tree} tree The owner tree
23225         * @param {Node} this This node
23226         * @param {Node} node The removed node
23227         */
23228        "remove" : true,
23229        /**
23230         * @event move
23231         * Fires when this node is moved to a new location in the tree
23232         * @param {Tree} tree The owner tree
23233         * @param {Node} this This node
23234         * @param {Node} oldParent The old parent of this node
23235         * @param {Node} newParent The new parent of this node
23236         * @param {Number} index The index it was moved to
23237         */
23238        "move" : true,
23239        /**
23240         * @event insert
23241         * Fires when a new child node is inserted.
23242         * @param {Tree} tree The owner tree
23243         * @param {Node} this This node
23244         * @param {Node} node The child node inserted
23245         * @param {Node} refNode The child node the node was inserted before
23246         */
23247        "insert" : true,
23248        /**
23249         * @event beforeappend
23250         * Fires before a new child is appended, return false to cancel the append.
23251         * @param {Tree} tree The owner tree
23252         * @param {Node} this This node
23253         * @param {Node} node The child node to be appended
23254         */
23255        "beforeappend" : true,
23256        /**
23257         * @event beforeremove
23258         * Fires before a child is removed, return false to cancel the remove.
23259         * @param {Tree} tree The owner tree
23260         * @param {Node} this This node
23261         * @param {Node} node The child node to be removed
23262         */
23263        "beforeremove" : true,
23264        /**
23265         * @event beforemove
23266         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23267         * @param {Tree} tree The owner tree
23268         * @param {Node} this This node
23269         * @param {Node} oldParent The parent of this node
23270         * @param {Node} newParent The new parent this node is moving to
23271         * @param {Number} index The index it is being moved to
23272         */
23273        "beforemove" : true,
23274        /**
23275         * @event beforeinsert
23276         * Fires before a new child is inserted, return false to cancel the insert.
23277         * @param {Tree} tree The owner tree
23278         * @param {Node} this This node
23279         * @param {Node} node The child node to be inserted
23280         * @param {Node} refNode The child node the node is being inserted before
23281         */
23282        "beforeinsert" : true
23283    });
23284     this.listeners = this.attributes.listeners;
23285     Roo.data.Node.superclass.constructor.call(this);
23286 };
23287
23288 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23289     fireEvent : function(evtName){
23290         // first do standard event for this node
23291         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23292             return false;
23293         }
23294         // then bubble it up to the tree if the event wasn't cancelled
23295         var ot = this.getOwnerTree();
23296         if(ot){
23297             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23298                 return false;
23299             }
23300         }
23301         return true;
23302     },
23303
23304     /**
23305      * Returns true if this node is a leaf
23306      * @return {Boolean}
23307      */
23308     isLeaf : function(){
23309         return this.leaf === true;
23310     },
23311
23312     // private
23313     setFirstChild : function(node){
23314         this.firstChild = node;
23315     },
23316
23317     //private
23318     setLastChild : function(node){
23319         this.lastChild = node;
23320     },
23321
23322
23323     /**
23324      * Returns true if this node is the last child of its parent
23325      * @return {Boolean}
23326      */
23327     isLast : function(){
23328        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23329     },
23330
23331     /**
23332      * Returns true if this node is the first child of its parent
23333      * @return {Boolean}
23334      */
23335     isFirst : function(){
23336        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23337     },
23338
23339     hasChildNodes : function(){
23340         return !this.isLeaf() && this.childNodes.length > 0;
23341     },
23342
23343     /**
23344      * Insert node(s) as the last child node of this node.
23345      * @param {Node/Array} node The node or Array of nodes to append
23346      * @return {Node} The appended node if single append, or null if an array was passed
23347      */
23348     appendChild : function(node){
23349         var multi = false;
23350         if(node instanceof Array){
23351             multi = node;
23352         }else if(arguments.length > 1){
23353             multi = arguments;
23354         }
23355         // if passed an array or multiple args do them one by one
23356         if(multi){
23357             for(var i = 0, len = multi.length; i < len; i++) {
23358                 this.appendChild(multi[i]);
23359             }
23360         }else{
23361             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23362                 return false;
23363             }
23364             var index = this.childNodes.length;
23365             var oldParent = node.parentNode;
23366             // it's a move, make sure we move it cleanly
23367             if(oldParent){
23368                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23369                     return false;
23370                 }
23371                 oldParent.removeChild(node);
23372             }
23373             index = this.childNodes.length;
23374             if(index == 0){
23375                 this.setFirstChild(node);
23376             }
23377             this.childNodes.push(node);
23378             node.parentNode = this;
23379             var ps = this.childNodes[index-1];
23380             if(ps){
23381                 node.previousSibling = ps;
23382                 ps.nextSibling = node;
23383             }else{
23384                 node.previousSibling = null;
23385             }
23386             node.nextSibling = null;
23387             this.setLastChild(node);
23388             node.setOwnerTree(this.getOwnerTree());
23389             this.fireEvent("append", this.ownerTree, this, node, index);
23390             if(oldParent){
23391                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23392             }
23393             return node;
23394         }
23395     },
23396
23397     /**
23398      * Removes a child node from this node.
23399      * @param {Node} node The node to remove
23400      * @return {Node} The removed node
23401      */
23402     removeChild : function(node){
23403         var index = this.childNodes.indexOf(node);
23404         if(index == -1){
23405             return false;
23406         }
23407         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23408             return false;
23409         }
23410
23411         // remove it from childNodes collection
23412         this.childNodes.splice(index, 1);
23413
23414         // update siblings
23415         if(node.previousSibling){
23416             node.previousSibling.nextSibling = node.nextSibling;
23417         }
23418         if(node.nextSibling){
23419             node.nextSibling.previousSibling = node.previousSibling;
23420         }
23421
23422         // update child refs
23423         if(this.firstChild == node){
23424             this.setFirstChild(node.nextSibling);
23425         }
23426         if(this.lastChild == node){
23427             this.setLastChild(node.previousSibling);
23428         }
23429
23430         node.setOwnerTree(null);
23431         // clear any references from the node
23432         node.parentNode = null;
23433         node.previousSibling = null;
23434         node.nextSibling = null;
23435         this.fireEvent("remove", this.ownerTree, this, node);
23436         return node;
23437     },
23438
23439     /**
23440      * Inserts the first node before the second node in this nodes childNodes collection.
23441      * @param {Node} node The node to insert
23442      * @param {Node} refNode The node to insert before (if null the node is appended)
23443      * @return {Node} The inserted node
23444      */
23445     insertBefore : function(node, refNode){
23446         if(!refNode){ // like standard Dom, refNode can be null for append
23447             return this.appendChild(node);
23448         }
23449         // nothing to do
23450         if(node == refNode){
23451             return false;
23452         }
23453
23454         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23455             return false;
23456         }
23457         var index = this.childNodes.indexOf(refNode);
23458         var oldParent = node.parentNode;
23459         var refIndex = index;
23460
23461         // when moving internally, indexes will change after remove
23462         if(oldParent == this && this.childNodes.indexOf(node) < index){
23463             refIndex--;
23464         }
23465
23466         // it's a move, make sure we move it cleanly
23467         if(oldParent){
23468             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23469                 return false;
23470             }
23471             oldParent.removeChild(node);
23472         }
23473         if(refIndex == 0){
23474             this.setFirstChild(node);
23475         }
23476         this.childNodes.splice(refIndex, 0, node);
23477         node.parentNode = this;
23478         var ps = this.childNodes[refIndex-1];
23479         if(ps){
23480             node.previousSibling = ps;
23481             ps.nextSibling = node;
23482         }else{
23483             node.previousSibling = null;
23484         }
23485         node.nextSibling = refNode;
23486         refNode.previousSibling = node;
23487         node.setOwnerTree(this.getOwnerTree());
23488         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23489         if(oldParent){
23490             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23491         }
23492         return node;
23493     },
23494
23495     /**
23496      * Returns the child node at the specified index.
23497      * @param {Number} index
23498      * @return {Node}
23499      */
23500     item : function(index){
23501         return this.childNodes[index];
23502     },
23503
23504     /**
23505      * Replaces one child node in this node with another.
23506      * @param {Node} newChild The replacement node
23507      * @param {Node} oldChild The node to replace
23508      * @return {Node} The replaced node
23509      */
23510     replaceChild : function(newChild, oldChild){
23511         this.insertBefore(newChild, oldChild);
23512         this.removeChild(oldChild);
23513         return oldChild;
23514     },
23515
23516     /**
23517      * Returns the index of a child node
23518      * @param {Node} node
23519      * @return {Number} The index of the node or -1 if it was not found
23520      */
23521     indexOf : function(child){
23522         return this.childNodes.indexOf(child);
23523     },
23524
23525     /**
23526      * Returns the tree this node is in.
23527      * @return {Tree}
23528      */
23529     getOwnerTree : function(){
23530         // if it doesn't have one, look for one
23531         if(!this.ownerTree){
23532             var p = this;
23533             while(p){
23534                 if(p.ownerTree){
23535                     this.ownerTree = p.ownerTree;
23536                     break;
23537                 }
23538                 p = p.parentNode;
23539             }
23540         }
23541         return this.ownerTree;
23542     },
23543
23544     /**
23545      * Returns depth of this node (the root node has a depth of 0)
23546      * @return {Number}
23547      */
23548     getDepth : function(){
23549         var depth = 0;
23550         var p = this;
23551         while(p.parentNode){
23552             ++depth;
23553             p = p.parentNode;
23554         }
23555         return depth;
23556     },
23557
23558     // private
23559     setOwnerTree : function(tree){
23560         // if it's move, we need to update everyone
23561         if(tree != this.ownerTree){
23562             if(this.ownerTree){
23563                 this.ownerTree.unregisterNode(this);
23564             }
23565             this.ownerTree = tree;
23566             var cs = this.childNodes;
23567             for(var i = 0, len = cs.length; i < len; i++) {
23568                 cs[i].setOwnerTree(tree);
23569             }
23570             if(tree){
23571                 tree.registerNode(this);
23572             }
23573         }
23574     },
23575
23576     /**
23577      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23578      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23579      * @return {String} The path
23580      */
23581     getPath : function(attr){
23582         attr = attr || "id";
23583         var p = this.parentNode;
23584         var b = [this.attributes[attr]];
23585         while(p){
23586             b.unshift(p.attributes[attr]);
23587             p = p.parentNode;
23588         }
23589         var sep = this.getOwnerTree().pathSeparator;
23590         return sep + b.join(sep);
23591     },
23592
23593     /**
23594      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23595      * function call will be the scope provided or the current node. The arguments to the function
23596      * will be the args provided or the current node. If the function returns false at any point,
23597      * the bubble is stopped.
23598      * @param {Function} fn The function to call
23599      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23600      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23601      */
23602     bubble : function(fn, scope, args){
23603         var p = this;
23604         while(p){
23605             if(fn.call(scope || p, args || p) === false){
23606                 break;
23607             }
23608             p = p.parentNode;
23609         }
23610     },
23611
23612     /**
23613      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23614      * function call will be the scope provided or the current node. The arguments to the function
23615      * will be the args provided or the current node. If the function returns false at any point,
23616      * the cascade is stopped on that branch.
23617      * @param {Function} fn The function to call
23618      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23619      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23620      */
23621     cascade : function(fn, scope, args){
23622         if(fn.call(scope || this, args || this) !== false){
23623             var cs = this.childNodes;
23624             for(var i = 0, len = cs.length; i < len; i++) {
23625                 cs[i].cascade(fn, scope, args);
23626             }
23627         }
23628     },
23629
23630     /**
23631      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23632      * function call will be the scope provided or the current node. The arguments to the function
23633      * will be the args provided or the current node. If the function returns false at any point,
23634      * the iteration stops.
23635      * @param {Function} fn The function to call
23636      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23637      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23638      */
23639     eachChild : function(fn, scope, args){
23640         var cs = this.childNodes;
23641         for(var i = 0, len = cs.length; i < len; i++) {
23642                 if(fn.call(scope || this, args || cs[i]) === false){
23643                     break;
23644                 }
23645         }
23646     },
23647
23648     /**
23649      * Finds the first child that has the attribute with the specified value.
23650      * @param {String} attribute The attribute name
23651      * @param {Mixed} value The value to search for
23652      * @return {Node} The found child or null if none was found
23653      */
23654     findChild : function(attribute, value){
23655         var cs = this.childNodes;
23656         for(var i = 0, len = cs.length; i < len; i++) {
23657                 if(cs[i].attributes[attribute] == value){
23658                     return cs[i];
23659                 }
23660         }
23661         return null;
23662     },
23663
23664     /**
23665      * Finds the first child by a custom function. The child matches if the function passed
23666      * returns true.
23667      * @param {Function} fn
23668      * @param {Object} scope (optional)
23669      * @return {Node} The found child or null if none was found
23670      */
23671     findChildBy : function(fn, scope){
23672         var cs = this.childNodes;
23673         for(var i = 0, len = cs.length; i < len; i++) {
23674                 if(fn.call(scope||cs[i], cs[i]) === true){
23675                     return cs[i];
23676                 }
23677         }
23678         return null;
23679     },
23680
23681     /**
23682      * Sorts this nodes children using the supplied sort function
23683      * @param {Function} fn
23684      * @param {Object} scope (optional)
23685      */
23686     sort : function(fn, scope){
23687         var cs = this.childNodes;
23688         var len = cs.length;
23689         if(len > 0){
23690             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23691             cs.sort(sortFn);
23692             for(var i = 0; i < len; i++){
23693                 var n = cs[i];
23694                 n.previousSibling = cs[i-1];
23695                 n.nextSibling = cs[i+1];
23696                 if(i == 0){
23697                     this.setFirstChild(n);
23698                 }
23699                 if(i == len-1){
23700                     this.setLastChild(n);
23701                 }
23702             }
23703         }
23704     },
23705
23706     /**
23707      * Returns true if this node is an ancestor (at any point) of the passed node.
23708      * @param {Node} node
23709      * @return {Boolean}
23710      */
23711     contains : function(node){
23712         return node.isAncestor(this);
23713     },
23714
23715     /**
23716      * Returns true if the passed node is an ancestor (at any point) of this node.
23717      * @param {Node} node
23718      * @return {Boolean}
23719      */
23720     isAncestor : function(node){
23721         var p = this.parentNode;
23722         while(p){
23723             if(p == node){
23724                 return true;
23725             }
23726             p = p.parentNode;
23727         }
23728         return false;
23729     },
23730
23731     toString : function(){
23732         return "[Node"+(this.id?" "+this.id:"")+"]";
23733     }
23734 });/*
23735  * Based on:
23736  * Ext JS Library 1.1.1
23737  * Copyright(c) 2006-2007, Ext JS, LLC.
23738  *
23739  * Originally Released Under LGPL - original licence link has changed is not relivant.
23740  *
23741  * Fork - LGPL
23742  * <script type="text/javascript">
23743  */
23744  (function(){ 
23745 /**
23746  * @class Roo.Layer
23747  * @extends Roo.Element
23748  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23749  * automatic maintaining of shadow/shim positions.
23750  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23751  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23752  * you can pass a string with a CSS class name. False turns off the shadow.
23753  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23754  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23755  * @cfg {String} cls CSS class to add to the element
23756  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23757  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23758  * @constructor
23759  * @param {Object} config An object with config options.
23760  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23761  */
23762
23763 Roo.Layer = function(config, existingEl){
23764     config = config || {};
23765     var dh = Roo.DomHelper;
23766     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23767     if(existingEl){
23768         this.dom = Roo.getDom(existingEl);
23769     }
23770     if(!this.dom){
23771         var o = config.dh || {tag: "div", cls: "x-layer"};
23772         this.dom = dh.append(pel, o);
23773     }
23774     if(config.cls){
23775         this.addClass(config.cls);
23776     }
23777     this.constrain = config.constrain !== false;
23778     this.visibilityMode = Roo.Element.VISIBILITY;
23779     if(config.id){
23780         this.id = this.dom.id = config.id;
23781     }else{
23782         this.id = Roo.id(this.dom);
23783     }
23784     this.zindex = config.zindex || this.getZIndex();
23785     this.position("absolute", this.zindex);
23786     if(config.shadow){
23787         this.shadowOffset = config.shadowOffset || 4;
23788         this.shadow = new Roo.Shadow({
23789             offset : this.shadowOffset,
23790             mode : config.shadow
23791         });
23792     }else{
23793         this.shadowOffset = 0;
23794     }
23795     this.useShim = config.shim !== false && Roo.useShims;
23796     this.useDisplay = config.useDisplay;
23797     this.hide();
23798 };
23799
23800 var supr = Roo.Element.prototype;
23801
23802 // shims are shared among layer to keep from having 100 iframes
23803 var shims = [];
23804
23805 Roo.extend(Roo.Layer, Roo.Element, {
23806
23807     getZIndex : function(){
23808         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23809     },
23810
23811     getShim : function(){
23812         if(!this.useShim){
23813             return null;
23814         }
23815         if(this.shim){
23816             return this.shim;
23817         }
23818         var shim = shims.shift();
23819         if(!shim){
23820             shim = this.createShim();
23821             shim.enableDisplayMode('block');
23822             shim.dom.style.display = 'none';
23823             shim.dom.style.visibility = 'visible';
23824         }
23825         var pn = this.dom.parentNode;
23826         if(shim.dom.parentNode != pn){
23827             pn.insertBefore(shim.dom, this.dom);
23828         }
23829         shim.setStyle('z-index', this.getZIndex()-2);
23830         this.shim = shim;
23831         return shim;
23832     },
23833
23834     hideShim : function(){
23835         if(this.shim){
23836             this.shim.setDisplayed(false);
23837             shims.push(this.shim);
23838             delete this.shim;
23839         }
23840     },
23841
23842     disableShadow : function(){
23843         if(this.shadow){
23844             this.shadowDisabled = true;
23845             this.shadow.hide();
23846             this.lastShadowOffset = this.shadowOffset;
23847             this.shadowOffset = 0;
23848         }
23849     },
23850
23851     enableShadow : function(show){
23852         if(this.shadow){
23853             this.shadowDisabled = false;
23854             this.shadowOffset = this.lastShadowOffset;
23855             delete this.lastShadowOffset;
23856             if(show){
23857                 this.sync(true);
23858             }
23859         }
23860     },
23861
23862     // private
23863     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23864     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23865     sync : function(doShow){
23866         var sw = this.shadow;
23867         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23868             var sh = this.getShim();
23869
23870             var w = this.getWidth(),
23871                 h = this.getHeight();
23872
23873             var l = this.getLeft(true),
23874                 t = this.getTop(true);
23875
23876             if(sw && !this.shadowDisabled){
23877                 if(doShow && !sw.isVisible()){
23878                     sw.show(this);
23879                 }else{
23880                     sw.realign(l, t, w, h);
23881                 }
23882                 if(sh){
23883                     if(doShow){
23884                        sh.show();
23885                     }
23886                     // fit the shim behind the shadow, so it is shimmed too
23887                     var a = sw.adjusts, s = sh.dom.style;
23888                     s.left = (Math.min(l, l+a.l))+"px";
23889                     s.top = (Math.min(t, t+a.t))+"px";
23890                     s.width = (w+a.w)+"px";
23891                     s.height = (h+a.h)+"px";
23892                 }
23893             }else if(sh){
23894                 if(doShow){
23895                    sh.show();
23896                 }
23897                 sh.setSize(w, h);
23898                 sh.setLeftTop(l, t);
23899             }
23900             
23901         }
23902     },
23903
23904     // private
23905     destroy : function(){
23906         this.hideShim();
23907         if(this.shadow){
23908             this.shadow.hide();
23909         }
23910         this.removeAllListeners();
23911         var pn = this.dom.parentNode;
23912         if(pn){
23913             pn.removeChild(this.dom);
23914         }
23915         Roo.Element.uncache(this.id);
23916     },
23917
23918     remove : function(){
23919         this.destroy();
23920     },
23921
23922     // private
23923     beginUpdate : function(){
23924         this.updating = true;
23925     },
23926
23927     // private
23928     endUpdate : function(){
23929         this.updating = false;
23930         this.sync(true);
23931     },
23932
23933     // private
23934     hideUnders : function(negOffset){
23935         if(this.shadow){
23936             this.shadow.hide();
23937         }
23938         this.hideShim();
23939     },
23940
23941     // private
23942     constrainXY : function(){
23943         if(this.constrain){
23944             var vw = Roo.lib.Dom.getViewWidth(),
23945                 vh = Roo.lib.Dom.getViewHeight();
23946             var s = Roo.get(document).getScroll();
23947
23948             var xy = this.getXY();
23949             var x = xy[0], y = xy[1];   
23950             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23951             // only move it if it needs it
23952             var moved = false;
23953             // first validate right/bottom
23954             if((x + w) > vw+s.left){
23955                 x = vw - w - this.shadowOffset;
23956                 moved = true;
23957             }
23958             if((y + h) > vh+s.top){
23959                 y = vh - h - this.shadowOffset;
23960                 moved = true;
23961             }
23962             // then make sure top/left isn't negative
23963             if(x < s.left){
23964                 x = s.left;
23965                 moved = true;
23966             }
23967             if(y < s.top){
23968                 y = s.top;
23969                 moved = true;
23970             }
23971             if(moved){
23972                 if(this.avoidY){
23973                     var ay = this.avoidY;
23974                     if(y <= ay && (y+h) >= ay){
23975                         y = ay-h-5;   
23976                     }
23977                 }
23978                 xy = [x, y];
23979                 this.storeXY(xy);
23980                 supr.setXY.call(this, xy);
23981                 this.sync();
23982             }
23983         }
23984     },
23985
23986     isVisible : function(){
23987         return this.visible;    
23988     },
23989
23990     // private
23991     showAction : function(){
23992         this.visible = true; // track visibility to prevent getStyle calls
23993         if(this.useDisplay === true){
23994             this.setDisplayed("");
23995         }else if(this.lastXY){
23996             supr.setXY.call(this, this.lastXY);
23997         }else if(this.lastLT){
23998             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23999         }
24000     },
24001
24002     // private
24003     hideAction : function(){
24004         this.visible = false;
24005         if(this.useDisplay === true){
24006             this.setDisplayed(false);
24007         }else{
24008             this.setLeftTop(-10000,-10000);
24009         }
24010     },
24011
24012     // overridden Element method
24013     setVisible : function(v, a, d, c, e){
24014         if(v){
24015             this.showAction();
24016         }
24017         if(a && v){
24018             var cb = function(){
24019                 this.sync(true);
24020                 if(c){
24021                     c();
24022                 }
24023             }.createDelegate(this);
24024             supr.setVisible.call(this, true, true, d, cb, e);
24025         }else{
24026             if(!v){
24027                 this.hideUnders(true);
24028             }
24029             var cb = c;
24030             if(a){
24031                 cb = function(){
24032                     this.hideAction();
24033                     if(c){
24034                         c();
24035                     }
24036                 }.createDelegate(this);
24037             }
24038             supr.setVisible.call(this, v, a, d, cb, e);
24039             if(v){
24040                 this.sync(true);
24041             }else if(!a){
24042                 this.hideAction();
24043             }
24044         }
24045     },
24046
24047     storeXY : function(xy){
24048         delete this.lastLT;
24049         this.lastXY = xy;
24050     },
24051
24052     storeLeftTop : function(left, top){
24053         delete this.lastXY;
24054         this.lastLT = [left, top];
24055     },
24056
24057     // private
24058     beforeFx : function(){
24059         this.beforeAction();
24060         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24061     },
24062
24063     // private
24064     afterFx : function(){
24065         Roo.Layer.superclass.afterFx.apply(this, arguments);
24066         this.sync(this.isVisible());
24067     },
24068
24069     // private
24070     beforeAction : function(){
24071         if(!this.updating && this.shadow){
24072             this.shadow.hide();
24073         }
24074     },
24075
24076     // overridden Element method
24077     setLeft : function(left){
24078         this.storeLeftTop(left, this.getTop(true));
24079         supr.setLeft.apply(this, arguments);
24080         this.sync();
24081     },
24082
24083     setTop : function(top){
24084         this.storeLeftTop(this.getLeft(true), top);
24085         supr.setTop.apply(this, arguments);
24086         this.sync();
24087     },
24088
24089     setLeftTop : function(left, top){
24090         this.storeLeftTop(left, top);
24091         supr.setLeftTop.apply(this, arguments);
24092         this.sync();
24093     },
24094
24095     setXY : function(xy, a, d, c, e){
24096         this.fixDisplay();
24097         this.beforeAction();
24098         this.storeXY(xy);
24099         var cb = this.createCB(c);
24100         supr.setXY.call(this, xy, a, d, cb, e);
24101         if(!a){
24102             cb();
24103         }
24104     },
24105
24106     // private
24107     createCB : function(c){
24108         var el = this;
24109         return function(){
24110             el.constrainXY();
24111             el.sync(true);
24112             if(c){
24113                 c();
24114             }
24115         };
24116     },
24117
24118     // overridden Element method
24119     setX : function(x, a, d, c, e){
24120         this.setXY([x, this.getY()], a, d, c, e);
24121     },
24122
24123     // overridden Element method
24124     setY : function(y, a, d, c, e){
24125         this.setXY([this.getX(), y], a, d, c, e);
24126     },
24127
24128     // overridden Element method
24129     setSize : function(w, h, a, d, c, e){
24130         this.beforeAction();
24131         var cb = this.createCB(c);
24132         supr.setSize.call(this, w, h, a, d, cb, e);
24133         if(!a){
24134             cb();
24135         }
24136     },
24137
24138     // overridden Element method
24139     setWidth : function(w, a, d, c, e){
24140         this.beforeAction();
24141         var cb = this.createCB(c);
24142         supr.setWidth.call(this, w, a, d, cb, e);
24143         if(!a){
24144             cb();
24145         }
24146     },
24147
24148     // overridden Element method
24149     setHeight : function(h, a, d, c, e){
24150         this.beforeAction();
24151         var cb = this.createCB(c);
24152         supr.setHeight.call(this, h, a, d, cb, e);
24153         if(!a){
24154             cb();
24155         }
24156     },
24157
24158     // overridden Element method
24159     setBounds : function(x, y, w, h, a, d, c, e){
24160         this.beforeAction();
24161         var cb = this.createCB(c);
24162         if(!a){
24163             this.storeXY([x, y]);
24164             supr.setXY.call(this, [x, y]);
24165             supr.setSize.call(this, w, h, a, d, cb, e);
24166             cb();
24167         }else{
24168             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24169         }
24170         return this;
24171     },
24172     
24173     /**
24174      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24175      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24176      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24177      * @param {Number} zindex The new z-index to set
24178      * @return {this} The Layer
24179      */
24180     setZIndex : function(zindex){
24181         this.zindex = zindex;
24182         this.setStyle("z-index", zindex + 2);
24183         if(this.shadow){
24184             this.shadow.setZIndex(zindex + 1);
24185         }
24186         if(this.shim){
24187             this.shim.setStyle("z-index", zindex);
24188         }
24189     }
24190 });
24191 })();/*
24192  * Based on:
24193  * Ext JS Library 1.1.1
24194  * Copyright(c) 2006-2007, Ext JS, LLC.
24195  *
24196  * Originally Released Under LGPL - original licence link has changed is not relivant.
24197  *
24198  * Fork - LGPL
24199  * <script type="text/javascript">
24200  */
24201
24202
24203 /**
24204  * @class Roo.Shadow
24205  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24206  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24207  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24208  * @constructor
24209  * Create a new Shadow
24210  * @param {Object} config The config object
24211  */
24212 Roo.Shadow = function(config){
24213     Roo.apply(this, config);
24214     if(typeof this.mode != "string"){
24215         this.mode = this.defaultMode;
24216     }
24217     var o = this.offset, a = {h: 0};
24218     var rad = Math.floor(this.offset/2);
24219     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24220         case "drop":
24221             a.w = 0;
24222             a.l = a.t = o;
24223             a.t -= 1;
24224             if(Roo.isIE){
24225                 a.l -= this.offset + rad;
24226                 a.t -= this.offset + rad;
24227                 a.w -= rad;
24228                 a.h -= rad;
24229                 a.t += 1;
24230             }
24231         break;
24232         case "sides":
24233             a.w = (o*2);
24234             a.l = -o;
24235             a.t = o-1;
24236             if(Roo.isIE){
24237                 a.l -= (this.offset - rad);
24238                 a.t -= this.offset + rad;
24239                 a.l += 1;
24240                 a.w -= (this.offset - rad)*2;
24241                 a.w -= rad + 1;
24242                 a.h -= 1;
24243             }
24244         break;
24245         case "frame":
24246             a.w = a.h = (o*2);
24247             a.l = a.t = -o;
24248             a.t += 1;
24249             a.h -= 2;
24250             if(Roo.isIE){
24251                 a.l -= (this.offset - rad);
24252                 a.t -= (this.offset - rad);
24253                 a.l += 1;
24254                 a.w -= (this.offset + rad + 1);
24255                 a.h -= (this.offset + rad);
24256                 a.h += 1;
24257             }
24258         break;
24259     };
24260
24261     this.adjusts = a;
24262 };
24263
24264 Roo.Shadow.prototype = {
24265     /**
24266      * @cfg {String} mode
24267      * The shadow display mode.  Supports the following options:<br />
24268      * sides: Shadow displays on both sides and bottom only<br />
24269      * frame: Shadow displays equally on all four sides<br />
24270      * drop: Traditional bottom-right drop shadow (default)
24271      */
24272     /**
24273      * @cfg {String} offset
24274      * The number of pixels to offset the shadow from the element (defaults to 4)
24275      */
24276     offset: 4,
24277
24278     // private
24279     defaultMode: "drop",
24280
24281     /**
24282      * Displays the shadow under the target element
24283      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24284      */
24285     show : function(target){
24286         target = Roo.get(target);
24287         if(!this.el){
24288             this.el = Roo.Shadow.Pool.pull();
24289             if(this.el.dom.nextSibling != target.dom){
24290                 this.el.insertBefore(target);
24291             }
24292         }
24293         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24294         if(Roo.isIE){
24295             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24296         }
24297         this.realign(
24298             target.getLeft(true),
24299             target.getTop(true),
24300             target.getWidth(),
24301             target.getHeight()
24302         );
24303         this.el.dom.style.display = "block";
24304     },
24305
24306     /**
24307      * Returns true if the shadow is visible, else false
24308      */
24309     isVisible : function(){
24310         return this.el ? true : false;  
24311     },
24312
24313     /**
24314      * Direct alignment when values are already available. Show must be called at least once before
24315      * calling this method to ensure it is initialized.
24316      * @param {Number} left The target element left position
24317      * @param {Number} top The target element top position
24318      * @param {Number} width The target element width
24319      * @param {Number} height The target element height
24320      */
24321     realign : function(l, t, w, h){
24322         if(!this.el){
24323             return;
24324         }
24325         var a = this.adjusts, d = this.el.dom, s = d.style;
24326         var iea = 0;
24327         s.left = (l+a.l)+"px";
24328         s.top = (t+a.t)+"px";
24329         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24330  
24331         if(s.width != sws || s.height != shs){
24332             s.width = sws;
24333             s.height = shs;
24334             if(!Roo.isIE){
24335                 var cn = d.childNodes;
24336                 var sww = Math.max(0, (sw-12))+"px";
24337                 cn[0].childNodes[1].style.width = sww;
24338                 cn[1].childNodes[1].style.width = sww;
24339                 cn[2].childNodes[1].style.width = sww;
24340                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24341             }
24342         }
24343     },
24344
24345     /**
24346      * Hides this shadow
24347      */
24348     hide : function(){
24349         if(this.el){
24350             this.el.dom.style.display = "none";
24351             Roo.Shadow.Pool.push(this.el);
24352             delete this.el;
24353         }
24354     },
24355
24356     /**
24357      * Adjust the z-index of this shadow
24358      * @param {Number} zindex The new z-index
24359      */
24360     setZIndex : function(z){
24361         this.zIndex = z;
24362         if(this.el){
24363             this.el.setStyle("z-index", z);
24364         }
24365     }
24366 };
24367
24368 // Private utility class that manages the internal Shadow cache
24369 Roo.Shadow.Pool = function(){
24370     var p = [];
24371     var markup = Roo.isIE ?
24372                  '<div class="x-ie-shadow"></div>' :
24373                  '<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>';
24374     return {
24375         pull : function(){
24376             var sh = p.shift();
24377             if(!sh){
24378                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24379                 sh.autoBoxAdjust = false;
24380             }
24381             return sh;
24382         },
24383
24384         push : function(sh){
24385             p.push(sh);
24386         }
24387     };
24388 }();/*
24389  * Based on:
24390  * Ext JS Library 1.1.1
24391  * Copyright(c) 2006-2007, Ext JS, LLC.
24392  *
24393  * Originally Released Under LGPL - original licence link has changed is not relivant.
24394  *
24395  * Fork - LGPL
24396  * <script type="text/javascript">
24397  */
24398
24399
24400 /**
24401  * @class Roo.SplitBar
24402  * @extends Roo.util.Observable
24403  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24404  * <br><br>
24405  * Usage:
24406  * <pre><code>
24407 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24408                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24409 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24410 split.minSize = 100;
24411 split.maxSize = 600;
24412 split.animate = true;
24413 split.on('moved', splitterMoved);
24414 </code></pre>
24415  * @constructor
24416  * Create a new SplitBar
24417  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24418  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24419  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24420  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24421                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24422                         position of the SplitBar).
24423  */
24424 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24425     
24426     /** @private */
24427     this.el = Roo.get(dragElement, true);
24428     this.el.dom.unselectable = "on";
24429     /** @private */
24430     this.resizingEl = Roo.get(resizingElement, true);
24431
24432     /**
24433      * @private
24434      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24435      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24436      * @type Number
24437      */
24438     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24439     
24440     /**
24441      * The minimum size of the resizing element. (Defaults to 0)
24442      * @type Number
24443      */
24444     this.minSize = 0;
24445     
24446     /**
24447      * The maximum size of the resizing element. (Defaults to 2000)
24448      * @type Number
24449      */
24450     this.maxSize = 2000;
24451     
24452     /**
24453      * Whether to animate the transition to the new size
24454      * @type Boolean
24455      */
24456     this.animate = false;
24457     
24458     /**
24459      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24460      * @type Boolean
24461      */
24462     this.useShim = false;
24463     
24464     /** @private */
24465     this.shim = null;
24466     
24467     if(!existingProxy){
24468         /** @private */
24469         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24470     }else{
24471         this.proxy = Roo.get(existingProxy).dom;
24472     }
24473     /** @private */
24474     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24475     
24476     /** @private */
24477     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24478     
24479     /** @private */
24480     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24481     
24482     /** @private */
24483     this.dragSpecs = {};
24484     
24485     /**
24486      * @private The adapter to use to positon and resize elements
24487      */
24488     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24489     this.adapter.init(this);
24490     
24491     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24492         /** @private */
24493         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24494         this.el.addClass("x-splitbar-h");
24495     }else{
24496         /** @private */
24497         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24498         this.el.addClass("x-splitbar-v");
24499     }
24500     
24501     this.addEvents({
24502         /**
24503          * @event resize
24504          * Fires when the splitter is moved (alias for {@link #event-moved})
24505          * @param {Roo.SplitBar} this
24506          * @param {Number} newSize the new width or height
24507          */
24508         "resize" : true,
24509         /**
24510          * @event moved
24511          * Fires when the splitter is moved
24512          * @param {Roo.SplitBar} this
24513          * @param {Number} newSize the new width or height
24514          */
24515         "moved" : true,
24516         /**
24517          * @event beforeresize
24518          * Fires before the splitter is dragged
24519          * @param {Roo.SplitBar} this
24520          */
24521         "beforeresize" : true,
24522
24523         "beforeapply" : true
24524     });
24525
24526     Roo.util.Observable.call(this);
24527 };
24528
24529 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24530     onStartProxyDrag : function(x, y){
24531         this.fireEvent("beforeresize", this);
24532         if(!this.overlay){
24533             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24534             o.unselectable();
24535             o.enableDisplayMode("block");
24536             // all splitbars share the same overlay
24537             Roo.SplitBar.prototype.overlay = o;
24538         }
24539         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24540         this.overlay.show();
24541         Roo.get(this.proxy).setDisplayed("block");
24542         var size = this.adapter.getElementSize(this);
24543         this.activeMinSize = this.getMinimumSize();;
24544         this.activeMaxSize = this.getMaximumSize();;
24545         var c1 = size - this.activeMinSize;
24546         var c2 = Math.max(this.activeMaxSize - size, 0);
24547         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24548             this.dd.resetConstraints();
24549             this.dd.setXConstraint(
24550                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24551                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24552             );
24553             this.dd.setYConstraint(0, 0);
24554         }else{
24555             this.dd.resetConstraints();
24556             this.dd.setXConstraint(0, 0);
24557             this.dd.setYConstraint(
24558                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24559                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24560             );
24561          }
24562         this.dragSpecs.startSize = size;
24563         this.dragSpecs.startPoint = [x, y];
24564         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24565     },
24566     
24567     /** 
24568      * @private Called after the drag operation by the DDProxy
24569      */
24570     onEndProxyDrag : function(e){
24571         Roo.get(this.proxy).setDisplayed(false);
24572         var endPoint = Roo.lib.Event.getXY(e);
24573         if(this.overlay){
24574             this.overlay.hide();
24575         }
24576         var newSize;
24577         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24578             newSize = this.dragSpecs.startSize + 
24579                 (this.placement == Roo.SplitBar.LEFT ?
24580                     endPoint[0] - this.dragSpecs.startPoint[0] :
24581                     this.dragSpecs.startPoint[0] - endPoint[0]
24582                 );
24583         }else{
24584             newSize = this.dragSpecs.startSize + 
24585                 (this.placement == Roo.SplitBar.TOP ?
24586                     endPoint[1] - this.dragSpecs.startPoint[1] :
24587                     this.dragSpecs.startPoint[1] - endPoint[1]
24588                 );
24589         }
24590         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24591         if(newSize != this.dragSpecs.startSize){
24592             if(this.fireEvent('beforeapply', this, newSize) !== false){
24593                 this.adapter.setElementSize(this, newSize);
24594                 this.fireEvent("moved", this, newSize);
24595                 this.fireEvent("resize", this, newSize);
24596             }
24597         }
24598     },
24599     
24600     /**
24601      * Get the adapter this SplitBar uses
24602      * @return The adapter object
24603      */
24604     getAdapter : function(){
24605         return this.adapter;
24606     },
24607     
24608     /**
24609      * Set the adapter this SplitBar uses
24610      * @param {Object} adapter A SplitBar adapter object
24611      */
24612     setAdapter : function(adapter){
24613         this.adapter = adapter;
24614         this.adapter.init(this);
24615     },
24616     
24617     /**
24618      * Gets the minimum size for the resizing element
24619      * @return {Number} The minimum size
24620      */
24621     getMinimumSize : function(){
24622         return this.minSize;
24623     },
24624     
24625     /**
24626      * Sets the minimum size for the resizing element
24627      * @param {Number} minSize The minimum size
24628      */
24629     setMinimumSize : function(minSize){
24630         this.minSize = minSize;
24631     },
24632     
24633     /**
24634      * Gets the maximum size for the resizing element
24635      * @return {Number} The maximum size
24636      */
24637     getMaximumSize : function(){
24638         return this.maxSize;
24639     },
24640     
24641     /**
24642      * Sets the maximum size for the resizing element
24643      * @param {Number} maxSize The maximum size
24644      */
24645     setMaximumSize : function(maxSize){
24646         this.maxSize = maxSize;
24647     },
24648     
24649     /**
24650      * Sets the initialize size for the resizing element
24651      * @param {Number} size The initial size
24652      */
24653     setCurrentSize : function(size){
24654         var oldAnimate = this.animate;
24655         this.animate = false;
24656         this.adapter.setElementSize(this, size);
24657         this.animate = oldAnimate;
24658     },
24659     
24660     /**
24661      * Destroy this splitbar. 
24662      * @param {Boolean} removeEl True to remove the element
24663      */
24664     destroy : function(removeEl){
24665         if(this.shim){
24666             this.shim.remove();
24667         }
24668         this.dd.unreg();
24669         this.proxy.parentNode.removeChild(this.proxy);
24670         if(removeEl){
24671             this.el.remove();
24672         }
24673     }
24674 });
24675
24676 /**
24677  * @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.
24678  */
24679 Roo.SplitBar.createProxy = function(dir){
24680     var proxy = new Roo.Element(document.createElement("div"));
24681     proxy.unselectable();
24682     var cls = 'x-splitbar-proxy';
24683     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24684     document.body.appendChild(proxy.dom);
24685     return proxy.dom;
24686 };
24687
24688 /** 
24689  * @class Roo.SplitBar.BasicLayoutAdapter
24690  * Default Adapter. It assumes the splitter and resizing element are not positioned
24691  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24692  */
24693 Roo.SplitBar.BasicLayoutAdapter = function(){
24694 };
24695
24696 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24697     // do nothing for now
24698     init : function(s){
24699     
24700     },
24701     /**
24702      * Called before drag operations to get the current size of the resizing element. 
24703      * @param {Roo.SplitBar} s The SplitBar using this adapter
24704      */
24705      getElementSize : function(s){
24706         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24707             return s.resizingEl.getWidth();
24708         }else{
24709             return s.resizingEl.getHeight();
24710         }
24711     },
24712     
24713     /**
24714      * Called after drag operations to set the size of the resizing element.
24715      * @param {Roo.SplitBar} s The SplitBar using this adapter
24716      * @param {Number} newSize The new size to set
24717      * @param {Function} onComplete A function to be invoked when resizing is complete
24718      */
24719     setElementSize : function(s, newSize, onComplete){
24720         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24721             if(!s.animate){
24722                 s.resizingEl.setWidth(newSize);
24723                 if(onComplete){
24724                     onComplete(s, newSize);
24725                 }
24726             }else{
24727                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24728             }
24729         }else{
24730             
24731             if(!s.animate){
24732                 s.resizingEl.setHeight(newSize);
24733                 if(onComplete){
24734                     onComplete(s, newSize);
24735                 }
24736             }else{
24737                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24738             }
24739         }
24740     }
24741 };
24742
24743 /** 
24744  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24745  * @extends Roo.SplitBar.BasicLayoutAdapter
24746  * Adapter that  moves the splitter element to align with the resized sizing element. 
24747  * Used with an absolute positioned SplitBar.
24748  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24749  * document.body, make sure you assign an id to the body element.
24750  */
24751 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24752     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24753     this.container = Roo.get(container);
24754 };
24755
24756 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24757     init : function(s){
24758         this.basic.init(s);
24759     },
24760     
24761     getElementSize : function(s){
24762         return this.basic.getElementSize(s);
24763     },
24764     
24765     setElementSize : function(s, newSize, onComplete){
24766         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24767     },
24768     
24769     moveSplitter : function(s){
24770         var yes = Roo.SplitBar;
24771         switch(s.placement){
24772             case yes.LEFT:
24773                 s.el.setX(s.resizingEl.getRight());
24774                 break;
24775             case yes.RIGHT:
24776                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24777                 break;
24778             case yes.TOP:
24779                 s.el.setY(s.resizingEl.getBottom());
24780                 break;
24781             case yes.BOTTOM:
24782                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24783                 break;
24784         }
24785     }
24786 };
24787
24788 /**
24789  * Orientation constant - Create a vertical SplitBar
24790  * @static
24791  * @type Number
24792  */
24793 Roo.SplitBar.VERTICAL = 1;
24794
24795 /**
24796  * Orientation constant - Create a horizontal SplitBar
24797  * @static
24798  * @type Number
24799  */
24800 Roo.SplitBar.HORIZONTAL = 2;
24801
24802 /**
24803  * Placement constant - The resizing element is to the left of the splitter element
24804  * @static
24805  * @type Number
24806  */
24807 Roo.SplitBar.LEFT = 1;
24808
24809 /**
24810  * Placement constant - The resizing element is to the right of the splitter element
24811  * @static
24812  * @type Number
24813  */
24814 Roo.SplitBar.RIGHT = 2;
24815
24816 /**
24817  * Placement constant - The resizing element is positioned above the splitter element
24818  * @static
24819  * @type Number
24820  */
24821 Roo.SplitBar.TOP = 3;
24822
24823 /**
24824  * Placement constant - The resizing element is positioned under splitter element
24825  * @static
24826  * @type Number
24827  */
24828 Roo.SplitBar.BOTTOM = 4;
24829 /*
24830  * Based on:
24831  * Ext JS Library 1.1.1
24832  * Copyright(c) 2006-2007, Ext JS, LLC.
24833  *
24834  * Originally Released Under LGPL - original licence link has changed is not relivant.
24835  *
24836  * Fork - LGPL
24837  * <script type="text/javascript">
24838  */
24839
24840 /**
24841  * @class Roo.View
24842  * @extends Roo.util.Observable
24843  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24844  * This class also supports single and multi selection modes. <br>
24845  * Create a data model bound view:
24846  <pre><code>
24847  var store = new Roo.data.Store(...);
24848
24849  var view = new Roo.View({
24850     el : "my-element",
24851     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24852  
24853     singleSelect: true,
24854     selectedClass: "ydataview-selected",
24855     store: store
24856  });
24857
24858  // listen for node click?
24859  view.on("click", function(vw, index, node, e){
24860  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24861  });
24862
24863  // load XML data
24864  dataModel.load("foobar.xml");
24865  </code></pre>
24866  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24867  * <br><br>
24868  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24869  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24870  * 
24871  * Note: old style constructor is still suported (container, template, config)
24872  * 
24873  * @constructor
24874  * Create a new View
24875  * @param {Object} config The config object
24876  * 
24877  */
24878 Roo.View = function(config, depreciated_tpl, depreciated_config){
24879     
24880     this.parent = false;
24881     
24882     if (typeof(depreciated_tpl) == 'undefined') {
24883         // new way.. - universal constructor.
24884         Roo.apply(this, config);
24885         this.el  = Roo.get(this.el);
24886     } else {
24887         // old format..
24888         this.el  = Roo.get(config);
24889         this.tpl = depreciated_tpl;
24890         Roo.apply(this, depreciated_config);
24891     }
24892     this.wrapEl  = this.el.wrap().wrap();
24893     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24894     
24895     
24896     if(typeof(this.tpl) == "string"){
24897         this.tpl = new Roo.Template(this.tpl);
24898     } else {
24899         // support xtype ctors..
24900         this.tpl = new Roo.factory(this.tpl, Roo);
24901     }
24902     
24903     
24904     this.tpl.compile();
24905     
24906     /** @private */
24907     this.addEvents({
24908         /**
24909          * @event beforeclick
24910          * Fires before a click is processed. Returns false to cancel the default action.
24911          * @param {Roo.View} this
24912          * @param {Number} index The index of the target node
24913          * @param {HTMLElement} node The target node
24914          * @param {Roo.EventObject} e The raw event object
24915          */
24916             "beforeclick" : true,
24917         /**
24918          * @event click
24919          * Fires when a template node is clicked.
24920          * @param {Roo.View} this
24921          * @param {Number} index The index of the target node
24922          * @param {HTMLElement} node The target node
24923          * @param {Roo.EventObject} e The raw event object
24924          */
24925             "click" : true,
24926         /**
24927          * @event dblclick
24928          * Fires when a template node is double clicked.
24929          * @param {Roo.View} this
24930          * @param {Number} index The index of the target node
24931          * @param {HTMLElement} node The target node
24932          * @param {Roo.EventObject} e The raw event object
24933          */
24934             "dblclick" : true,
24935         /**
24936          * @event contextmenu
24937          * Fires when a template node is right clicked.
24938          * @param {Roo.View} this
24939          * @param {Number} index The index of the target node
24940          * @param {HTMLElement} node The target node
24941          * @param {Roo.EventObject} e The raw event object
24942          */
24943             "contextmenu" : true,
24944         /**
24945          * @event selectionchange
24946          * Fires when the selected nodes change.
24947          * @param {Roo.View} this
24948          * @param {Array} selections Array of the selected nodes
24949          */
24950             "selectionchange" : true,
24951     
24952         /**
24953          * @event beforeselect
24954          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24955          * @param {Roo.View} this
24956          * @param {HTMLElement} node The node to be selected
24957          * @param {Array} selections Array of currently selected nodes
24958          */
24959             "beforeselect" : true,
24960         /**
24961          * @event preparedata
24962          * Fires on every row to render, to allow you to change the data.
24963          * @param {Roo.View} this
24964          * @param {Object} data to be rendered (change this)
24965          */
24966           "preparedata" : true
24967           
24968           
24969         });
24970
24971
24972
24973     this.el.on({
24974         "click": this.onClick,
24975         "dblclick": this.onDblClick,
24976         "contextmenu": this.onContextMenu,
24977         scope:this
24978     });
24979
24980     this.selections = [];
24981     this.nodes = [];
24982     this.cmp = new Roo.CompositeElementLite([]);
24983     if(this.store){
24984         this.store = Roo.factory(this.store, Roo.data);
24985         this.setStore(this.store, true);
24986     }
24987     
24988     if ( this.footer && this.footer.xtype) {
24989            
24990          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24991         
24992         this.footer.dataSource = this.store
24993         this.footer.container = fctr;
24994         this.footer = Roo.factory(this.footer, Roo);
24995         fctr.insertFirst(this.el);
24996         
24997         // this is a bit insane - as the paging toolbar seems to detach the el..
24998 //        dom.parentNode.parentNode.parentNode
24999          // they get detached?
25000     }
25001     
25002     
25003     Roo.View.superclass.constructor.call(this);
25004     
25005     
25006 };
25007
25008 Roo.extend(Roo.View, Roo.util.Observable, {
25009     
25010      /**
25011      * @cfg {Roo.data.Store} store Data store to load data from.
25012      */
25013     store : false,
25014     
25015     /**
25016      * @cfg {String|Roo.Element} el The container element.
25017      */
25018     el : '',
25019     
25020     /**
25021      * @cfg {String|Roo.Template} tpl The template used by this View 
25022      */
25023     tpl : false,
25024     /**
25025      * @cfg {String} dataName the named area of the template to use as the data area
25026      *                          Works with domtemplates roo-name="name"
25027      */
25028     dataName: false,
25029     /**
25030      * @cfg {String} selectedClass The css class to add to selected nodes
25031      */
25032     selectedClass : "x-view-selected",
25033      /**
25034      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25035      */
25036     emptyText : "",
25037     
25038     /**
25039      * @cfg {String} text to display on mask (default Loading)
25040      */
25041     mask : false,
25042     /**
25043      * @cfg {Boolean} multiSelect Allow multiple selection
25044      */
25045     multiSelect : false,
25046     /**
25047      * @cfg {Boolean} singleSelect Allow single selection
25048      */
25049     singleSelect:  false,
25050     
25051     /**
25052      * @cfg {Boolean} toggleSelect - selecting 
25053      */
25054     toggleSelect : false,
25055     
25056     /**
25057      * @cfg {Boolean} tickable - selecting 
25058      */
25059     tickable : false,
25060     
25061     /**
25062      * Returns the element this view is bound to.
25063      * @return {Roo.Element}
25064      */
25065     getEl : function(){
25066         return this.wrapEl;
25067     },
25068     
25069     
25070
25071     /**
25072      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25073      */
25074     refresh : function(){
25075         //Roo.log('refresh');
25076         var t = this.tpl;
25077         
25078         // if we are using something like 'domtemplate', then
25079         // the what gets used is:
25080         // t.applySubtemplate(NAME, data, wrapping data..)
25081         // the outer template then get' applied with
25082         //     the store 'extra data'
25083         // and the body get's added to the
25084         //      roo-name="data" node?
25085         //      <span class='roo-tpl-{name}'></span> ?????
25086         
25087         
25088         
25089         this.clearSelections();
25090         this.el.update("");
25091         var html = [];
25092         var records = this.store.getRange();
25093         if(records.length < 1) {
25094             
25095             // is this valid??  = should it render a template??
25096             
25097             this.el.update(this.emptyText);
25098             return;
25099         }
25100         var el = this.el;
25101         if (this.dataName) {
25102             this.el.update(t.apply(this.store.meta)); //????
25103             el = this.el.child('.roo-tpl-' + this.dataName);
25104         }
25105         
25106         for(var i = 0, len = records.length; i < len; i++){
25107             var data = this.prepareData(records[i].data, i, records[i]);
25108             this.fireEvent("preparedata", this, data, i, records[i]);
25109             
25110             var d = Roo.apply({}, data);
25111             
25112             if(this.tickable){
25113                 Roo.apply(d, {'roo-id' : Roo.id()});
25114                 
25115                 var _this = this;
25116             
25117                 Roo.each(this.parent.item, function(item){
25118                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25119                         return;
25120                     }
25121                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25122                 });
25123             }
25124             
25125             html[html.length] = Roo.util.Format.trim(
25126                 this.dataName ?
25127                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25128                     t.apply(d)
25129             );
25130         }
25131         
25132         
25133         
25134         el.update(html.join(""));
25135         this.nodes = el.dom.childNodes;
25136         this.updateIndexes(0);
25137     },
25138     
25139
25140     /**
25141      * Function to override to reformat the data that is sent to
25142      * the template for each node.
25143      * DEPRICATED - use the preparedata event handler.
25144      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25145      * a JSON object for an UpdateManager bound view).
25146      */
25147     prepareData : function(data, index, record)
25148     {
25149         this.fireEvent("preparedata", this, data, index, record);
25150         return data;
25151     },
25152
25153     onUpdate : function(ds, record){
25154         // Roo.log('on update');   
25155         this.clearSelections();
25156         var index = this.store.indexOf(record);
25157         var n = this.nodes[index];
25158         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25159         n.parentNode.removeChild(n);
25160         this.updateIndexes(index, index);
25161     },
25162
25163     
25164     
25165 // --------- FIXME     
25166     onAdd : function(ds, records, index)
25167     {
25168         //Roo.log(['on Add', ds, records, index] );        
25169         this.clearSelections();
25170         if(this.nodes.length == 0){
25171             this.refresh();
25172             return;
25173         }
25174         var n = this.nodes[index];
25175         for(var i = 0, len = records.length; i < len; i++){
25176             var d = this.prepareData(records[i].data, i, records[i]);
25177             if(n){
25178                 this.tpl.insertBefore(n, d);
25179             }else{
25180                 
25181                 this.tpl.append(this.el, d);
25182             }
25183         }
25184         this.updateIndexes(index);
25185     },
25186
25187     onRemove : function(ds, record, index){
25188        // Roo.log('onRemove');
25189         this.clearSelections();
25190         var el = this.dataName  ?
25191             this.el.child('.roo-tpl-' + this.dataName) :
25192             this.el; 
25193         
25194         el.dom.removeChild(this.nodes[index]);
25195         this.updateIndexes(index);
25196     },
25197
25198     /**
25199      * Refresh an individual node.
25200      * @param {Number} index
25201      */
25202     refreshNode : function(index){
25203         this.onUpdate(this.store, this.store.getAt(index));
25204     },
25205
25206     updateIndexes : function(startIndex, endIndex){
25207         var ns = this.nodes;
25208         startIndex = startIndex || 0;
25209         endIndex = endIndex || ns.length - 1;
25210         for(var i = startIndex; i <= endIndex; i++){
25211             ns[i].nodeIndex = i;
25212         }
25213     },
25214
25215     /**
25216      * Changes the data store this view uses and refresh the view.
25217      * @param {Store} store
25218      */
25219     setStore : function(store, initial){
25220         if(!initial && this.store){
25221             this.store.un("datachanged", this.refresh);
25222             this.store.un("add", this.onAdd);
25223             this.store.un("remove", this.onRemove);
25224             this.store.un("update", this.onUpdate);
25225             this.store.un("clear", this.refresh);
25226             this.store.un("beforeload", this.onBeforeLoad);
25227             this.store.un("load", this.onLoad);
25228             this.store.un("loadexception", this.onLoad);
25229         }
25230         if(store){
25231           
25232             store.on("datachanged", this.refresh, this);
25233             store.on("add", this.onAdd, this);
25234             store.on("remove", this.onRemove, this);
25235             store.on("update", this.onUpdate, this);
25236             store.on("clear", this.refresh, this);
25237             store.on("beforeload", this.onBeforeLoad, this);
25238             store.on("load", this.onLoad, this);
25239             store.on("loadexception", this.onLoad, this);
25240         }
25241         
25242         if(store){
25243             this.refresh();
25244         }
25245     },
25246     /**
25247      * onbeforeLoad - masks the loading area.
25248      *
25249      */
25250     onBeforeLoad : function(store,opts)
25251     {
25252          //Roo.log('onBeforeLoad');   
25253         if (!opts.add) {
25254             this.el.update("");
25255         }
25256         this.el.mask(this.mask ? this.mask : "Loading" ); 
25257     },
25258     onLoad : function ()
25259     {
25260         this.el.unmask();
25261     },
25262     
25263
25264     /**
25265      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25266      * @param {HTMLElement} node
25267      * @return {HTMLElement} The template node
25268      */
25269     findItemFromChild : function(node){
25270         var el = this.dataName  ?
25271             this.el.child('.roo-tpl-' + this.dataName,true) :
25272             this.el.dom; 
25273         
25274         if(!node || node.parentNode == el){
25275                     return node;
25276             }
25277             var p = node.parentNode;
25278             while(p && p != el){
25279             if(p.parentNode == el){
25280                 return p;
25281             }
25282             p = p.parentNode;
25283         }
25284             return null;
25285     },
25286
25287     /** @ignore */
25288     onClick : function(e){
25289         var item = this.findItemFromChild(e.getTarget());
25290         if(item){
25291             var index = this.indexOf(item);
25292             if(this.onItemClick(item, index, e) !== false){
25293                 this.fireEvent("click", this, index, item, e);
25294             }
25295         }else{
25296             this.clearSelections();
25297         }
25298     },
25299
25300     /** @ignore */
25301     onContextMenu : function(e){
25302         var item = this.findItemFromChild(e.getTarget());
25303         if(item){
25304             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25305         }
25306     },
25307
25308     /** @ignore */
25309     onDblClick : function(e){
25310         var item = this.findItemFromChild(e.getTarget());
25311         if(item){
25312             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25313         }
25314     },
25315
25316     onItemClick : function(item, index, e)
25317     {
25318         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25319             return false;
25320         }
25321         if (this.toggleSelect) {
25322             var m = this.isSelected(item) ? 'unselect' : 'select';
25323             //Roo.log(m);
25324             var _t = this;
25325             _t[m](item, true, false);
25326             return true;
25327         }
25328         if(this.multiSelect || this.singleSelect){
25329             if(this.multiSelect && e.shiftKey && this.lastSelection){
25330                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25331             }else{
25332                 this.select(item, this.multiSelect && e.ctrlKey);
25333                 this.lastSelection = item;
25334             }
25335             
25336             if(!this.tickable){
25337                 e.preventDefault();
25338             }
25339             
25340         }
25341         return true;
25342     },
25343
25344     /**
25345      * Get the number of selected nodes.
25346      * @return {Number}
25347      */
25348     getSelectionCount : function(){
25349         return this.selections.length;
25350     },
25351
25352     /**
25353      * Get the currently selected nodes.
25354      * @return {Array} An array of HTMLElements
25355      */
25356     getSelectedNodes : function(){
25357         return this.selections;
25358     },
25359
25360     /**
25361      * Get the indexes of the selected nodes.
25362      * @return {Array}
25363      */
25364     getSelectedIndexes : function(){
25365         var indexes = [], s = this.selections;
25366         for(var i = 0, len = s.length; i < len; i++){
25367             indexes.push(s[i].nodeIndex);
25368         }
25369         return indexes;
25370     },
25371
25372     /**
25373      * Clear all selections
25374      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25375      */
25376     clearSelections : function(suppressEvent){
25377         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25378             this.cmp.elements = this.selections;
25379             this.cmp.removeClass(this.selectedClass);
25380             this.selections = [];
25381             if(!suppressEvent){
25382                 this.fireEvent("selectionchange", this, this.selections);
25383             }
25384         }
25385     },
25386
25387     /**
25388      * Returns true if the passed node is selected
25389      * @param {HTMLElement/Number} node The node or node index
25390      * @return {Boolean}
25391      */
25392     isSelected : function(node){
25393         var s = this.selections;
25394         if(s.length < 1){
25395             return false;
25396         }
25397         node = this.getNode(node);
25398         return s.indexOf(node) !== -1;
25399     },
25400
25401     /**
25402      * Selects nodes.
25403      * @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
25404      * @param {Boolean} keepExisting (optional) true to keep existing selections
25405      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25406      */
25407     select : function(nodeInfo, keepExisting, suppressEvent){
25408         if(nodeInfo instanceof Array){
25409             if(!keepExisting){
25410                 this.clearSelections(true);
25411             }
25412             for(var i = 0, len = nodeInfo.length; i < len; i++){
25413                 this.select(nodeInfo[i], true, true);
25414             }
25415             return;
25416         } 
25417         var node = this.getNode(nodeInfo);
25418         if(!node || this.isSelected(node)){
25419             return; // already selected.
25420         }
25421         if(!keepExisting){
25422             this.clearSelections(true);
25423         }
25424         
25425         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25426             Roo.fly(node).addClass(this.selectedClass);
25427             this.selections.push(node);
25428             if(!suppressEvent){
25429                 this.fireEvent("selectionchange", this, this.selections);
25430             }
25431         }
25432         
25433         
25434     },
25435       /**
25436      * Unselects nodes.
25437      * @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
25438      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25439      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25440      */
25441     unselect : function(nodeInfo, keepExisting, suppressEvent)
25442     {
25443         if(nodeInfo instanceof Array){
25444             Roo.each(this.selections, function(s) {
25445                 this.unselect(s, nodeInfo);
25446             }, this);
25447             return;
25448         }
25449         var node = this.getNode(nodeInfo);
25450         if(!node || !this.isSelected(node)){
25451             //Roo.log("not selected");
25452             return; // not selected.
25453         }
25454         // fireevent???
25455         var ns = [];
25456         Roo.each(this.selections, function(s) {
25457             if (s == node ) {
25458                 Roo.fly(node).removeClass(this.selectedClass);
25459
25460                 return;
25461             }
25462             ns.push(s);
25463         },this);
25464         
25465         this.selections= ns;
25466         this.fireEvent("selectionchange", this, this.selections);
25467     },
25468
25469     /**
25470      * Gets a template node.
25471      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25472      * @return {HTMLElement} The node or null if it wasn't found
25473      */
25474     getNode : function(nodeInfo){
25475         if(typeof nodeInfo == "string"){
25476             return document.getElementById(nodeInfo);
25477         }else if(typeof nodeInfo == "number"){
25478             return this.nodes[nodeInfo];
25479         }
25480         return nodeInfo;
25481     },
25482
25483     /**
25484      * Gets a range template nodes.
25485      * @param {Number} startIndex
25486      * @param {Number} endIndex
25487      * @return {Array} An array of nodes
25488      */
25489     getNodes : function(start, end){
25490         var ns = this.nodes;
25491         start = start || 0;
25492         end = typeof end == "undefined" ? ns.length - 1 : end;
25493         var nodes = [];
25494         if(start <= end){
25495             for(var i = start; i <= end; i++){
25496                 nodes.push(ns[i]);
25497             }
25498         } else{
25499             for(var i = start; i >= end; i--){
25500                 nodes.push(ns[i]);
25501             }
25502         }
25503         return nodes;
25504     },
25505
25506     /**
25507      * Finds the index of the passed node
25508      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25509      * @return {Number} The index of the node or -1
25510      */
25511     indexOf : function(node){
25512         node = this.getNode(node);
25513         if(typeof node.nodeIndex == "number"){
25514             return node.nodeIndex;
25515         }
25516         var ns = this.nodes;
25517         for(var i = 0, len = ns.length; i < len; i++){
25518             if(ns[i] == node){
25519                 return i;
25520             }
25521         }
25522         return -1;
25523     }
25524 });
25525 /*
25526  * Based on:
25527  * Ext JS Library 1.1.1
25528  * Copyright(c) 2006-2007, Ext JS, LLC.
25529  *
25530  * Originally Released Under LGPL - original licence link has changed is not relivant.
25531  *
25532  * Fork - LGPL
25533  * <script type="text/javascript">
25534  */
25535
25536 /**
25537  * @class Roo.JsonView
25538  * @extends Roo.View
25539  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25540 <pre><code>
25541 var view = new Roo.JsonView({
25542     container: "my-element",
25543     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25544     multiSelect: true, 
25545     jsonRoot: "data" 
25546 });
25547
25548 // listen for node click?
25549 view.on("click", function(vw, index, node, e){
25550     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25551 });
25552
25553 // direct load of JSON data
25554 view.load("foobar.php");
25555
25556 // Example from my blog list
25557 var tpl = new Roo.Template(
25558     '&lt;div class="entry"&gt;' +
25559     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25560     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25561     "&lt;/div&gt;&lt;hr /&gt;"
25562 );
25563
25564 var moreView = new Roo.JsonView({
25565     container :  "entry-list", 
25566     template : tpl,
25567     jsonRoot: "posts"
25568 });
25569 moreView.on("beforerender", this.sortEntries, this);
25570 moreView.load({
25571     url: "/blog/get-posts.php",
25572     params: "allposts=true",
25573     text: "Loading Blog Entries..."
25574 });
25575 </code></pre>
25576
25577 * Note: old code is supported with arguments : (container, template, config)
25578
25579
25580  * @constructor
25581  * Create a new JsonView
25582  * 
25583  * @param {Object} config The config object
25584  * 
25585  */
25586 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25587     
25588     
25589     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25590
25591     var um = this.el.getUpdateManager();
25592     um.setRenderer(this);
25593     um.on("update", this.onLoad, this);
25594     um.on("failure", this.onLoadException, this);
25595
25596     /**
25597      * @event beforerender
25598      * Fires before rendering of the downloaded JSON data.
25599      * @param {Roo.JsonView} this
25600      * @param {Object} data The JSON data loaded
25601      */
25602     /**
25603      * @event load
25604      * Fires when data is loaded.
25605      * @param {Roo.JsonView} this
25606      * @param {Object} data The JSON data loaded
25607      * @param {Object} response The raw Connect response object
25608      */
25609     /**
25610      * @event loadexception
25611      * Fires when loading fails.
25612      * @param {Roo.JsonView} this
25613      * @param {Object} response The raw Connect response object
25614      */
25615     this.addEvents({
25616         'beforerender' : true,
25617         'load' : true,
25618         'loadexception' : true
25619     });
25620 };
25621 Roo.extend(Roo.JsonView, Roo.View, {
25622     /**
25623      * @type {String} The root property in the loaded JSON object that contains the data
25624      */
25625     jsonRoot : "",
25626
25627     /**
25628      * Refreshes the view.
25629      */
25630     refresh : function(){
25631         this.clearSelections();
25632         this.el.update("");
25633         var html = [];
25634         var o = this.jsonData;
25635         if(o && o.length > 0){
25636             for(var i = 0, len = o.length; i < len; i++){
25637                 var data = this.prepareData(o[i], i, o);
25638                 html[html.length] = this.tpl.apply(data);
25639             }
25640         }else{
25641             html.push(this.emptyText);
25642         }
25643         this.el.update(html.join(""));
25644         this.nodes = this.el.dom.childNodes;
25645         this.updateIndexes(0);
25646     },
25647
25648     /**
25649      * 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.
25650      * @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:
25651      <pre><code>
25652      view.load({
25653          url: "your-url.php",
25654          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25655          callback: yourFunction,
25656          scope: yourObject, //(optional scope)
25657          discardUrl: false,
25658          nocache: false,
25659          text: "Loading...",
25660          timeout: 30,
25661          scripts: false
25662      });
25663      </code></pre>
25664      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25665      * 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.
25666      * @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}
25667      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25668      * @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.
25669      */
25670     load : function(){
25671         var um = this.el.getUpdateManager();
25672         um.update.apply(um, arguments);
25673     },
25674
25675     render : function(el, response){
25676         this.clearSelections();
25677         this.el.update("");
25678         var o;
25679         try{
25680             o = Roo.util.JSON.decode(response.responseText);
25681             if(this.jsonRoot){
25682                 
25683                 o = o[this.jsonRoot];
25684             }
25685         } catch(e){
25686         }
25687         /**
25688          * The current JSON data or null
25689          */
25690         this.jsonData = o;
25691         this.beforeRender();
25692         this.refresh();
25693     },
25694
25695 /**
25696  * Get the number of records in the current JSON dataset
25697  * @return {Number}
25698  */
25699     getCount : function(){
25700         return this.jsonData ? this.jsonData.length : 0;
25701     },
25702
25703 /**
25704  * Returns the JSON object for the specified node(s)
25705  * @param {HTMLElement/Array} node The node or an array of nodes
25706  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25707  * you get the JSON object for the node
25708  */
25709     getNodeData : function(node){
25710         if(node instanceof Array){
25711             var data = [];
25712             for(var i = 0, len = node.length; i < len; i++){
25713                 data.push(this.getNodeData(node[i]));
25714             }
25715             return data;
25716         }
25717         return this.jsonData[this.indexOf(node)] || null;
25718     },
25719
25720     beforeRender : function(){
25721         this.snapshot = this.jsonData;
25722         if(this.sortInfo){
25723             this.sort.apply(this, this.sortInfo);
25724         }
25725         this.fireEvent("beforerender", this, this.jsonData);
25726     },
25727
25728     onLoad : function(el, o){
25729         this.fireEvent("load", this, this.jsonData, o);
25730     },
25731
25732     onLoadException : function(el, o){
25733         this.fireEvent("loadexception", this, o);
25734     },
25735
25736 /**
25737  * Filter the data by a specific property.
25738  * @param {String} property A property on your JSON objects
25739  * @param {String/RegExp} value Either string that the property values
25740  * should start with, or a RegExp to test against the property
25741  */
25742     filter : function(property, value){
25743         if(this.jsonData){
25744             var data = [];
25745             var ss = this.snapshot;
25746             if(typeof value == "string"){
25747                 var vlen = value.length;
25748                 if(vlen == 0){
25749                     this.clearFilter();
25750                     return;
25751                 }
25752                 value = value.toLowerCase();
25753                 for(var i = 0, len = ss.length; i < len; i++){
25754                     var o = ss[i];
25755                     if(o[property].substr(0, vlen).toLowerCase() == value){
25756                         data.push(o);
25757                     }
25758                 }
25759             } else if(value.exec){ // regex?
25760                 for(var i = 0, len = ss.length; i < len; i++){
25761                     var o = ss[i];
25762                     if(value.test(o[property])){
25763                         data.push(o);
25764                     }
25765                 }
25766             } else{
25767                 return;
25768             }
25769             this.jsonData = data;
25770             this.refresh();
25771         }
25772     },
25773
25774 /**
25775  * Filter by a function. The passed function will be called with each
25776  * object in the current dataset. If the function returns true the value is kept,
25777  * otherwise it is filtered.
25778  * @param {Function} fn
25779  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25780  */
25781     filterBy : function(fn, scope){
25782         if(this.jsonData){
25783             var data = [];
25784             var ss = this.snapshot;
25785             for(var i = 0, len = ss.length; i < len; i++){
25786                 var o = ss[i];
25787                 if(fn.call(scope || this, o)){
25788                     data.push(o);
25789                 }
25790             }
25791             this.jsonData = data;
25792             this.refresh();
25793         }
25794     },
25795
25796 /**
25797  * Clears the current filter.
25798  */
25799     clearFilter : function(){
25800         if(this.snapshot && this.jsonData != this.snapshot){
25801             this.jsonData = this.snapshot;
25802             this.refresh();
25803         }
25804     },
25805
25806
25807 /**
25808  * Sorts the data for this view and refreshes it.
25809  * @param {String} property A property on your JSON objects to sort on
25810  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25811  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25812  */
25813     sort : function(property, dir, sortType){
25814         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25815         if(this.jsonData){
25816             var p = property;
25817             var dsc = dir && dir.toLowerCase() == "desc";
25818             var f = function(o1, o2){
25819                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25820                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25821                 ;
25822                 if(v1 < v2){
25823                     return dsc ? +1 : -1;
25824                 } else if(v1 > v2){
25825                     return dsc ? -1 : +1;
25826                 } else{
25827                     return 0;
25828                 }
25829             };
25830             this.jsonData.sort(f);
25831             this.refresh();
25832             if(this.jsonData != this.snapshot){
25833                 this.snapshot.sort(f);
25834             }
25835         }
25836     }
25837 });/*
25838  * Based on:
25839  * Ext JS Library 1.1.1
25840  * Copyright(c) 2006-2007, Ext JS, LLC.
25841  *
25842  * Originally Released Under LGPL - original licence link has changed is not relivant.
25843  *
25844  * Fork - LGPL
25845  * <script type="text/javascript">
25846  */
25847  
25848
25849 /**
25850  * @class Roo.ColorPalette
25851  * @extends Roo.Component
25852  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25853  * Here's an example of typical usage:
25854  * <pre><code>
25855 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25856 cp.render('my-div');
25857
25858 cp.on('select', function(palette, selColor){
25859     // do something with selColor
25860 });
25861 </code></pre>
25862  * @constructor
25863  * Create a new ColorPalette
25864  * @param {Object} config The config object
25865  */
25866 Roo.ColorPalette = function(config){
25867     Roo.ColorPalette.superclass.constructor.call(this, config);
25868     this.addEvents({
25869         /**
25870              * @event select
25871              * Fires when a color is selected
25872              * @param {ColorPalette} this
25873              * @param {String} color The 6-digit color hex code (without the # symbol)
25874              */
25875         select: true
25876     });
25877
25878     if(this.handler){
25879         this.on("select", this.handler, this.scope, true);
25880     }
25881 };
25882 Roo.extend(Roo.ColorPalette, Roo.Component, {
25883     /**
25884      * @cfg {String} itemCls
25885      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25886      */
25887     itemCls : "x-color-palette",
25888     /**
25889      * @cfg {String} value
25890      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25891      * the hex codes are case-sensitive.
25892      */
25893     value : null,
25894     clickEvent:'click',
25895     // private
25896     ctype: "Roo.ColorPalette",
25897
25898     /**
25899      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25900      */
25901     allowReselect : false,
25902
25903     /**
25904      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25905      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25906      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25907      * of colors with the width setting until the box is symmetrical.</p>
25908      * <p>You can override individual colors if needed:</p>
25909      * <pre><code>
25910 var cp = new Roo.ColorPalette();
25911 cp.colors[0] = "FF0000";  // change the first box to red
25912 </code></pre>
25913
25914 Or you can provide a custom array of your own for complete control:
25915 <pre><code>
25916 var cp = new Roo.ColorPalette();
25917 cp.colors = ["000000", "993300", "333300"];
25918 </code></pre>
25919      * @type Array
25920      */
25921     colors : [
25922         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25923         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25924         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25925         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25926         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25927     ],
25928
25929     // private
25930     onRender : function(container, position){
25931         var t = new Roo.MasterTemplate(
25932             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25933         );
25934         var c = this.colors;
25935         for(var i = 0, len = c.length; i < len; i++){
25936             t.add([c[i]]);
25937         }
25938         var el = document.createElement("div");
25939         el.className = this.itemCls;
25940         t.overwrite(el);
25941         container.dom.insertBefore(el, position);
25942         this.el = Roo.get(el);
25943         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25944         if(this.clickEvent != 'click'){
25945             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25946         }
25947     },
25948
25949     // private
25950     afterRender : function(){
25951         Roo.ColorPalette.superclass.afterRender.call(this);
25952         if(this.value){
25953             var s = this.value;
25954             this.value = null;
25955             this.select(s);
25956         }
25957     },
25958
25959     // private
25960     handleClick : function(e, t){
25961         e.preventDefault();
25962         if(!this.disabled){
25963             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25964             this.select(c.toUpperCase());
25965         }
25966     },
25967
25968     /**
25969      * Selects the specified color in the palette (fires the select event)
25970      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25971      */
25972     select : function(color){
25973         color = color.replace("#", "");
25974         if(color != this.value || this.allowReselect){
25975             var el = this.el;
25976             if(this.value){
25977                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25978             }
25979             el.child("a.color-"+color).addClass("x-color-palette-sel");
25980             this.value = color;
25981             this.fireEvent("select", this, color);
25982         }
25983     }
25984 });/*
25985  * Based on:
25986  * Ext JS Library 1.1.1
25987  * Copyright(c) 2006-2007, Ext JS, LLC.
25988  *
25989  * Originally Released Under LGPL - original licence link has changed is not relivant.
25990  *
25991  * Fork - LGPL
25992  * <script type="text/javascript">
25993  */
25994  
25995 /**
25996  * @class Roo.DatePicker
25997  * @extends Roo.Component
25998  * Simple date picker class.
25999  * @constructor
26000  * Create a new DatePicker
26001  * @param {Object} config The config object
26002  */
26003 Roo.DatePicker = function(config){
26004     Roo.DatePicker.superclass.constructor.call(this, config);
26005
26006     this.value = config && config.value ?
26007                  config.value.clearTime() : new Date().clearTime();
26008
26009     this.addEvents({
26010         /**
26011              * @event select
26012              * Fires when a date is selected
26013              * @param {DatePicker} this
26014              * @param {Date} date The selected date
26015              */
26016         'select': true,
26017         /**
26018              * @event monthchange
26019              * Fires when the displayed month changes 
26020              * @param {DatePicker} this
26021              * @param {Date} date The selected month
26022              */
26023         'monthchange': true
26024     });
26025
26026     if(this.handler){
26027         this.on("select", this.handler,  this.scope || this);
26028     }
26029     // build the disabledDatesRE
26030     if(!this.disabledDatesRE && this.disabledDates){
26031         var dd = this.disabledDates;
26032         var re = "(?:";
26033         for(var i = 0; i < dd.length; i++){
26034             re += dd[i];
26035             if(i != dd.length-1) re += "|";
26036         }
26037         this.disabledDatesRE = new RegExp(re + ")");
26038     }
26039 };
26040
26041 Roo.extend(Roo.DatePicker, Roo.Component, {
26042     /**
26043      * @cfg {String} todayText
26044      * The text to display on the button that selects the current date (defaults to "Today")
26045      */
26046     todayText : "Today",
26047     /**
26048      * @cfg {String} okText
26049      * The text to display on the ok button
26050      */
26051     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26052     /**
26053      * @cfg {String} cancelText
26054      * The text to display on the cancel button
26055      */
26056     cancelText : "Cancel",
26057     /**
26058      * @cfg {String} todayTip
26059      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26060      */
26061     todayTip : "{0} (Spacebar)",
26062     /**
26063      * @cfg {Date} minDate
26064      * Minimum allowable date (JavaScript date object, defaults to null)
26065      */
26066     minDate : null,
26067     /**
26068      * @cfg {Date} maxDate
26069      * Maximum allowable date (JavaScript date object, defaults to null)
26070      */
26071     maxDate : null,
26072     /**
26073      * @cfg {String} minText
26074      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26075      */
26076     minText : "This date is before the minimum date",
26077     /**
26078      * @cfg {String} maxText
26079      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26080      */
26081     maxText : "This date is after the maximum date",
26082     /**
26083      * @cfg {String} format
26084      * The default date format string which can be overriden for localization support.  The format must be
26085      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26086      */
26087     format : "m/d/y",
26088     /**
26089      * @cfg {Array} disabledDays
26090      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26091      */
26092     disabledDays : null,
26093     /**
26094      * @cfg {String} disabledDaysText
26095      * The tooltip to display when the date falls on a disabled day (defaults to "")
26096      */
26097     disabledDaysText : "",
26098     /**
26099      * @cfg {RegExp} disabledDatesRE
26100      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26101      */
26102     disabledDatesRE : null,
26103     /**
26104      * @cfg {String} disabledDatesText
26105      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26106      */
26107     disabledDatesText : "",
26108     /**
26109      * @cfg {Boolean} constrainToViewport
26110      * True to constrain the date picker to the viewport (defaults to true)
26111      */
26112     constrainToViewport : true,
26113     /**
26114      * @cfg {Array} monthNames
26115      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26116      */
26117     monthNames : Date.monthNames,
26118     /**
26119      * @cfg {Array} dayNames
26120      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26121      */
26122     dayNames : Date.dayNames,
26123     /**
26124      * @cfg {String} nextText
26125      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26126      */
26127     nextText: 'Next Month (Control+Right)',
26128     /**
26129      * @cfg {String} prevText
26130      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26131      */
26132     prevText: 'Previous Month (Control+Left)',
26133     /**
26134      * @cfg {String} monthYearText
26135      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26136      */
26137     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26138     /**
26139      * @cfg {Number} startDay
26140      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26141      */
26142     startDay : 0,
26143     /**
26144      * @cfg {Bool} showClear
26145      * Show a clear button (usefull for date form elements that can be blank.)
26146      */
26147     
26148     showClear: false,
26149     
26150     /**
26151      * Sets the value of the date field
26152      * @param {Date} value The date to set
26153      */
26154     setValue : function(value){
26155         var old = this.value;
26156         
26157         if (typeof(value) == 'string') {
26158          
26159             value = Date.parseDate(value, this.format);
26160         }
26161         if (!value) {
26162             value = new Date();
26163         }
26164         
26165         this.value = value.clearTime(true);
26166         if(this.el){
26167             this.update(this.value);
26168         }
26169     },
26170
26171     /**
26172      * Gets the current selected value of the date field
26173      * @return {Date} The selected date
26174      */
26175     getValue : function(){
26176         return this.value;
26177     },
26178
26179     // private
26180     focus : function(){
26181         if(this.el){
26182             this.update(this.activeDate);
26183         }
26184     },
26185
26186     // privateval
26187     onRender : function(container, position){
26188         
26189         var m = [
26190              '<table cellspacing="0">',
26191                 '<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>',
26192                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26193         var dn = this.dayNames;
26194         for(var i = 0; i < 7; i++){
26195             var d = this.startDay+i;
26196             if(d > 6){
26197                 d = d-7;
26198             }
26199             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26200         }
26201         m[m.length] = "</tr></thead><tbody><tr>";
26202         for(var i = 0; i < 42; i++) {
26203             if(i % 7 == 0 && i != 0){
26204                 m[m.length] = "</tr><tr>";
26205             }
26206             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26207         }
26208         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26209             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26210
26211         var el = document.createElement("div");
26212         el.className = "x-date-picker";
26213         el.innerHTML = m.join("");
26214
26215         container.dom.insertBefore(el, position);
26216
26217         this.el = Roo.get(el);
26218         this.eventEl = Roo.get(el.firstChild);
26219
26220         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26221             handler: this.showPrevMonth,
26222             scope: this,
26223             preventDefault:true,
26224             stopDefault:true
26225         });
26226
26227         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26228             handler: this.showNextMonth,
26229             scope: this,
26230             preventDefault:true,
26231             stopDefault:true
26232         });
26233
26234         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26235
26236         this.monthPicker = this.el.down('div.x-date-mp');
26237         this.monthPicker.enableDisplayMode('block');
26238         
26239         var kn = new Roo.KeyNav(this.eventEl, {
26240             "left" : function(e){
26241                 e.ctrlKey ?
26242                     this.showPrevMonth() :
26243                     this.update(this.activeDate.add("d", -1));
26244             },
26245
26246             "right" : function(e){
26247                 e.ctrlKey ?
26248                     this.showNextMonth() :
26249                     this.update(this.activeDate.add("d", 1));
26250             },
26251
26252             "up" : function(e){
26253                 e.ctrlKey ?
26254                     this.showNextYear() :
26255                     this.update(this.activeDate.add("d", -7));
26256             },
26257
26258             "down" : function(e){
26259                 e.ctrlKey ?
26260                     this.showPrevYear() :
26261                     this.update(this.activeDate.add("d", 7));
26262             },
26263
26264             "pageUp" : function(e){
26265                 this.showNextMonth();
26266             },
26267
26268             "pageDown" : function(e){
26269                 this.showPrevMonth();
26270             },
26271
26272             "enter" : function(e){
26273                 e.stopPropagation();
26274                 return true;
26275             },
26276
26277             scope : this
26278         });
26279
26280         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26281
26282         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26283
26284         this.el.unselectable();
26285         
26286         this.cells = this.el.select("table.x-date-inner tbody td");
26287         this.textNodes = this.el.query("table.x-date-inner tbody span");
26288
26289         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26290             text: "&#160;",
26291             tooltip: this.monthYearText
26292         });
26293
26294         this.mbtn.on('click', this.showMonthPicker, this);
26295         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26296
26297
26298         var today = (new Date()).dateFormat(this.format);
26299         
26300         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26301         if (this.showClear) {
26302             baseTb.add( new Roo.Toolbar.Fill());
26303         }
26304         baseTb.add({
26305             text: String.format(this.todayText, today),
26306             tooltip: String.format(this.todayTip, today),
26307             handler: this.selectToday,
26308             scope: this
26309         });
26310         
26311         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26312             
26313         //});
26314         if (this.showClear) {
26315             
26316             baseTb.add( new Roo.Toolbar.Fill());
26317             baseTb.add({
26318                 text: '&#160;',
26319                 cls: 'x-btn-icon x-btn-clear',
26320                 handler: function() {
26321                     //this.value = '';
26322                     this.fireEvent("select", this, '');
26323                 },
26324                 scope: this
26325             });
26326         }
26327         
26328         
26329         if(Roo.isIE){
26330             this.el.repaint();
26331         }
26332         this.update(this.value);
26333     },
26334
26335     createMonthPicker : function(){
26336         if(!this.monthPicker.dom.firstChild){
26337             var buf = ['<table border="0" cellspacing="0">'];
26338             for(var i = 0; i < 6; i++){
26339                 buf.push(
26340                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26341                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26342                     i == 0 ?
26343                     '<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>' :
26344                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26345                 );
26346             }
26347             buf.push(
26348                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26349                     this.okText,
26350                     '</button><button type="button" class="x-date-mp-cancel">',
26351                     this.cancelText,
26352                     '</button></td></tr>',
26353                 '</table>'
26354             );
26355             this.monthPicker.update(buf.join(''));
26356             this.monthPicker.on('click', this.onMonthClick, this);
26357             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26358
26359             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26360             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26361
26362             this.mpMonths.each(function(m, a, i){
26363                 i += 1;
26364                 if((i%2) == 0){
26365                     m.dom.xmonth = 5 + Math.round(i * .5);
26366                 }else{
26367                     m.dom.xmonth = Math.round((i-1) * .5);
26368                 }
26369             });
26370         }
26371     },
26372
26373     showMonthPicker : function(){
26374         this.createMonthPicker();
26375         var size = this.el.getSize();
26376         this.monthPicker.setSize(size);
26377         this.monthPicker.child('table').setSize(size);
26378
26379         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26380         this.updateMPMonth(this.mpSelMonth);
26381         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26382         this.updateMPYear(this.mpSelYear);
26383
26384         this.monthPicker.slideIn('t', {duration:.2});
26385     },
26386
26387     updateMPYear : function(y){
26388         this.mpyear = y;
26389         var ys = this.mpYears.elements;
26390         for(var i = 1; i <= 10; i++){
26391             var td = ys[i-1], y2;
26392             if((i%2) == 0){
26393                 y2 = y + Math.round(i * .5);
26394                 td.firstChild.innerHTML = y2;
26395                 td.xyear = y2;
26396             }else{
26397                 y2 = y - (5-Math.round(i * .5));
26398                 td.firstChild.innerHTML = y2;
26399                 td.xyear = y2;
26400             }
26401             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26402         }
26403     },
26404
26405     updateMPMonth : function(sm){
26406         this.mpMonths.each(function(m, a, i){
26407             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26408         });
26409     },
26410
26411     selectMPMonth: function(m){
26412         
26413     },
26414
26415     onMonthClick : function(e, t){
26416         e.stopEvent();
26417         var el = new Roo.Element(t), pn;
26418         if(el.is('button.x-date-mp-cancel')){
26419             this.hideMonthPicker();
26420         }
26421         else if(el.is('button.x-date-mp-ok')){
26422             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26423             this.hideMonthPicker();
26424         }
26425         else if(pn = el.up('td.x-date-mp-month', 2)){
26426             this.mpMonths.removeClass('x-date-mp-sel');
26427             pn.addClass('x-date-mp-sel');
26428             this.mpSelMonth = pn.dom.xmonth;
26429         }
26430         else if(pn = el.up('td.x-date-mp-year', 2)){
26431             this.mpYears.removeClass('x-date-mp-sel');
26432             pn.addClass('x-date-mp-sel');
26433             this.mpSelYear = pn.dom.xyear;
26434         }
26435         else if(el.is('a.x-date-mp-prev')){
26436             this.updateMPYear(this.mpyear-10);
26437         }
26438         else if(el.is('a.x-date-mp-next')){
26439             this.updateMPYear(this.mpyear+10);
26440         }
26441     },
26442
26443     onMonthDblClick : function(e, t){
26444         e.stopEvent();
26445         var el = new Roo.Element(t), pn;
26446         if(pn = el.up('td.x-date-mp-month', 2)){
26447             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26448             this.hideMonthPicker();
26449         }
26450         else if(pn = el.up('td.x-date-mp-year', 2)){
26451             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26452             this.hideMonthPicker();
26453         }
26454     },
26455
26456     hideMonthPicker : function(disableAnim){
26457         if(this.monthPicker){
26458             if(disableAnim === true){
26459                 this.monthPicker.hide();
26460             }else{
26461                 this.monthPicker.slideOut('t', {duration:.2});
26462             }
26463         }
26464     },
26465
26466     // private
26467     showPrevMonth : function(e){
26468         this.update(this.activeDate.add("mo", -1));
26469     },
26470
26471     // private
26472     showNextMonth : function(e){
26473         this.update(this.activeDate.add("mo", 1));
26474     },
26475
26476     // private
26477     showPrevYear : function(){
26478         this.update(this.activeDate.add("y", -1));
26479     },
26480
26481     // private
26482     showNextYear : function(){
26483         this.update(this.activeDate.add("y", 1));
26484     },
26485
26486     // private
26487     handleMouseWheel : function(e){
26488         var delta = e.getWheelDelta();
26489         if(delta > 0){
26490             this.showPrevMonth();
26491             e.stopEvent();
26492         } else if(delta < 0){
26493             this.showNextMonth();
26494             e.stopEvent();
26495         }
26496     },
26497
26498     // private
26499     handleDateClick : function(e, t){
26500         e.stopEvent();
26501         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26502             this.setValue(new Date(t.dateValue));
26503             this.fireEvent("select", this, this.value);
26504         }
26505     },
26506
26507     // private
26508     selectToday : function(){
26509         this.setValue(new Date().clearTime());
26510         this.fireEvent("select", this, this.value);
26511     },
26512
26513     // private
26514     update : function(date)
26515     {
26516         var vd = this.activeDate;
26517         this.activeDate = date;
26518         if(vd && this.el){
26519             var t = date.getTime();
26520             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26521                 this.cells.removeClass("x-date-selected");
26522                 this.cells.each(function(c){
26523                    if(c.dom.firstChild.dateValue == t){
26524                        c.addClass("x-date-selected");
26525                        setTimeout(function(){
26526                             try{c.dom.firstChild.focus();}catch(e){}
26527                        }, 50);
26528                        return false;
26529                    }
26530                 });
26531                 return;
26532             }
26533         }
26534         
26535         var days = date.getDaysInMonth();
26536         var firstOfMonth = date.getFirstDateOfMonth();
26537         var startingPos = firstOfMonth.getDay()-this.startDay;
26538
26539         if(startingPos <= this.startDay){
26540             startingPos += 7;
26541         }
26542
26543         var pm = date.add("mo", -1);
26544         var prevStart = pm.getDaysInMonth()-startingPos;
26545
26546         var cells = this.cells.elements;
26547         var textEls = this.textNodes;
26548         days += startingPos;
26549
26550         // convert everything to numbers so it's fast
26551         var day = 86400000;
26552         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26553         var today = new Date().clearTime().getTime();
26554         var sel = date.clearTime().getTime();
26555         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26556         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26557         var ddMatch = this.disabledDatesRE;
26558         var ddText = this.disabledDatesText;
26559         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26560         var ddaysText = this.disabledDaysText;
26561         var format = this.format;
26562
26563         var setCellClass = function(cal, cell){
26564             cell.title = "";
26565             var t = d.getTime();
26566             cell.firstChild.dateValue = t;
26567             if(t == today){
26568                 cell.className += " x-date-today";
26569                 cell.title = cal.todayText;
26570             }
26571             if(t == sel){
26572                 cell.className += " x-date-selected";
26573                 setTimeout(function(){
26574                     try{cell.firstChild.focus();}catch(e){}
26575                 }, 50);
26576             }
26577             // disabling
26578             if(t < min) {
26579                 cell.className = " x-date-disabled";
26580                 cell.title = cal.minText;
26581                 return;
26582             }
26583             if(t > max) {
26584                 cell.className = " x-date-disabled";
26585                 cell.title = cal.maxText;
26586                 return;
26587             }
26588             if(ddays){
26589                 if(ddays.indexOf(d.getDay()) != -1){
26590                     cell.title = ddaysText;
26591                     cell.className = " x-date-disabled";
26592                 }
26593             }
26594             if(ddMatch && format){
26595                 var fvalue = d.dateFormat(format);
26596                 if(ddMatch.test(fvalue)){
26597                     cell.title = ddText.replace("%0", fvalue);
26598                     cell.className = " x-date-disabled";
26599                 }
26600             }
26601         };
26602
26603         var i = 0;
26604         for(; i < startingPos; i++) {
26605             textEls[i].innerHTML = (++prevStart);
26606             d.setDate(d.getDate()+1);
26607             cells[i].className = "x-date-prevday";
26608             setCellClass(this, cells[i]);
26609         }
26610         for(; i < days; i++){
26611             intDay = i - startingPos + 1;
26612             textEls[i].innerHTML = (intDay);
26613             d.setDate(d.getDate()+1);
26614             cells[i].className = "x-date-active";
26615             setCellClass(this, cells[i]);
26616         }
26617         var extraDays = 0;
26618         for(; i < 42; i++) {
26619              textEls[i].innerHTML = (++extraDays);
26620              d.setDate(d.getDate()+1);
26621              cells[i].className = "x-date-nextday";
26622              setCellClass(this, cells[i]);
26623         }
26624
26625         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26626         this.fireEvent('monthchange', this, date);
26627         
26628         if(!this.internalRender){
26629             var main = this.el.dom.firstChild;
26630             var w = main.offsetWidth;
26631             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26632             Roo.fly(main).setWidth(w);
26633             this.internalRender = true;
26634             // opera does not respect the auto grow header center column
26635             // then, after it gets a width opera refuses to recalculate
26636             // without a second pass
26637             if(Roo.isOpera && !this.secondPass){
26638                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26639                 this.secondPass = true;
26640                 this.update.defer(10, this, [date]);
26641             }
26642         }
26643         
26644         
26645     }
26646 });        /*
26647  * Based on:
26648  * Ext JS Library 1.1.1
26649  * Copyright(c) 2006-2007, Ext JS, LLC.
26650  *
26651  * Originally Released Under LGPL - original licence link has changed is not relivant.
26652  *
26653  * Fork - LGPL
26654  * <script type="text/javascript">
26655  */
26656 /**
26657  * @class Roo.TabPanel
26658  * @extends Roo.util.Observable
26659  * A lightweight tab container.
26660  * <br><br>
26661  * Usage:
26662  * <pre><code>
26663 // basic tabs 1, built from existing content
26664 var tabs = new Roo.TabPanel("tabs1");
26665 tabs.addTab("script", "View Script");
26666 tabs.addTab("markup", "View Markup");
26667 tabs.activate("script");
26668
26669 // more advanced tabs, built from javascript
26670 var jtabs = new Roo.TabPanel("jtabs");
26671 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26672
26673 // set up the UpdateManager
26674 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26675 var updater = tab2.getUpdateManager();
26676 updater.setDefaultUrl("ajax1.htm");
26677 tab2.on('activate', updater.refresh, updater, true);
26678
26679 // Use setUrl for Ajax loading
26680 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26681 tab3.setUrl("ajax2.htm", null, true);
26682
26683 // Disabled tab
26684 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26685 tab4.disable();
26686
26687 jtabs.activate("jtabs-1");
26688  * </code></pre>
26689  * @constructor
26690  * Create a new TabPanel.
26691  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26692  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26693  */
26694 Roo.TabPanel = function(container, config){
26695     /**
26696     * The container element for this TabPanel.
26697     * @type Roo.Element
26698     */
26699     this.el = Roo.get(container, true);
26700     if(config){
26701         if(typeof config == "boolean"){
26702             this.tabPosition = config ? "bottom" : "top";
26703         }else{
26704             Roo.apply(this, config);
26705         }
26706     }
26707     if(this.tabPosition == "bottom"){
26708         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26709         this.el.addClass("x-tabs-bottom");
26710     }
26711     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26712     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26713     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26714     if(Roo.isIE){
26715         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26716     }
26717     if(this.tabPosition != "bottom"){
26718         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26719          * @type Roo.Element
26720          */
26721         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26722         this.el.addClass("x-tabs-top");
26723     }
26724     this.items = [];
26725
26726     this.bodyEl.setStyle("position", "relative");
26727
26728     this.active = null;
26729     this.activateDelegate = this.activate.createDelegate(this);
26730
26731     this.addEvents({
26732         /**
26733          * @event tabchange
26734          * Fires when the active tab changes
26735          * @param {Roo.TabPanel} this
26736          * @param {Roo.TabPanelItem} activePanel The new active tab
26737          */
26738         "tabchange": true,
26739         /**
26740          * @event beforetabchange
26741          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26742          * @param {Roo.TabPanel} this
26743          * @param {Object} e Set cancel to true on this object to cancel the tab change
26744          * @param {Roo.TabPanelItem} tab The tab being changed to
26745          */
26746         "beforetabchange" : true
26747     });
26748
26749     Roo.EventManager.onWindowResize(this.onResize, this);
26750     this.cpad = this.el.getPadding("lr");
26751     this.hiddenCount = 0;
26752
26753
26754     // toolbar on the tabbar support...
26755     if (this.toolbar) {
26756         var tcfg = this.toolbar;
26757         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26758         this.toolbar = new Roo.Toolbar(tcfg);
26759         if (Roo.isSafari) {
26760             var tbl = tcfg.container.child('table', true);
26761             tbl.setAttribute('width', '100%');
26762         }
26763         
26764     }
26765    
26766
26767
26768     Roo.TabPanel.superclass.constructor.call(this);
26769 };
26770
26771 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26772     /*
26773      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26774      */
26775     tabPosition : "top",
26776     /*
26777      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26778      */
26779     currentTabWidth : 0,
26780     /*
26781      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26782      */
26783     minTabWidth : 40,
26784     /*
26785      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26786      */
26787     maxTabWidth : 250,
26788     /*
26789      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26790      */
26791     preferredTabWidth : 175,
26792     /*
26793      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26794      */
26795     resizeTabs : false,
26796     /*
26797      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26798      */
26799     monitorResize : true,
26800     /*
26801      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26802      */
26803     toolbar : false,
26804
26805     /**
26806      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26807      * @param {String} id The id of the div to use <b>or create</b>
26808      * @param {String} text The text for the tab
26809      * @param {String} content (optional) Content to put in the TabPanelItem body
26810      * @param {Boolean} closable (optional) True to create a close icon on the tab
26811      * @return {Roo.TabPanelItem} The created TabPanelItem
26812      */
26813     addTab : function(id, text, content, closable){
26814         var item = new Roo.TabPanelItem(this, id, text, closable);
26815         this.addTabItem(item);
26816         if(content){
26817             item.setContent(content);
26818         }
26819         return item;
26820     },
26821
26822     /**
26823      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26824      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26825      * @return {Roo.TabPanelItem}
26826      */
26827     getTab : function(id){
26828         return this.items[id];
26829     },
26830
26831     /**
26832      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26833      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26834      */
26835     hideTab : function(id){
26836         var t = this.items[id];
26837         if(!t.isHidden()){
26838            t.setHidden(true);
26839            this.hiddenCount++;
26840            this.autoSizeTabs();
26841         }
26842     },
26843
26844     /**
26845      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26846      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26847      */
26848     unhideTab : function(id){
26849         var t = this.items[id];
26850         if(t.isHidden()){
26851            t.setHidden(false);
26852            this.hiddenCount--;
26853            this.autoSizeTabs();
26854         }
26855     },
26856
26857     /**
26858      * Adds an existing {@link Roo.TabPanelItem}.
26859      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26860      */
26861     addTabItem : function(item){
26862         this.items[item.id] = item;
26863         this.items.push(item);
26864         if(this.resizeTabs){
26865            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26866            this.autoSizeTabs();
26867         }else{
26868             item.autoSize();
26869         }
26870     },
26871
26872     /**
26873      * Removes a {@link Roo.TabPanelItem}.
26874      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26875      */
26876     removeTab : function(id){
26877         var items = this.items;
26878         var tab = items[id];
26879         if(!tab) { return; }
26880         var index = items.indexOf(tab);
26881         if(this.active == tab && items.length > 1){
26882             var newTab = this.getNextAvailable(index);
26883             if(newTab) {
26884                 newTab.activate();
26885             }
26886         }
26887         this.stripEl.dom.removeChild(tab.pnode.dom);
26888         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26889             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26890         }
26891         items.splice(index, 1);
26892         delete this.items[tab.id];
26893         tab.fireEvent("close", tab);
26894         tab.purgeListeners();
26895         this.autoSizeTabs();
26896     },
26897
26898     getNextAvailable : function(start){
26899         var items = this.items;
26900         var index = start;
26901         // look for a next tab that will slide over to
26902         // replace the one being removed
26903         while(index < items.length){
26904             var item = items[++index];
26905             if(item && !item.isHidden()){
26906                 return item;
26907             }
26908         }
26909         // if one isn't found select the previous tab (on the left)
26910         index = start;
26911         while(index >= 0){
26912             var item = items[--index];
26913             if(item && !item.isHidden()){
26914                 return item;
26915             }
26916         }
26917         return null;
26918     },
26919
26920     /**
26921      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26922      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26923      */
26924     disableTab : function(id){
26925         var tab = this.items[id];
26926         if(tab && this.active != tab){
26927             tab.disable();
26928         }
26929     },
26930
26931     /**
26932      * Enables a {@link Roo.TabPanelItem} that is disabled.
26933      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26934      */
26935     enableTab : function(id){
26936         var tab = this.items[id];
26937         tab.enable();
26938     },
26939
26940     /**
26941      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26942      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26943      * @return {Roo.TabPanelItem} The TabPanelItem.
26944      */
26945     activate : function(id){
26946         var tab = this.items[id];
26947         if(!tab){
26948             return null;
26949         }
26950         if(tab == this.active || tab.disabled){
26951             return tab;
26952         }
26953         var e = {};
26954         this.fireEvent("beforetabchange", this, e, tab);
26955         if(e.cancel !== true && !tab.disabled){
26956             if(this.active){
26957                 this.active.hide();
26958             }
26959             this.active = this.items[id];
26960             this.active.show();
26961             this.fireEvent("tabchange", this, this.active);
26962         }
26963         return tab;
26964     },
26965
26966     /**
26967      * Gets the active {@link Roo.TabPanelItem}.
26968      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26969      */
26970     getActiveTab : function(){
26971         return this.active;
26972     },
26973
26974     /**
26975      * Updates the tab body element to fit the height of the container element
26976      * for overflow scrolling
26977      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26978      */
26979     syncHeight : function(targetHeight){
26980         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26981         var bm = this.bodyEl.getMargins();
26982         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26983         this.bodyEl.setHeight(newHeight);
26984         return newHeight;
26985     },
26986
26987     onResize : function(){
26988         if(this.monitorResize){
26989             this.autoSizeTabs();
26990         }
26991     },
26992
26993     /**
26994      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26995      */
26996     beginUpdate : function(){
26997         this.updating = true;
26998     },
26999
27000     /**
27001      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
27002      */
27003     endUpdate : function(){
27004         this.updating = false;
27005         this.autoSizeTabs();
27006     },
27007
27008     /**
27009      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27010      */
27011     autoSizeTabs : function(){
27012         var count = this.items.length;
27013         var vcount = count - this.hiddenCount;
27014         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
27015         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27016         var availWidth = Math.floor(w / vcount);
27017         var b = this.stripBody;
27018         if(b.getWidth() > w){
27019             var tabs = this.items;
27020             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27021             if(availWidth < this.minTabWidth){
27022                 /*if(!this.sleft){    // incomplete scrolling code
27023                     this.createScrollButtons();
27024                 }
27025                 this.showScroll();
27026                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27027             }
27028         }else{
27029             if(this.currentTabWidth < this.preferredTabWidth){
27030                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27031             }
27032         }
27033     },
27034
27035     /**
27036      * Returns the number of tabs in this TabPanel.
27037      * @return {Number}
27038      */
27039      getCount : function(){
27040          return this.items.length;
27041      },
27042
27043     /**
27044      * Resizes all the tabs to the passed width
27045      * @param {Number} The new width
27046      */
27047     setTabWidth : function(width){
27048         this.currentTabWidth = width;
27049         for(var i = 0, len = this.items.length; i < len; i++) {
27050                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27051         }
27052     },
27053
27054     /**
27055      * Destroys this TabPanel
27056      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27057      */
27058     destroy : function(removeEl){
27059         Roo.EventManager.removeResizeListener(this.onResize, this);
27060         for(var i = 0, len = this.items.length; i < len; i++){
27061             this.items[i].purgeListeners();
27062         }
27063         if(removeEl === true){
27064             this.el.update("");
27065             this.el.remove();
27066         }
27067     }
27068 });
27069
27070 /**
27071  * @class Roo.TabPanelItem
27072  * @extends Roo.util.Observable
27073  * Represents an individual item (tab plus body) in a TabPanel.
27074  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27075  * @param {String} id The id of this TabPanelItem
27076  * @param {String} text The text for the tab of this TabPanelItem
27077  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27078  */
27079 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27080     /**
27081      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27082      * @type Roo.TabPanel
27083      */
27084     this.tabPanel = tabPanel;
27085     /**
27086      * The id for this TabPanelItem
27087      * @type String
27088      */
27089     this.id = id;
27090     /** @private */
27091     this.disabled = false;
27092     /** @private */
27093     this.text = text;
27094     /** @private */
27095     this.loaded = false;
27096     this.closable = closable;
27097
27098     /**
27099      * The body element for this TabPanelItem.
27100      * @type Roo.Element
27101      */
27102     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27103     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27104     this.bodyEl.setStyle("display", "block");
27105     this.bodyEl.setStyle("zoom", "1");
27106     this.hideAction();
27107
27108     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27109     /** @private */
27110     this.el = Roo.get(els.el, true);
27111     this.inner = Roo.get(els.inner, true);
27112     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27113     this.pnode = Roo.get(els.el.parentNode, true);
27114     this.el.on("mousedown", this.onTabMouseDown, this);
27115     this.el.on("click", this.onTabClick, this);
27116     /** @private */
27117     if(closable){
27118         var c = Roo.get(els.close, true);
27119         c.dom.title = this.closeText;
27120         c.addClassOnOver("close-over");
27121         c.on("click", this.closeClick, this);
27122      }
27123
27124     this.addEvents({
27125          /**
27126          * @event activate
27127          * Fires when this tab becomes the active tab.
27128          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27129          * @param {Roo.TabPanelItem} this
27130          */
27131         "activate": true,
27132         /**
27133          * @event beforeclose
27134          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27135          * @param {Roo.TabPanelItem} this
27136          * @param {Object} e Set cancel to true on this object to cancel the close.
27137          */
27138         "beforeclose": true,
27139         /**
27140          * @event close
27141          * Fires when this tab is closed.
27142          * @param {Roo.TabPanelItem} this
27143          */
27144          "close": true,
27145         /**
27146          * @event deactivate
27147          * Fires when this tab is no longer the active tab.
27148          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27149          * @param {Roo.TabPanelItem} this
27150          */
27151          "deactivate" : true
27152     });
27153     this.hidden = false;
27154
27155     Roo.TabPanelItem.superclass.constructor.call(this);
27156 };
27157
27158 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27159     purgeListeners : function(){
27160        Roo.util.Observable.prototype.purgeListeners.call(this);
27161        this.el.removeAllListeners();
27162     },
27163     /**
27164      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27165      */
27166     show : function(){
27167         this.pnode.addClass("on");
27168         this.showAction();
27169         if(Roo.isOpera){
27170             this.tabPanel.stripWrap.repaint();
27171         }
27172         this.fireEvent("activate", this.tabPanel, this);
27173     },
27174
27175     /**
27176      * Returns true if this tab is the active tab.
27177      * @return {Boolean}
27178      */
27179     isActive : function(){
27180         return this.tabPanel.getActiveTab() == this;
27181     },
27182
27183     /**
27184      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27185      */
27186     hide : function(){
27187         this.pnode.removeClass("on");
27188         this.hideAction();
27189         this.fireEvent("deactivate", this.tabPanel, this);
27190     },
27191
27192     hideAction : function(){
27193         this.bodyEl.hide();
27194         this.bodyEl.setStyle("position", "absolute");
27195         this.bodyEl.setLeft("-20000px");
27196         this.bodyEl.setTop("-20000px");
27197     },
27198
27199     showAction : function(){
27200         this.bodyEl.setStyle("position", "relative");
27201         this.bodyEl.setTop("");
27202         this.bodyEl.setLeft("");
27203         this.bodyEl.show();
27204     },
27205
27206     /**
27207      * Set the tooltip for the tab.
27208      * @param {String} tooltip The tab's tooltip
27209      */
27210     setTooltip : function(text){
27211         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27212             this.textEl.dom.qtip = text;
27213             this.textEl.dom.removeAttribute('title');
27214         }else{
27215             this.textEl.dom.title = text;
27216         }
27217     },
27218
27219     onTabClick : function(e){
27220         e.preventDefault();
27221         this.tabPanel.activate(this.id);
27222     },
27223
27224     onTabMouseDown : function(e){
27225         e.preventDefault();
27226         this.tabPanel.activate(this.id);
27227     },
27228
27229     getWidth : function(){
27230         return this.inner.getWidth();
27231     },
27232
27233     setWidth : function(width){
27234         var iwidth = width - this.pnode.getPadding("lr");
27235         this.inner.setWidth(iwidth);
27236         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27237         this.pnode.setWidth(width);
27238     },
27239
27240     /**
27241      * Show or hide the tab
27242      * @param {Boolean} hidden True to hide or false to show.
27243      */
27244     setHidden : function(hidden){
27245         this.hidden = hidden;
27246         this.pnode.setStyle("display", hidden ? "none" : "");
27247     },
27248
27249     /**
27250      * Returns true if this tab is "hidden"
27251      * @return {Boolean}
27252      */
27253     isHidden : function(){
27254         return this.hidden;
27255     },
27256
27257     /**
27258      * Returns the text for this tab
27259      * @return {String}
27260      */
27261     getText : function(){
27262         return this.text;
27263     },
27264
27265     autoSize : function(){
27266         //this.el.beginMeasure();
27267         this.textEl.setWidth(1);
27268         /*
27269          *  #2804 [new] Tabs in Roojs
27270          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27271          */
27272         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27273         //this.el.endMeasure();
27274     },
27275
27276     /**
27277      * Sets the text for the tab (Note: this also sets the tooltip text)
27278      * @param {String} text The tab's text and tooltip
27279      */
27280     setText : function(text){
27281         this.text = text;
27282         this.textEl.update(text);
27283         this.setTooltip(text);
27284         if(!this.tabPanel.resizeTabs){
27285             this.autoSize();
27286         }
27287     },
27288     /**
27289      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27290      */
27291     activate : function(){
27292         this.tabPanel.activate(this.id);
27293     },
27294
27295     /**
27296      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27297      */
27298     disable : function(){
27299         if(this.tabPanel.active != this){
27300             this.disabled = true;
27301             this.pnode.addClass("disabled");
27302         }
27303     },
27304
27305     /**
27306      * Enables this TabPanelItem if it was previously disabled.
27307      */
27308     enable : function(){
27309         this.disabled = false;
27310         this.pnode.removeClass("disabled");
27311     },
27312
27313     /**
27314      * Sets the content for this TabPanelItem.
27315      * @param {String} content The content
27316      * @param {Boolean} loadScripts true to look for and load scripts
27317      */
27318     setContent : function(content, loadScripts){
27319         this.bodyEl.update(content, loadScripts);
27320     },
27321
27322     /**
27323      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27324      * @return {Roo.UpdateManager} The UpdateManager
27325      */
27326     getUpdateManager : function(){
27327         return this.bodyEl.getUpdateManager();
27328     },
27329
27330     /**
27331      * Set a URL to be used to load the content for this TabPanelItem.
27332      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27333      * @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)
27334      * @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)
27335      * @return {Roo.UpdateManager} The UpdateManager
27336      */
27337     setUrl : function(url, params, loadOnce){
27338         if(this.refreshDelegate){
27339             this.un('activate', this.refreshDelegate);
27340         }
27341         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27342         this.on("activate", this.refreshDelegate);
27343         return this.bodyEl.getUpdateManager();
27344     },
27345
27346     /** @private */
27347     _handleRefresh : function(url, params, loadOnce){
27348         if(!loadOnce || !this.loaded){
27349             var updater = this.bodyEl.getUpdateManager();
27350             updater.update(url, params, this._setLoaded.createDelegate(this));
27351         }
27352     },
27353
27354     /**
27355      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27356      *   Will fail silently if the setUrl method has not been called.
27357      *   This does not activate the panel, just updates its content.
27358      */
27359     refresh : function(){
27360         if(this.refreshDelegate){
27361            this.loaded = false;
27362            this.refreshDelegate();
27363         }
27364     },
27365
27366     /** @private */
27367     _setLoaded : function(){
27368         this.loaded = true;
27369     },
27370
27371     /** @private */
27372     closeClick : function(e){
27373         var o = {};
27374         e.stopEvent();
27375         this.fireEvent("beforeclose", this, o);
27376         if(o.cancel !== true){
27377             this.tabPanel.removeTab(this.id);
27378         }
27379     },
27380     /**
27381      * The text displayed in the tooltip for the close icon.
27382      * @type String
27383      */
27384     closeText : "Close this tab"
27385 });
27386
27387 /** @private */
27388 Roo.TabPanel.prototype.createStrip = function(container){
27389     var strip = document.createElement("div");
27390     strip.className = "x-tabs-wrap";
27391     container.appendChild(strip);
27392     return strip;
27393 };
27394 /** @private */
27395 Roo.TabPanel.prototype.createStripList = function(strip){
27396     // div wrapper for retard IE
27397     // returns the "tr" element.
27398     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27399         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27400         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27401     return strip.firstChild.firstChild.firstChild.firstChild;
27402 };
27403 /** @private */
27404 Roo.TabPanel.prototype.createBody = function(container){
27405     var body = document.createElement("div");
27406     Roo.id(body, "tab-body");
27407     Roo.fly(body).addClass("x-tabs-body");
27408     container.appendChild(body);
27409     return body;
27410 };
27411 /** @private */
27412 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27413     var body = Roo.getDom(id);
27414     if(!body){
27415         body = document.createElement("div");
27416         body.id = id;
27417     }
27418     Roo.fly(body).addClass("x-tabs-item-body");
27419     bodyEl.insertBefore(body, bodyEl.firstChild);
27420     return body;
27421 };
27422 /** @private */
27423 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27424     var td = document.createElement("td");
27425     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27426     //stripEl.appendChild(td);
27427     if(closable){
27428         td.className = "x-tabs-closable";
27429         if(!this.closeTpl){
27430             this.closeTpl = new Roo.Template(
27431                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27432                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27433                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27434             );
27435         }
27436         var el = this.closeTpl.overwrite(td, {"text": text});
27437         var close = el.getElementsByTagName("div")[0];
27438         var inner = el.getElementsByTagName("em")[0];
27439         return {"el": el, "close": close, "inner": inner};
27440     } else {
27441         if(!this.tabTpl){
27442             this.tabTpl = new Roo.Template(
27443                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27444                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27445             );
27446         }
27447         var el = this.tabTpl.overwrite(td, {"text": text});
27448         var inner = el.getElementsByTagName("em")[0];
27449         return {"el": el, "inner": inner};
27450     }
27451 };/*
27452  * Based on:
27453  * Ext JS Library 1.1.1
27454  * Copyright(c) 2006-2007, Ext JS, LLC.
27455  *
27456  * Originally Released Under LGPL - original licence link has changed is not relivant.
27457  *
27458  * Fork - LGPL
27459  * <script type="text/javascript">
27460  */
27461
27462 /**
27463  * @class Roo.Button
27464  * @extends Roo.util.Observable
27465  * Simple Button class
27466  * @cfg {String} text The button text
27467  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27468  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27469  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27470  * @cfg {Object} scope The scope of the handler
27471  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27472  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27473  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27474  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27475  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27476  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27477    applies if enableToggle = true)
27478  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27479  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27480   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27481  * @constructor
27482  * Create a new button
27483  * @param {Object} config The config object
27484  */
27485 Roo.Button = function(renderTo, config)
27486 {
27487     if (!config) {
27488         config = renderTo;
27489         renderTo = config.renderTo || false;
27490     }
27491     
27492     Roo.apply(this, config);
27493     this.addEvents({
27494         /**
27495              * @event click
27496              * Fires when this button is clicked
27497              * @param {Button} this
27498              * @param {EventObject} e The click event
27499              */
27500             "click" : true,
27501         /**
27502              * @event toggle
27503              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27504              * @param {Button} this
27505              * @param {Boolean} pressed
27506              */
27507             "toggle" : true,
27508         /**
27509              * @event mouseover
27510              * Fires when the mouse hovers over the button
27511              * @param {Button} this
27512              * @param {Event} e The event object
27513              */
27514         'mouseover' : true,
27515         /**
27516              * @event mouseout
27517              * Fires when the mouse exits the button
27518              * @param {Button} this
27519              * @param {Event} e The event object
27520              */
27521         'mouseout': true,
27522          /**
27523              * @event render
27524              * Fires when the button is rendered
27525              * @param {Button} this
27526              */
27527         'render': true
27528     });
27529     if(this.menu){
27530         this.menu = Roo.menu.MenuMgr.get(this.menu);
27531     }
27532     // register listeners first!!  - so render can be captured..
27533     Roo.util.Observable.call(this);
27534     if(renderTo){
27535         this.render(renderTo);
27536     }
27537     
27538   
27539 };
27540
27541 Roo.extend(Roo.Button, Roo.util.Observable, {
27542     /**
27543      * 
27544      */
27545     
27546     /**
27547      * Read-only. True if this button is hidden
27548      * @type Boolean
27549      */
27550     hidden : false,
27551     /**
27552      * Read-only. True if this button is disabled
27553      * @type Boolean
27554      */
27555     disabled : false,
27556     /**
27557      * Read-only. True if this button is pressed (only if enableToggle = true)
27558      * @type Boolean
27559      */
27560     pressed : false,
27561
27562     /**
27563      * @cfg {Number} tabIndex 
27564      * The DOM tabIndex for this button (defaults to undefined)
27565      */
27566     tabIndex : undefined,
27567
27568     /**
27569      * @cfg {Boolean} enableToggle
27570      * True to enable pressed/not pressed toggling (defaults to false)
27571      */
27572     enableToggle: false,
27573     /**
27574      * @cfg {Mixed} menu
27575      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27576      */
27577     menu : undefined,
27578     /**
27579      * @cfg {String} menuAlign
27580      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27581      */
27582     menuAlign : "tl-bl?",
27583
27584     /**
27585      * @cfg {String} iconCls
27586      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27587      */
27588     iconCls : undefined,
27589     /**
27590      * @cfg {String} type
27591      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27592      */
27593     type : 'button',
27594
27595     // private
27596     menuClassTarget: 'tr',
27597
27598     /**
27599      * @cfg {String} clickEvent
27600      * The type of event to map to the button's event handler (defaults to 'click')
27601      */
27602     clickEvent : 'click',
27603
27604     /**
27605      * @cfg {Boolean} handleMouseEvents
27606      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27607      */
27608     handleMouseEvents : true,
27609
27610     /**
27611      * @cfg {String} tooltipType
27612      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27613      */
27614     tooltipType : 'qtip',
27615
27616     /**
27617      * @cfg {String} cls
27618      * A CSS class to apply to the button's main element.
27619      */
27620     
27621     /**
27622      * @cfg {Roo.Template} template (Optional)
27623      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27624      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27625      * require code modifications if required elements (e.g. a button) aren't present.
27626      */
27627
27628     // private
27629     render : function(renderTo){
27630         var btn;
27631         if(this.hideParent){
27632             this.parentEl = Roo.get(renderTo);
27633         }
27634         if(!this.dhconfig){
27635             if(!this.template){
27636                 if(!Roo.Button.buttonTemplate){
27637                     // hideous table template
27638                     Roo.Button.buttonTemplate = new Roo.Template(
27639                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27640                         '<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>',
27641                         "</tr></tbody></table>");
27642                 }
27643                 this.template = Roo.Button.buttonTemplate;
27644             }
27645             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27646             var btnEl = btn.child("button:first");
27647             btnEl.on('focus', this.onFocus, this);
27648             btnEl.on('blur', this.onBlur, this);
27649             if(this.cls){
27650                 btn.addClass(this.cls);
27651             }
27652             if(this.icon){
27653                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27654             }
27655             if(this.iconCls){
27656                 btnEl.addClass(this.iconCls);
27657                 if(!this.cls){
27658                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27659                 }
27660             }
27661             if(this.tabIndex !== undefined){
27662                 btnEl.dom.tabIndex = this.tabIndex;
27663             }
27664             if(this.tooltip){
27665                 if(typeof this.tooltip == 'object'){
27666                     Roo.QuickTips.tips(Roo.apply({
27667                           target: btnEl.id
27668                     }, this.tooltip));
27669                 } else {
27670                     btnEl.dom[this.tooltipType] = this.tooltip;
27671                 }
27672             }
27673         }else{
27674             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27675         }
27676         this.el = btn;
27677         if(this.id){
27678             this.el.dom.id = this.el.id = this.id;
27679         }
27680         if(this.menu){
27681             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27682             this.menu.on("show", this.onMenuShow, this);
27683             this.menu.on("hide", this.onMenuHide, this);
27684         }
27685         btn.addClass("x-btn");
27686         if(Roo.isIE && !Roo.isIE7){
27687             this.autoWidth.defer(1, this);
27688         }else{
27689             this.autoWidth();
27690         }
27691         if(this.handleMouseEvents){
27692             btn.on("mouseover", this.onMouseOver, this);
27693             btn.on("mouseout", this.onMouseOut, this);
27694             btn.on("mousedown", this.onMouseDown, this);
27695         }
27696         btn.on(this.clickEvent, this.onClick, this);
27697         //btn.on("mouseup", this.onMouseUp, this);
27698         if(this.hidden){
27699             this.hide();
27700         }
27701         if(this.disabled){
27702             this.disable();
27703         }
27704         Roo.ButtonToggleMgr.register(this);
27705         if(this.pressed){
27706             this.el.addClass("x-btn-pressed");
27707         }
27708         if(this.repeat){
27709             var repeater = new Roo.util.ClickRepeater(btn,
27710                 typeof this.repeat == "object" ? this.repeat : {}
27711             );
27712             repeater.on("click", this.onClick,  this);
27713         }
27714         
27715         this.fireEvent('render', this);
27716         
27717     },
27718     /**
27719      * Returns the button's underlying element
27720      * @return {Roo.Element} The element
27721      */
27722     getEl : function(){
27723         return this.el;  
27724     },
27725     
27726     /**
27727      * Destroys this Button and removes any listeners.
27728      */
27729     destroy : function(){
27730         Roo.ButtonToggleMgr.unregister(this);
27731         this.el.removeAllListeners();
27732         this.purgeListeners();
27733         this.el.remove();
27734     },
27735
27736     // private
27737     autoWidth : function(){
27738         if(this.el){
27739             this.el.setWidth("auto");
27740             if(Roo.isIE7 && Roo.isStrict){
27741                 var ib = this.el.child('button');
27742                 if(ib && ib.getWidth() > 20){
27743                     ib.clip();
27744                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27745                 }
27746             }
27747             if(this.minWidth){
27748                 if(this.hidden){
27749                     this.el.beginMeasure();
27750                 }
27751                 if(this.el.getWidth() < this.minWidth){
27752                     this.el.setWidth(this.minWidth);
27753                 }
27754                 if(this.hidden){
27755                     this.el.endMeasure();
27756                 }
27757             }
27758         }
27759     },
27760
27761     /**
27762      * Assigns this button's click handler
27763      * @param {Function} handler The function to call when the button is clicked
27764      * @param {Object} scope (optional) Scope for the function passed in
27765      */
27766     setHandler : function(handler, scope){
27767         this.handler = handler;
27768         this.scope = scope;  
27769     },
27770     
27771     /**
27772      * Sets this button's text
27773      * @param {String} text The button text
27774      */
27775     setText : function(text){
27776         this.text = text;
27777         if(this.el){
27778             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27779         }
27780         this.autoWidth();
27781     },
27782     
27783     /**
27784      * Gets the text for this button
27785      * @return {String} The button text
27786      */
27787     getText : function(){
27788         return this.text;  
27789     },
27790     
27791     /**
27792      * Show this button
27793      */
27794     show: function(){
27795         this.hidden = false;
27796         if(this.el){
27797             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27798         }
27799     },
27800     
27801     /**
27802      * Hide this button
27803      */
27804     hide: function(){
27805         this.hidden = true;
27806         if(this.el){
27807             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27808         }
27809     },
27810     
27811     /**
27812      * Convenience function for boolean show/hide
27813      * @param {Boolean} visible True to show, false to hide
27814      */
27815     setVisible: function(visible){
27816         if(visible) {
27817             this.show();
27818         }else{
27819             this.hide();
27820         }
27821     },
27822     
27823     /**
27824      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27825      * @param {Boolean} state (optional) Force a particular state
27826      */
27827     toggle : function(state){
27828         state = state === undefined ? !this.pressed : state;
27829         if(state != this.pressed){
27830             if(state){
27831                 this.el.addClass("x-btn-pressed");
27832                 this.pressed = true;
27833                 this.fireEvent("toggle", this, true);
27834             }else{
27835                 this.el.removeClass("x-btn-pressed");
27836                 this.pressed = false;
27837                 this.fireEvent("toggle", this, false);
27838             }
27839             if(this.toggleHandler){
27840                 this.toggleHandler.call(this.scope || this, this, state);
27841             }
27842         }
27843     },
27844     
27845     /**
27846      * Focus the button
27847      */
27848     focus : function(){
27849         this.el.child('button:first').focus();
27850     },
27851     
27852     /**
27853      * Disable this button
27854      */
27855     disable : function(){
27856         if(this.el){
27857             this.el.addClass("x-btn-disabled");
27858         }
27859         this.disabled = true;
27860     },
27861     
27862     /**
27863      * Enable this button
27864      */
27865     enable : function(){
27866         if(this.el){
27867             this.el.removeClass("x-btn-disabled");
27868         }
27869         this.disabled = false;
27870     },
27871
27872     /**
27873      * Convenience function for boolean enable/disable
27874      * @param {Boolean} enabled True to enable, false to disable
27875      */
27876     setDisabled : function(v){
27877         this[v !== true ? "enable" : "disable"]();
27878     },
27879
27880     // private
27881     onClick : function(e)
27882     {
27883         if(e){
27884             e.preventDefault();
27885         }
27886         if(e.button != 0){
27887             return;
27888         }
27889         if(!this.disabled){
27890             if(this.enableToggle){
27891                 this.toggle();
27892             }
27893             if(this.menu && !this.menu.isVisible()){
27894                 this.menu.show(this.el, this.menuAlign);
27895             }
27896             this.fireEvent("click", this, e);
27897             if(this.handler){
27898                 this.el.removeClass("x-btn-over");
27899                 this.handler.call(this.scope || this, this, e);
27900             }
27901         }
27902     },
27903     // private
27904     onMouseOver : function(e){
27905         if(!this.disabled){
27906             this.el.addClass("x-btn-over");
27907             this.fireEvent('mouseover', this, e);
27908         }
27909     },
27910     // private
27911     onMouseOut : function(e){
27912         if(!e.within(this.el,  true)){
27913             this.el.removeClass("x-btn-over");
27914             this.fireEvent('mouseout', this, e);
27915         }
27916     },
27917     // private
27918     onFocus : function(e){
27919         if(!this.disabled){
27920             this.el.addClass("x-btn-focus");
27921         }
27922     },
27923     // private
27924     onBlur : function(e){
27925         this.el.removeClass("x-btn-focus");
27926     },
27927     // private
27928     onMouseDown : function(e){
27929         if(!this.disabled && e.button == 0){
27930             this.el.addClass("x-btn-click");
27931             Roo.get(document).on('mouseup', this.onMouseUp, this);
27932         }
27933     },
27934     // private
27935     onMouseUp : function(e){
27936         if(e.button == 0){
27937             this.el.removeClass("x-btn-click");
27938             Roo.get(document).un('mouseup', this.onMouseUp, this);
27939         }
27940     },
27941     // private
27942     onMenuShow : function(e){
27943         this.el.addClass("x-btn-menu-active");
27944     },
27945     // private
27946     onMenuHide : function(e){
27947         this.el.removeClass("x-btn-menu-active");
27948     }   
27949 });
27950
27951 // Private utility class used by Button
27952 Roo.ButtonToggleMgr = function(){
27953    var groups = {};
27954    
27955    function toggleGroup(btn, state){
27956        if(state){
27957            var g = groups[btn.toggleGroup];
27958            for(var i = 0, l = g.length; i < l; i++){
27959                if(g[i] != btn){
27960                    g[i].toggle(false);
27961                }
27962            }
27963        }
27964    }
27965    
27966    return {
27967        register : function(btn){
27968            if(!btn.toggleGroup){
27969                return;
27970            }
27971            var g = groups[btn.toggleGroup];
27972            if(!g){
27973                g = groups[btn.toggleGroup] = [];
27974            }
27975            g.push(btn);
27976            btn.on("toggle", toggleGroup);
27977        },
27978        
27979        unregister : function(btn){
27980            if(!btn.toggleGroup){
27981                return;
27982            }
27983            var g = groups[btn.toggleGroup];
27984            if(g){
27985                g.remove(btn);
27986                btn.un("toggle", toggleGroup);
27987            }
27988        }
27989    };
27990 }();/*
27991  * Based on:
27992  * Ext JS Library 1.1.1
27993  * Copyright(c) 2006-2007, Ext JS, LLC.
27994  *
27995  * Originally Released Under LGPL - original licence link has changed is not relivant.
27996  *
27997  * Fork - LGPL
27998  * <script type="text/javascript">
27999  */
28000  
28001 /**
28002  * @class Roo.SplitButton
28003  * @extends Roo.Button
28004  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28005  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28006  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28007  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28008  * @cfg {String} arrowTooltip The title attribute of the arrow
28009  * @constructor
28010  * Create a new menu button
28011  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28012  * @param {Object} config The config object
28013  */
28014 Roo.SplitButton = function(renderTo, config){
28015     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28016     /**
28017      * @event arrowclick
28018      * Fires when this button's arrow is clicked
28019      * @param {SplitButton} this
28020      * @param {EventObject} e The click event
28021      */
28022     this.addEvents({"arrowclick":true});
28023 };
28024
28025 Roo.extend(Roo.SplitButton, Roo.Button, {
28026     render : function(renderTo){
28027         // this is one sweet looking template!
28028         var tpl = new Roo.Template(
28029             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28030             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28031             '<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>',
28032             "</tbody></table></td><td>",
28033             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28034             '<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>',
28035             "</tbody></table></td></tr></table>"
28036         );
28037         var btn = tpl.append(renderTo, [this.text, this.type], true);
28038         var btnEl = btn.child("button");
28039         if(this.cls){
28040             btn.addClass(this.cls);
28041         }
28042         if(this.icon){
28043             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28044         }
28045         if(this.iconCls){
28046             btnEl.addClass(this.iconCls);
28047             if(!this.cls){
28048                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28049             }
28050         }
28051         this.el = btn;
28052         if(this.handleMouseEvents){
28053             btn.on("mouseover", this.onMouseOver, this);
28054             btn.on("mouseout", this.onMouseOut, this);
28055             btn.on("mousedown", this.onMouseDown, this);
28056             btn.on("mouseup", this.onMouseUp, this);
28057         }
28058         btn.on(this.clickEvent, this.onClick, this);
28059         if(this.tooltip){
28060             if(typeof this.tooltip == 'object'){
28061                 Roo.QuickTips.tips(Roo.apply({
28062                       target: btnEl.id
28063                 }, this.tooltip));
28064             } else {
28065                 btnEl.dom[this.tooltipType] = this.tooltip;
28066             }
28067         }
28068         if(this.arrowTooltip){
28069             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28070         }
28071         if(this.hidden){
28072             this.hide();
28073         }
28074         if(this.disabled){
28075             this.disable();
28076         }
28077         if(this.pressed){
28078             this.el.addClass("x-btn-pressed");
28079         }
28080         if(Roo.isIE && !Roo.isIE7){
28081             this.autoWidth.defer(1, this);
28082         }else{
28083             this.autoWidth();
28084         }
28085         if(this.menu){
28086             this.menu.on("show", this.onMenuShow, this);
28087             this.menu.on("hide", this.onMenuHide, this);
28088         }
28089         this.fireEvent('render', this);
28090     },
28091
28092     // private
28093     autoWidth : function(){
28094         if(this.el){
28095             var tbl = this.el.child("table:first");
28096             var tbl2 = this.el.child("table:last");
28097             this.el.setWidth("auto");
28098             tbl.setWidth("auto");
28099             if(Roo.isIE7 && Roo.isStrict){
28100                 var ib = this.el.child('button:first');
28101                 if(ib && ib.getWidth() > 20){
28102                     ib.clip();
28103                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28104                 }
28105             }
28106             if(this.minWidth){
28107                 if(this.hidden){
28108                     this.el.beginMeasure();
28109                 }
28110                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28111                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28112                 }
28113                 if(this.hidden){
28114                     this.el.endMeasure();
28115                 }
28116             }
28117             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28118         } 
28119     },
28120     /**
28121      * Sets this button's click handler
28122      * @param {Function} handler The function to call when the button is clicked
28123      * @param {Object} scope (optional) Scope for the function passed above
28124      */
28125     setHandler : function(handler, scope){
28126         this.handler = handler;
28127         this.scope = scope;  
28128     },
28129     
28130     /**
28131      * Sets this button's arrow click handler
28132      * @param {Function} handler The function to call when the arrow is clicked
28133      * @param {Object} scope (optional) Scope for the function passed above
28134      */
28135     setArrowHandler : function(handler, scope){
28136         this.arrowHandler = handler;
28137         this.scope = scope;  
28138     },
28139     
28140     /**
28141      * Focus the button
28142      */
28143     focus : function(){
28144         if(this.el){
28145             this.el.child("button:first").focus();
28146         }
28147     },
28148
28149     // private
28150     onClick : function(e){
28151         e.preventDefault();
28152         if(!this.disabled){
28153             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28154                 if(this.menu && !this.menu.isVisible()){
28155                     this.menu.show(this.el, this.menuAlign);
28156                 }
28157                 this.fireEvent("arrowclick", this, e);
28158                 if(this.arrowHandler){
28159                     this.arrowHandler.call(this.scope || this, this, e);
28160                 }
28161             }else{
28162                 this.fireEvent("click", this, e);
28163                 if(this.handler){
28164                     this.handler.call(this.scope || this, this, e);
28165                 }
28166             }
28167         }
28168     },
28169     // private
28170     onMouseDown : function(e){
28171         if(!this.disabled){
28172             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28173         }
28174     },
28175     // private
28176     onMouseUp : function(e){
28177         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28178     }   
28179 });
28180
28181
28182 // backwards compat
28183 Roo.MenuButton = Roo.SplitButton;/*
28184  * Based on:
28185  * Ext JS Library 1.1.1
28186  * Copyright(c) 2006-2007, Ext JS, LLC.
28187  *
28188  * Originally Released Under LGPL - original licence link has changed is not relivant.
28189  *
28190  * Fork - LGPL
28191  * <script type="text/javascript">
28192  */
28193
28194 /**
28195  * @class Roo.Toolbar
28196  * Basic Toolbar class.
28197  * @constructor
28198  * Creates a new Toolbar
28199  * @param {Object} container The config object
28200  */ 
28201 Roo.Toolbar = function(container, buttons, config)
28202 {
28203     /// old consturctor format still supported..
28204     if(container instanceof Array){ // omit the container for later rendering
28205         buttons = container;
28206         config = buttons;
28207         container = null;
28208     }
28209     if (typeof(container) == 'object' && container.xtype) {
28210         config = container;
28211         container = config.container;
28212         buttons = config.buttons || []; // not really - use items!!
28213     }
28214     var xitems = [];
28215     if (config && config.items) {
28216         xitems = config.items;
28217         delete config.items;
28218     }
28219     Roo.apply(this, config);
28220     this.buttons = buttons;
28221     
28222     if(container){
28223         this.render(container);
28224     }
28225     this.xitems = xitems;
28226     Roo.each(xitems, function(b) {
28227         this.add(b);
28228     }, this);
28229     
28230 };
28231
28232 Roo.Toolbar.prototype = {
28233     /**
28234      * @cfg {Array} items
28235      * array of button configs or elements to add (will be converted to a MixedCollection)
28236      */
28237     
28238     /**
28239      * @cfg {String/HTMLElement/Element} container
28240      * The id or element that will contain the toolbar
28241      */
28242     // private
28243     render : function(ct){
28244         this.el = Roo.get(ct);
28245         if(this.cls){
28246             this.el.addClass(this.cls);
28247         }
28248         // using a table allows for vertical alignment
28249         // 100% width is needed by Safari...
28250         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28251         this.tr = this.el.child("tr", true);
28252         var autoId = 0;
28253         this.items = new Roo.util.MixedCollection(false, function(o){
28254             return o.id || ("item" + (++autoId));
28255         });
28256         if(this.buttons){
28257             this.add.apply(this, this.buttons);
28258             delete this.buttons;
28259         }
28260     },
28261
28262     /**
28263      * Adds element(s) to the toolbar -- this function takes a variable number of 
28264      * arguments of mixed type and adds them to the toolbar.
28265      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28266      * <ul>
28267      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28268      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28269      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28270      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28271      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28272      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28273      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28274      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28275      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28276      * </ul>
28277      * @param {Mixed} arg2
28278      * @param {Mixed} etc.
28279      */
28280     add : function(){
28281         var a = arguments, l = a.length;
28282         for(var i = 0; i < l; i++){
28283             this._add(a[i]);
28284         }
28285     },
28286     // private..
28287     _add : function(el) {
28288         
28289         if (el.xtype) {
28290             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28291         }
28292         
28293         if (el.applyTo){ // some kind of form field
28294             return this.addField(el);
28295         } 
28296         if (el.render){ // some kind of Toolbar.Item
28297             return this.addItem(el);
28298         }
28299         if (typeof el == "string"){ // string
28300             if(el == "separator" || el == "-"){
28301                 return this.addSeparator();
28302             }
28303             if (el == " "){
28304                 return this.addSpacer();
28305             }
28306             if(el == "->"){
28307                 return this.addFill();
28308             }
28309             return this.addText(el);
28310             
28311         }
28312         if(el.tagName){ // element
28313             return this.addElement(el);
28314         }
28315         if(typeof el == "object"){ // must be button config?
28316             return this.addButton(el);
28317         }
28318         // and now what?!?!
28319         return false;
28320         
28321     },
28322     
28323     /**
28324      * Add an Xtype element
28325      * @param {Object} xtype Xtype Object
28326      * @return {Object} created Object
28327      */
28328     addxtype : function(e){
28329         return this.add(e);  
28330     },
28331     
28332     /**
28333      * Returns the Element for this toolbar.
28334      * @return {Roo.Element}
28335      */
28336     getEl : function(){
28337         return this.el;  
28338     },
28339     
28340     /**
28341      * Adds a separator
28342      * @return {Roo.Toolbar.Item} The separator item
28343      */
28344     addSeparator : function(){
28345         return this.addItem(new Roo.Toolbar.Separator());
28346     },
28347
28348     /**
28349      * Adds a spacer element
28350      * @return {Roo.Toolbar.Spacer} The spacer item
28351      */
28352     addSpacer : function(){
28353         return this.addItem(new Roo.Toolbar.Spacer());
28354     },
28355
28356     /**
28357      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28358      * @return {Roo.Toolbar.Fill} The fill item
28359      */
28360     addFill : function(){
28361         return this.addItem(new Roo.Toolbar.Fill());
28362     },
28363
28364     /**
28365      * Adds any standard HTML element to the toolbar
28366      * @param {String/HTMLElement/Element} el The element or id of the element to add
28367      * @return {Roo.Toolbar.Item} The element's item
28368      */
28369     addElement : function(el){
28370         return this.addItem(new Roo.Toolbar.Item(el));
28371     },
28372     /**
28373      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28374      * @type Roo.util.MixedCollection  
28375      */
28376     items : false,
28377      
28378     /**
28379      * Adds any Toolbar.Item or subclass
28380      * @param {Roo.Toolbar.Item} item
28381      * @return {Roo.Toolbar.Item} The item
28382      */
28383     addItem : function(item){
28384         var td = this.nextBlock();
28385         item.render(td);
28386         this.items.add(item);
28387         return item;
28388     },
28389     
28390     /**
28391      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28392      * @param {Object/Array} config A button config or array of configs
28393      * @return {Roo.Toolbar.Button/Array}
28394      */
28395     addButton : function(config){
28396         if(config instanceof Array){
28397             var buttons = [];
28398             for(var i = 0, len = config.length; i < len; i++) {
28399                 buttons.push(this.addButton(config[i]));
28400             }
28401             return buttons;
28402         }
28403         var b = config;
28404         if(!(config instanceof Roo.Toolbar.Button)){
28405             b = config.split ?
28406                 new Roo.Toolbar.SplitButton(config) :
28407                 new Roo.Toolbar.Button(config);
28408         }
28409         var td = this.nextBlock();
28410         b.render(td);
28411         this.items.add(b);
28412         return b;
28413     },
28414     
28415     /**
28416      * Adds text to the toolbar
28417      * @param {String} text The text to add
28418      * @return {Roo.Toolbar.Item} The element's item
28419      */
28420     addText : function(text){
28421         return this.addItem(new Roo.Toolbar.TextItem(text));
28422     },
28423     
28424     /**
28425      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28426      * @param {Number} index The index where the item is to be inserted
28427      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28428      * @return {Roo.Toolbar.Button/Item}
28429      */
28430     insertButton : function(index, item){
28431         if(item instanceof Array){
28432             var buttons = [];
28433             for(var i = 0, len = item.length; i < len; i++) {
28434                buttons.push(this.insertButton(index + i, item[i]));
28435             }
28436             return buttons;
28437         }
28438         if (!(item instanceof Roo.Toolbar.Button)){
28439            item = new Roo.Toolbar.Button(item);
28440         }
28441         var td = document.createElement("td");
28442         this.tr.insertBefore(td, this.tr.childNodes[index]);
28443         item.render(td);
28444         this.items.insert(index, item);
28445         return item;
28446     },
28447     
28448     /**
28449      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28450      * @param {Object} config
28451      * @return {Roo.Toolbar.Item} The element's item
28452      */
28453     addDom : function(config, returnEl){
28454         var td = this.nextBlock();
28455         Roo.DomHelper.overwrite(td, config);
28456         var ti = new Roo.Toolbar.Item(td.firstChild);
28457         ti.render(td);
28458         this.items.add(ti);
28459         return ti;
28460     },
28461
28462     /**
28463      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28464      * @type Roo.util.MixedCollection  
28465      */
28466     fields : false,
28467     
28468     /**
28469      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28470      * Note: the field should not have been rendered yet. For a field that has already been
28471      * rendered, use {@link #addElement}.
28472      * @param {Roo.form.Field} field
28473      * @return {Roo.ToolbarItem}
28474      */
28475      
28476       
28477     addField : function(field) {
28478         if (!this.fields) {
28479             var autoId = 0;
28480             this.fields = new Roo.util.MixedCollection(false, function(o){
28481                 return o.id || ("item" + (++autoId));
28482             });
28483
28484         }
28485         
28486         var td = this.nextBlock();
28487         field.render(td);
28488         var ti = new Roo.Toolbar.Item(td.firstChild);
28489         ti.render(td);
28490         this.items.add(ti);
28491         this.fields.add(field);
28492         return ti;
28493     },
28494     /**
28495      * Hide the toolbar
28496      * @method hide
28497      */
28498      
28499       
28500     hide : function()
28501     {
28502         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28503         this.el.child('div').hide();
28504     },
28505     /**
28506      * Show the toolbar
28507      * @method show
28508      */
28509     show : function()
28510     {
28511         this.el.child('div').show();
28512     },
28513       
28514     // private
28515     nextBlock : function(){
28516         var td = document.createElement("td");
28517         this.tr.appendChild(td);
28518         return td;
28519     },
28520
28521     // private
28522     destroy : function(){
28523         if(this.items){ // rendered?
28524             Roo.destroy.apply(Roo, this.items.items);
28525         }
28526         if(this.fields){ // rendered?
28527             Roo.destroy.apply(Roo, this.fields.items);
28528         }
28529         Roo.Element.uncache(this.el, this.tr);
28530     }
28531 };
28532
28533 /**
28534  * @class Roo.Toolbar.Item
28535  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28536  * @constructor
28537  * Creates a new Item
28538  * @param {HTMLElement} el 
28539  */
28540 Roo.Toolbar.Item = function(el){
28541     var cfg = {};
28542     if (typeof (el.xtype) != 'undefined') {
28543         cfg = el;
28544         el = cfg.el;
28545     }
28546     
28547     this.el = Roo.getDom(el);
28548     this.id = Roo.id(this.el);
28549     this.hidden = false;
28550     
28551     this.addEvents({
28552          /**
28553              * @event render
28554              * Fires when the button is rendered
28555              * @param {Button} this
28556              */
28557         'render': true
28558     });
28559     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28560 };
28561 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28562 //Roo.Toolbar.Item.prototype = {
28563     
28564     /**
28565      * Get this item's HTML Element
28566      * @return {HTMLElement}
28567      */
28568     getEl : function(){
28569        return this.el;  
28570     },
28571
28572     // private
28573     render : function(td){
28574         
28575          this.td = td;
28576         td.appendChild(this.el);
28577         
28578         this.fireEvent('render', this);
28579     },
28580     
28581     /**
28582      * Removes and destroys this item.
28583      */
28584     destroy : function(){
28585         this.td.parentNode.removeChild(this.td);
28586     },
28587     
28588     /**
28589      * Shows this item.
28590      */
28591     show: function(){
28592         this.hidden = false;
28593         this.td.style.display = "";
28594     },
28595     
28596     /**
28597      * Hides this item.
28598      */
28599     hide: function(){
28600         this.hidden = true;
28601         this.td.style.display = "none";
28602     },
28603     
28604     /**
28605      * Convenience function for boolean show/hide.
28606      * @param {Boolean} visible true to show/false to hide
28607      */
28608     setVisible: function(visible){
28609         if(visible) {
28610             this.show();
28611         }else{
28612             this.hide();
28613         }
28614     },
28615     
28616     /**
28617      * Try to focus this item.
28618      */
28619     focus : function(){
28620         Roo.fly(this.el).focus();
28621     },
28622     
28623     /**
28624      * Disables this item.
28625      */
28626     disable : function(){
28627         Roo.fly(this.td).addClass("x-item-disabled");
28628         this.disabled = true;
28629         this.el.disabled = true;
28630     },
28631     
28632     /**
28633      * Enables this item.
28634      */
28635     enable : function(){
28636         Roo.fly(this.td).removeClass("x-item-disabled");
28637         this.disabled = false;
28638         this.el.disabled = false;
28639     }
28640 });
28641
28642
28643 /**
28644  * @class Roo.Toolbar.Separator
28645  * @extends Roo.Toolbar.Item
28646  * A simple toolbar separator class
28647  * @constructor
28648  * Creates a new Separator
28649  */
28650 Roo.Toolbar.Separator = function(cfg){
28651     
28652     var s = document.createElement("span");
28653     s.className = "ytb-sep";
28654     if (cfg) {
28655         cfg.el = s;
28656     }
28657     
28658     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28659 };
28660 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28661     enable:Roo.emptyFn,
28662     disable:Roo.emptyFn,
28663     focus:Roo.emptyFn
28664 });
28665
28666 /**
28667  * @class Roo.Toolbar.Spacer
28668  * @extends Roo.Toolbar.Item
28669  * A simple element that adds extra horizontal space to a toolbar.
28670  * @constructor
28671  * Creates a new Spacer
28672  */
28673 Roo.Toolbar.Spacer = function(cfg){
28674     var s = document.createElement("div");
28675     s.className = "ytb-spacer";
28676     if (cfg) {
28677         cfg.el = s;
28678     }
28679     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28680 };
28681 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28682     enable:Roo.emptyFn,
28683     disable:Roo.emptyFn,
28684     focus:Roo.emptyFn
28685 });
28686
28687 /**
28688  * @class Roo.Toolbar.Fill
28689  * @extends Roo.Toolbar.Spacer
28690  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28691  * @constructor
28692  * Creates a new Spacer
28693  */
28694 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28695     // private
28696     render : function(td){
28697         td.style.width = '100%';
28698         Roo.Toolbar.Fill.superclass.render.call(this, td);
28699     }
28700 });
28701
28702 /**
28703  * @class Roo.Toolbar.TextItem
28704  * @extends Roo.Toolbar.Item
28705  * A simple class that renders text directly into a toolbar.
28706  * @constructor
28707  * Creates a new TextItem
28708  * @param {String} text
28709  */
28710 Roo.Toolbar.TextItem = function(cfg){
28711     var  text = cfg || "";
28712     if (typeof(cfg) == 'object') {
28713         text = cfg.text || "";
28714     }  else {
28715         cfg = null;
28716     }
28717     var s = document.createElement("span");
28718     s.className = "ytb-text";
28719     s.innerHTML = text;
28720     if (cfg) {
28721         cfg.el  = s;
28722     }
28723     
28724     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28725 };
28726 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28727     
28728      
28729     enable:Roo.emptyFn,
28730     disable:Roo.emptyFn,
28731     focus:Roo.emptyFn
28732 });
28733
28734 /**
28735  * @class Roo.Toolbar.Button
28736  * @extends Roo.Button
28737  * A button that renders into a toolbar.
28738  * @constructor
28739  * Creates a new Button
28740  * @param {Object} config A standard {@link Roo.Button} config object
28741  */
28742 Roo.Toolbar.Button = function(config){
28743     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28744 };
28745 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28746     render : function(td){
28747         this.td = td;
28748         Roo.Toolbar.Button.superclass.render.call(this, td);
28749     },
28750     
28751     /**
28752      * Removes and destroys this button
28753      */
28754     destroy : function(){
28755         Roo.Toolbar.Button.superclass.destroy.call(this);
28756         this.td.parentNode.removeChild(this.td);
28757     },
28758     
28759     /**
28760      * Shows this button
28761      */
28762     show: function(){
28763         this.hidden = false;
28764         this.td.style.display = "";
28765     },
28766     
28767     /**
28768      * Hides this button
28769      */
28770     hide: function(){
28771         this.hidden = true;
28772         this.td.style.display = "none";
28773     },
28774
28775     /**
28776      * Disables this item
28777      */
28778     disable : function(){
28779         Roo.fly(this.td).addClass("x-item-disabled");
28780         this.disabled = true;
28781     },
28782
28783     /**
28784      * Enables this item
28785      */
28786     enable : function(){
28787         Roo.fly(this.td).removeClass("x-item-disabled");
28788         this.disabled = false;
28789     }
28790 });
28791 // backwards compat
28792 Roo.ToolbarButton = Roo.Toolbar.Button;
28793
28794 /**
28795  * @class Roo.Toolbar.SplitButton
28796  * @extends Roo.SplitButton
28797  * A menu button that renders into a toolbar.
28798  * @constructor
28799  * Creates a new SplitButton
28800  * @param {Object} config A standard {@link Roo.SplitButton} config object
28801  */
28802 Roo.Toolbar.SplitButton = function(config){
28803     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28804 };
28805 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28806     render : function(td){
28807         this.td = td;
28808         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28809     },
28810     
28811     /**
28812      * Removes and destroys this button
28813      */
28814     destroy : function(){
28815         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28816         this.td.parentNode.removeChild(this.td);
28817     },
28818     
28819     /**
28820      * Shows this button
28821      */
28822     show: function(){
28823         this.hidden = false;
28824         this.td.style.display = "";
28825     },
28826     
28827     /**
28828      * Hides this button
28829      */
28830     hide: function(){
28831         this.hidden = true;
28832         this.td.style.display = "none";
28833     }
28834 });
28835
28836 // backwards compat
28837 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28838  * Based on:
28839  * Ext JS Library 1.1.1
28840  * Copyright(c) 2006-2007, Ext JS, LLC.
28841  *
28842  * Originally Released Under LGPL - original licence link has changed is not relivant.
28843  *
28844  * Fork - LGPL
28845  * <script type="text/javascript">
28846  */
28847  
28848 /**
28849  * @class Roo.PagingToolbar
28850  * @extends Roo.Toolbar
28851  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28852  * @constructor
28853  * Create a new PagingToolbar
28854  * @param {Object} config The config object
28855  */
28856 Roo.PagingToolbar = function(el, ds, config)
28857 {
28858     // old args format still supported... - xtype is prefered..
28859     if (typeof(el) == 'object' && el.xtype) {
28860         // created from xtype...
28861         config = el;
28862         ds = el.dataSource;
28863         el = config.container;
28864     }
28865     var items = [];
28866     if (config.items) {
28867         items = config.items;
28868         config.items = [];
28869     }
28870     
28871     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28872     this.ds = ds;
28873     this.cursor = 0;
28874     this.renderButtons(this.el);
28875     this.bind(ds);
28876     
28877     // supprot items array.
28878    
28879     Roo.each(items, function(e) {
28880         this.add(Roo.factory(e));
28881     },this);
28882     
28883 };
28884
28885 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28886     /**
28887      * @cfg {Roo.data.Store} dataSource
28888      * The underlying data store providing the paged data
28889      */
28890     /**
28891      * @cfg {String/HTMLElement/Element} container
28892      * container The id or element that will contain the toolbar
28893      */
28894     /**
28895      * @cfg {Boolean} displayInfo
28896      * True to display the displayMsg (defaults to false)
28897      */
28898     /**
28899      * @cfg {Number} pageSize
28900      * The number of records to display per page (defaults to 20)
28901      */
28902     pageSize: 20,
28903     /**
28904      * @cfg {String} displayMsg
28905      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28906      */
28907     displayMsg : 'Displaying {0} - {1} of {2}',
28908     /**
28909      * @cfg {String} emptyMsg
28910      * The message to display when no records are found (defaults to "No data to display")
28911      */
28912     emptyMsg : 'No data to display',
28913     /**
28914      * Customizable piece of the default paging text (defaults to "Page")
28915      * @type String
28916      */
28917     beforePageText : "Page",
28918     /**
28919      * Customizable piece of the default paging text (defaults to "of %0")
28920      * @type String
28921      */
28922     afterPageText : "of {0}",
28923     /**
28924      * Customizable piece of the default paging text (defaults to "First Page")
28925      * @type String
28926      */
28927     firstText : "First Page",
28928     /**
28929      * Customizable piece of the default paging text (defaults to "Previous Page")
28930      * @type String
28931      */
28932     prevText : "Previous Page",
28933     /**
28934      * Customizable piece of the default paging text (defaults to "Next Page")
28935      * @type String
28936      */
28937     nextText : "Next Page",
28938     /**
28939      * Customizable piece of the default paging text (defaults to "Last Page")
28940      * @type String
28941      */
28942     lastText : "Last Page",
28943     /**
28944      * Customizable piece of the default paging text (defaults to "Refresh")
28945      * @type String
28946      */
28947     refreshText : "Refresh",
28948
28949     // private
28950     renderButtons : function(el){
28951         Roo.PagingToolbar.superclass.render.call(this, el);
28952         this.first = this.addButton({
28953             tooltip: this.firstText,
28954             cls: "x-btn-icon x-grid-page-first",
28955             disabled: true,
28956             handler: this.onClick.createDelegate(this, ["first"])
28957         });
28958         this.prev = this.addButton({
28959             tooltip: this.prevText,
28960             cls: "x-btn-icon x-grid-page-prev",
28961             disabled: true,
28962             handler: this.onClick.createDelegate(this, ["prev"])
28963         });
28964         //this.addSeparator();
28965         this.add(this.beforePageText);
28966         this.field = Roo.get(this.addDom({
28967            tag: "input",
28968            type: "text",
28969            size: "3",
28970            value: "1",
28971            cls: "x-grid-page-number"
28972         }).el);
28973         this.field.on("keydown", this.onPagingKeydown, this);
28974         this.field.on("focus", function(){this.dom.select();});
28975         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28976         this.field.setHeight(18);
28977         //this.addSeparator();
28978         this.next = this.addButton({
28979             tooltip: this.nextText,
28980             cls: "x-btn-icon x-grid-page-next",
28981             disabled: true,
28982             handler: this.onClick.createDelegate(this, ["next"])
28983         });
28984         this.last = this.addButton({
28985             tooltip: this.lastText,
28986             cls: "x-btn-icon x-grid-page-last",
28987             disabled: true,
28988             handler: this.onClick.createDelegate(this, ["last"])
28989         });
28990         //this.addSeparator();
28991         this.loading = this.addButton({
28992             tooltip: this.refreshText,
28993             cls: "x-btn-icon x-grid-loading",
28994             handler: this.onClick.createDelegate(this, ["refresh"])
28995         });
28996
28997         if(this.displayInfo){
28998             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28999         }
29000     },
29001
29002     // private
29003     updateInfo : function(){
29004         if(this.displayEl){
29005             var count = this.ds.getCount();
29006             var msg = count == 0 ?
29007                 this.emptyMsg :
29008                 String.format(
29009                     this.displayMsg,
29010                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29011                 );
29012             this.displayEl.update(msg);
29013         }
29014     },
29015
29016     // private
29017     onLoad : function(ds, r, o){
29018        this.cursor = o.params ? o.params.start : 0;
29019        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29020
29021        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29022        this.field.dom.value = ap;
29023        this.first.setDisabled(ap == 1);
29024        this.prev.setDisabled(ap == 1);
29025        this.next.setDisabled(ap == ps);
29026        this.last.setDisabled(ap == ps);
29027        this.loading.enable();
29028        this.updateInfo();
29029     },
29030
29031     // private
29032     getPageData : function(){
29033         var total = this.ds.getTotalCount();
29034         return {
29035             total : total,
29036             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29037             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29038         };
29039     },
29040
29041     // private
29042     onLoadError : function(){
29043         this.loading.enable();
29044     },
29045
29046     // private
29047     onPagingKeydown : function(e){
29048         var k = e.getKey();
29049         var d = this.getPageData();
29050         if(k == e.RETURN){
29051             var v = this.field.dom.value, pageNum;
29052             if(!v || isNaN(pageNum = parseInt(v, 10))){
29053                 this.field.dom.value = d.activePage;
29054                 return;
29055             }
29056             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29057             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29058             e.stopEvent();
29059         }
29060         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))
29061         {
29062           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29063           this.field.dom.value = pageNum;
29064           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29065           e.stopEvent();
29066         }
29067         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29068         {
29069           var v = this.field.dom.value, pageNum; 
29070           var increment = (e.shiftKey) ? 10 : 1;
29071           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29072             increment *= -1;
29073           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29074             this.field.dom.value = d.activePage;
29075             return;
29076           }
29077           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29078           {
29079             this.field.dom.value = parseInt(v, 10) + increment;
29080             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29081             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29082           }
29083           e.stopEvent();
29084         }
29085     },
29086
29087     // private
29088     beforeLoad : function(){
29089         if(this.loading){
29090             this.loading.disable();
29091         }
29092     },
29093
29094     // private
29095     onClick : function(which){
29096         var ds = this.ds;
29097         switch(which){
29098             case "first":
29099                 ds.load({params:{start: 0, limit: this.pageSize}});
29100             break;
29101             case "prev":
29102                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29103             break;
29104             case "next":
29105                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29106             break;
29107             case "last":
29108                 var total = ds.getTotalCount();
29109                 var extra = total % this.pageSize;
29110                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29111                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29112             break;
29113             case "refresh":
29114                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29115             break;
29116         }
29117     },
29118
29119     /**
29120      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29121      * @param {Roo.data.Store} store The data store to unbind
29122      */
29123     unbind : function(ds){
29124         ds.un("beforeload", this.beforeLoad, this);
29125         ds.un("load", this.onLoad, this);
29126         ds.un("loadexception", this.onLoadError, this);
29127         ds.un("remove", this.updateInfo, this);
29128         ds.un("add", this.updateInfo, this);
29129         this.ds = undefined;
29130     },
29131
29132     /**
29133      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29134      * @param {Roo.data.Store} store The data store to bind
29135      */
29136     bind : function(ds){
29137         ds.on("beforeload", this.beforeLoad, this);
29138         ds.on("load", this.onLoad, this);
29139         ds.on("loadexception", this.onLoadError, this);
29140         ds.on("remove", this.updateInfo, this);
29141         ds.on("add", this.updateInfo, this);
29142         this.ds = ds;
29143     }
29144 });/*
29145  * Based on:
29146  * Ext JS Library 1.1.1
29147  * Copyright(c) 2006-2007, Ext JS, LLC.
29148  *
29149  * Originally Released Under LGPL - original licence link has changed is not relivant.
29150  *
29151  * Fork - LGPL
29152  * <script type="text/javascript">
29153  */
29154
29155 /**
29156  * @class Roo.Resizable
29157  * @extends Roo.util.Observable
29158  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29159  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29160  * 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
29161  * the element will be wrapped for you automatically.</p>
29162  * <p>Here is the list of valid resize handles:</p>
29163  * <pre>
29164 Value   Description
29165 ------  -------------------
29166  'n'     north
29167  's'     south
29168  'e'     east
29169  'w'     west
29170  'nw'    northwest
29171  'sw'    southwest
29172  'se'    southeast
29173  'ne'    northeast
29174  'hd'    horizontal drag
29175  'all'   all
29176 </pre>
29177  * <p>Here's an example showing the creation of a typical Resizable:</p>
29178  * <pre><code>
29179 var resizer = new Roo.Resizable("element-id", {
29180     handles: 'all',
29181     minWidth: 200,
29182     minHeight: 100,
29183     maxWidth: 500,
29184     maxHeight: 400,
29185     pinned: true
29186 });
29187 resizer.on("resize", myHandler);
29188 </code></pre>
29189  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29190  * resizer.east.setDisplayed(false);</p>
29191  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29192  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29193  * resize operation's new size (defaults to [0, 0])
29194  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29195  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29196  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29197  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29198  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29199  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29200  * @cfg {Number} width The width of the element in pixels (defaults to null)
29201  * @cfg {Number} height The height of the element in pixels (defaults to null)
29202  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29203  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29204  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29205  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29206  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29207  * in favor of the handles config option (defaults to false)
29208  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29209  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29210  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29211  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29212  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29213  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29214  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29215  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29216  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29217  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29218  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29219  * @constructor
29220  * Create a new resizable component
29221  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29222  * @param {Object} config configuration options
29223   */
29224 Roo.Resizable = function(el, config)
29225 {
29226     this.el = Roo.get(el);
29227
29228     if(config && config.wrap){
29229         config.resizeChild = this.el;
29230         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29231         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29232         this.el.setStyle("overflow", "hidden");
29233         this.el.setPositioning(config.resizeChild.getPositioning());
29234         config.resizeChild.clearPositioning();
29235         if(!config.width || !config.height){
29236             var csize = config.resizeChild.getSize();
29237             this.el.setSize(csize.width, csize.height);
29238         }
29239         if(config.pinned && !config.adjustments){
29240             config.adjustments = "auto";
29241         }
29242     }
29243
29244     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29245     this.proxy.unselectable();
29246     this.proxy.enableDisplayMode('block');
29247
29248     Roo.apply(this, config);
29249
29250     if(this.pinned){
29251         this.disableTrackOver = true;
29252         this.el.addClass("x-resizable-pinned");
29253     }
29254     // if the element isn't positioned, make it relative
29255     var position = this.el.getStyle("position");
29256     if(position != "absolute" && position != "fixed"){
29257         this.el.setStyle("position", "relative");
29258     }
29259     if(!this.handles){ // no handles passed, must be legacy style
29260         this.handles = 's,e,se';
29261         if(this.multiDirectional){
29262             this.handles += ',n,w';
29263         }
29264     }
29265     if(this.handles == "all"){
29266         this.handles = "n s e w ne nw se sw";
29267     }
29268     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29269     var ps = Roo.Resizable.positions;
29270     for(var i = 0, len = hs.length; i < len; i++){
29271         if(hs[i] && ps[hs[i]]){
29272             var pos = ps[hs[i]];
29273             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29274         }
29275     }
29276     // legacy
29277     this.corner = this.southeast;
29278     
29279     // updateBox = the box can move..
29280     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29281         this.updateBox = true;
29282     }
29283
29284     this.activeHandle = null;
29285
29286     if(this.resizeChild){
29287         if(typeof this.resizeChild == "boolean"){
29288             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29289         }else{
29290             this.resizeChild = Roo.get(this.resizeChild, true);
29291         }
29292     }
29293     
29294     if(this.adjustments == "auto"){
29295         var rc = this.resizeChild;
29296         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29297         if(rc && (hw || hn)){
29298             rc.position("relative");
29299             rc.setLeft(hw ? hw.el.getWidth() : 0);
29300             rc.setTop(hn ? hn.el.getHeight() : 0);
29301         }
29302         this.adjustments = [
29303             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29304             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29305         ];
29306     }
29307
29308     if(this.draggable){
29309         this.dd = this.dynamic ?
29310             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29311         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29312     }
29313
29314     // public events
29315     this.addEvents({
29316         /**
29317          * @event beforeresize
29318          * Fired before resize is allowed. Set enabled to false to cancel resize.
29319          * @param {Roo.Resizable} this
29320          * @param {Roo.EventObject} e The mousedown event
29321          */
29322         "beforeresize" : true,
29323         /**
29324          * @event resizing
29325          * Fired a resizing.
29326          * @param {Roo.Resizable} this
29327          * @param {Number} x The new x position
29328          * @param {Number} y The new y position
29329          * @param {Number} w The new w width
29330          * @param {Number} h The new h hight
29331          * @param {Roo.EventObject} e The mouseup event
29332          */
29333         "resizing" : true,
29334         /**
29335          * @event resize
29336          * Fired after a resize.
29337          * @param {Roo.Resizable} this
29338          * @param {Number} width The new width
29339          * @param {Number} height The new height
29340          * @param {Roo.EventObject} e The mouseup event
29341          */
29342         "resize" : true
29343     });
29344
29345     if(this.width !== null && this.height !== null){
29346         this.resizeTo(this.width, this.height);
29347     }else{
29348         this.updateChildSize();
29349     }
29350     if(Roo.isIE){
29351         this.el.dom.style.zoom = 1;
29352     }
29353     Roo.Resizable.superclass.constructor.call(this);
29354 };
29355
29356 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29357         resizeChild : false,
29358         adjustments : [0, 0],
29359         minWidth : 5,
29360         minHeight : 5,
29361         maxWidth : 10000,
29362         maxHeight : 10000,
29363         enabled : true,
29364         animate : false,
29365         duration : .35,
29366         dynamic : false,
29367         handles : false,
29368         multiDirectional : false,
29369         disableTrackOver : false,
29370         easing : 'easeOutStrong',
29371         widthIncrement : 0,
29372         heightIncrement : 0,
29373         pinned : false,
29374         width : null,
29375         height : null,
29376         preserveRatio : false,
29377         transparent: false,
29378         minX: 0,
29379         minY: 0,
29380         draggable: false,
29381
29382         /**
29383          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29384          */
29385         constrainTo: undefined,
29386         /**
29387          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29388          */
29389         resizeRegion: undefined,
29390
29391
29392     /**
29393      * Perform a manual resize
29394      * @param {Number} width
29395      * @param {Number} height
29396      */
29397     resizeTo : function(width, height){
29398         this.el.setSize(width, height);
29399         this.updateChildSize();
29400         this.fireEvent("resize", this, width, height, null);
29401     },
29402
29403     // private
29404     startSizing : function(e, handle){
29405         this.fireEvent("beforeresize", this, e);
29406         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29407
29408             if(!this.overlay){
29409                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29410                 this.overlay.unselectable();
29411                 this.overlay.enableDisplayMode("block");
29412                 this.overlay.on("mousemove", this.onMouseMove, this);
29413                 this.overlay.on("mouseup", this.onMouseUp, this);
29414             }
29415             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29416
29417             this.resizing = true;
29418             this.startBox = this.el.getBox();
29419             this.startPoint = e.getXY();
29420             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29421                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29422
29423             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29424             this.overlay.show();
29425
29426             if(this.constrainTo) {
29427                 var ct = Roo.get(this.constrainTo);
29428                 this.resizeRegion = ct.getRegion().adjust(
29429                     ct.getFrameWidth('t'),
29430                     ct.getFrameWidth('l'),
29431                     -ct.getFrameWidth('b'),
29432                     -ct.getFrameWidth('r')
29433                 );
29434             }
29435
29436             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29437             this.proxy.show();
29438             this.proxy.setBox(this.startBox);
29439             if(!this.dynamic){
29440                 this.proxy.setStyle('visibility', 'visible');
29441             }
29442         }
29443     },
29444
29445     // private
29446     onMouseDown : function(handle, e){
29447         if(this.enabled){
29448             e.stopEvent();
29449             this.activeHandle = handle;
29450             this.startSizing(e, handle);
29451         }
29452     },
29453
29454     // private
29455     onMouseUp : function(e){
29456         var size = this.resizeElement();
29457         this.resizing = false;
29458         this.handleOut();
29459         this.overlay.hide();
29460         this.proxy.hide();
29461         this.fireEvent("resize", this, size.width, size.height, e);
29462     },
29463
29464     // private
29465     updateChildSize : function(){
29466         
29467         if(this.resizeChild){
29468             var el = this.el;
29469             var child = this.resizeChild;
29470             var adj = this.adjustments;
29471             if(el.dom.offsetWidth){
29472                 var b = el.getSize(true);
29473                 child.setSize(b.width+adj[0], b.height+adj[1]);
29474             }
29475             // Second call here for IE
29476             // The first call enables instant resizing and
29477             // the second call corrects scroll bars if they
29478             // exist
29479             if(Roo.isIE){
29480                 setTimeout(function(){
29481                     if(el.dom.offsetWidth){
29482                         var b = el.getSize(true);
29483                         child.setSize(b.width+adj[0], b.height+adj[1]);
29484                     }
29485                 }, 10);
29486             }
29487         }
29488     },
29489
29490     // private
29491     snap : function(value, inc, min){
29492         if(!inc || !value) return value;
29493         var newValue = value;
29494         var m = value % inc;
29495         if(m > 0){
29496             if(m > (inc/2)){
29497                 newValue = value + (inc-m);
29498             }else{
29499                 newValue = value - m;
29500             }
29501         }
29502         return Math.max(min, newValue);
29503     },
29504
29505     // private
29506     resizeElement : function(){
29507         var box = this.proxy.getBox();
29508         if(this.updateBox){
29509             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29510         }else{
29511             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29512         }
29513         this.updateChildSize();
29514         if(!this.dynamic){
29515             this.proxy.hide();
29516         }
29517         return box;
29518     },
29519
29520     // private
29521     constrain : function(v, diff, m, mx){
29522         if(v - diff < m){
29523             diff = v - m;
29524         }else if(v - diff > mx){
29525             diff = mx - v;
29526         }
29527         return diff;
29528     },
29529
29530     // private
29531     onMouseMove : function(e){
29532         
29533         if(this.enabled){
29534             try{// try catch so if something goes wrong the user doesn't get hung
29535
29536             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29537                 return;
29538             }
29539
29540             //var curXY = this.startPoint;
29541             var curSize = this.curSize || this.startBox;
29542             var x = this.startBox.x, y = this.startBox.y;
29543             var ox = x, oy = y;
29544             var w = curSize.width, h = curSize.height;
29545             var ow = w, oh = h;
29546             var mw = this.minWidth, mh = this.minHeight;
29547             var mxw = this.maxWidth, mxh = this.maxHeight;
29548             var wi = this.widthIncrement;
29549             var hi = this.heightIncrement;
29550
29551             var eventXY = e.getXY();
29552             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29553             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29554
29555             var pos = this.activeHandle.position;
29556
29557             switch(pos){
29558                 case "east":
29559                     w += diffX;
29560                     w = Math.min(Math.max(mw, w), mxw);
29561                     break;
29562              
29563                 case "south":
29564                     h += diffY;
29565                     h = Math.min(Math.max(mh, h), mxh);
29566                     break;
29567                 case "southeast":
29568                     w += diffX;
29569                     h += diffY;
29570                     w = Math.min(Math.max(mw, w), mxw);
29571                     h = Math.min(Math.max(mh, h), mxh);
29572                     break;
29573                 case "north":
29574                     diffY = this.constrain(h, diffY, mh, mxh);
29575                     y += diffY;
29576                     h -= diffY;
29577                     break;
29578                 case "hdrag":
29579                     
29580                     if (wi) {
29581                         var adiffX = Math.abs(diffX);
29582                         var sub = (adiffX % wi); // how much 
29583                         if (sub > (wi/2)) { // far enough to snap
29584                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29585                         } else {
29586                             // remove difference.. 
29587                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29588                         }
29589                     }
29590                     x += diffX;
29591                     x = Math.max(this.minX, x);
29592                     break;
29593                 case "west":
29594                     diffX = this.constrain(w, diffX, mw, mxw);
29595                     x += diffX;
29596                     w -= diffX;
29597                     break;
29598                 case "northeast":
29599                     w += diffX;
29600                     w = Math.min(Math.max(mw, w), mxw);
29601                     diffY = this.constrain(h, diffY, mh, mxh);
29602                     y += diffY;
29603                     h -= diffY;
29604                     break;
29605                 case "northwest":
29606                     diffX = this.constrain(w, diffX, mw, mxw);
29607                     diffY = this.constrain(h, diffY, mh, mxh);
29608                     y += diffY;
29609                     h -= diffY;
29610                     x += diffX;
29611                     w -= diffX;
29612                     break;
29613                case "southwest":
29614                     diffX = this.constrain(w, diffX, mw, mxw);
29615                     h += diffY;
29616                     h = Math.min(Math.max(mh, h), mxh);
29617                     x += diffX;
29618                     w -= diffX;
29619                     break;
29620             }
29621
29622             var sw = this.snap(w, wi, mw);
29623             var sh = this.snap(h, hi, mh);
29624             if(sw != w || sh != h){
29625                 switch(pos){
29626                     case "northeast":
29627                         y -= sh - h;
29628                     break;
29629                     case "north":
29630                         y -= sh - h;
29631                         break;
29632                     case "southwest":
29633                         x -= sw - w;
29634                     break;
29635                     case "west":
29636                         x -= sw - w;
29637                         break;
29638                     case "northwest":
29639                         x -= sw - w;
29640                         y -= sh - h;
29641                     break;
29642                 }
29643                 w = sw;
29644                 h = sh;
29645             }
29646
29647             if(this.preserveRatio){
29648                 switch(pos){
29649                     case "southeast":
29650                     case "east":
29651                         h = oh * (w/ow);
29652                         h = Math.min(Math.max(mh, h), mxh);
29653                         w = ow * (h/oh);
29654                        break;
29655                     case "south":
29656                         w = ow * (h/oh);
29657                         w = Math.min(Math.max(mw, w), mxw);
29658                         h = oh * (w/ow);
29659                         break;
29660                     case "northeast":
29661                         w = ow * (h/oh);
29662                         w = Math.min(Math.max(mw, w), mxw);
29663                         h = oh * (w/ow);
29664                     break;
29665                     case "north":
29666                         var tw = w;
29667                         w = ow * (h/oh);
29668                         w = Math.min(Math.max(mw, w), mxw);
29669                         h = oh * (w/ow);
29670                         x += (tw - w) / 2;
29671                         break;
29672                     case "southwest":
29673                         h = oh * (w/ow);
29674                         h = Math.min(Math.max(mh, h), mxh);
29675                         var tw = w;
29676                         w = ow * (h/oh);
29677                         x += tw - w;
29678                         break;
29679                     case "west":
29680                         var th = h;
29681                         h = oh * (w/ow);
29682                         h = Math.min(Math.max(mh, h), mxh);
29683                         y += (th - h) / 2;
29684                         var tw = w;
29685                         w = ow * (h/oh);
29686                         x += tw - w;
29687                        break;
29688                     case "northwest":
29689                         var tw = w;
29690                         var th = h;
29691                         h = oh * (w/ow);
29692                         h = Math.min(Math.max(mh, h), mxh);
29693                         w = ow * (h/oh);
29694                         y += th - h;
29695                         x += tw - w;
29696                        break;
29697
29698                 }
29699             }
29700             if (pos == 'hdrag') {
29701                 w = ow;
29702             }
29703             this.proxy.setBounds(x, y, w, h);
29704             if(this.dynamic){
29705                 this.resizeElement();
29706             }
29707             }catch(e){}
29708         }
29709         this.fireEvent("resizing", this, x, y, w, h, e);
29710     },
29711
29712     // private
29713     handleOver : function(){
29714         if(this.enabled){
29715             this.el.addClass("x-resizable-over");
29716         }
29717     },
29718
29719     // private
29720     handleOut : function(){
29721         if(!this.resizing){
29722             this.el.removeClass("x-resizable-over");
29723         }
29724     },
29725
29726     /**
29727      * Returns the element this component is bound to.
29728      * @return {Roo.Element}
29729      */
29730     getEl : function(){
29731         return this.el;
29732     },
29733
29734     /**
29735      * Returns the resizeChild element (or null).
29736      * @return {Roo.Element}
29737      */
29738     getResizeChild : function(){
29739         return this.resizeChild;
29740     },
29741     groupHandler : function()
29742     {
29743         
29744     },
29745     /**
29746      * Destroys this resizable. If the element was wrapped and
29747      * removeEl is not true then the element remains.
29748      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29749      */
29750     destroy : function(removeEl){
29751         this.proxy.remove();
29752         if(this.overlay){
29753             this.overlay.removeAllListeners();
29754             this.overlay.remove();
29755         }
29756         var ps = Roo.Resizable.positions;
29757         for(var k in ps){
29758             if(typeof ps[k] != "function" && this[ps[k]]){
29759                 var h = this[ps[k]];
29760                 h.el.removeAllListeners();
29761                 h.el.remove();
29762             }
29763         }
29764         if(removeEl){
29765             this.el.update("");
29766             this.el.remove();
29767         }
29768     }
29769 });
29770
29771 // private
29772 // hash to map config positions to true positions
29773 Roo.Resizable.positions = {
29774     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29775     hd: "hdrag"
29776 };
29777
29778 // private
29779 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29780     if(!this.tpl){
29781         // only initialize the template if resizable is used
29782         var tpl = Roo.DomHelper.createTemplate(
29783             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29784         );
29785         tpl.compile();
29786         Roo.Resizable.Handle.prototype.tpl = tpl;
29787     }
29788     this.position = pos;
29789     this.rz = rz;
29790     // show north drag fro topdra
29791     var handlepos = pos == 'hdrag' ? 'north' : pos;
29792     
29793     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29794     if (pos == 'hdrag') {
29795         this.el.setStyle('cursor', 'pointer');
29796     }
29797     this.el.unselectable();
29798     if(transparent){
29799         this.el.setOpacity(0);
29800     }
29801     this.el.on("mousedown", this.onMouseDown, this);
29802     if(!disableTrackOver){
29803         this.el.on("mouseover", this.onMouseOver, this);
29804         this.el.on("mouseout", this.onMouseOut, this);
29805     }
29806 };
29807
29808 // private
29809 Roo.Resizable.Handle.prototype = {
29810     afterResize : function(rz){
29811         Roo.log('after?');
29812         // do nothing
29813     },
29814     // private
29815     onMouseDown : function(e){
29816         this.rz.onMouseDown(this, e);
29817     },
29818     // private
29819     onMouseOver : function(e){
29820         this.rz.handleOver(this, e);
29821     },
29822     // private
29823     onMouseOut : function(e){
29824         this.rz.handleOut(this, e);
29825     }
29826 };/*
29827  * Based on:
29828  * Ext JS Library 1.1.1
29829  * Copyright(c) 2006-2007, Ext JS, LLC.
29830  *
29831  * Originally Released Under LGPL - original licence link has changed is not relivant.
29832  *
29833  * Fork - LGPL
29834  * <script type="text/javascript">
29835  */
29836
29837 /**
29838  * @class Roo.Editor
29839  * @extends Roo.Component
29840  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29841  * @constructor
29842  * Create a new Editor
29843  * @param {Roo.form.Field} field The Field object (or descendant)
29844  * @param {Object} config The config object
29845  */
29846 Roo.Editor = function(field, config){
29847     Roo.Editor.superclass.constructor.call(this, config);
29848     this.field = field;
29849     this.addEvents({
29850         /**
29851              * @event beforestartedit
29852              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29853              * false from the handler of this event.
29854              * @param {Editor} this
29855              * @param {Roo.Element} boundEl The underlying element bound to this editor
29856              * @param {Mixed} value The field value being set
29857              */
29858         "beforestartedit" : true,
29859         /**
29860              * @event startedit
29861              * Fires when this editor is displayed
29862              * @param {Roo.Element} boundEl The underlying element bound to this editor
29863              * @param {Mixed} value The starting field value
29864              */
29865         "startedit" : true,
29866         /**
29867              * @event beforecomplete
29868              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29869              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29870              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29871              * event will not fire since no edit actually occurred.
29872              * @param {Editor} this
29873              * @param {Mixed} value The current field value
29874              * @param {Mixed} startValue The original field value
29875              */
29876         "beforecomplete" : true,
29877         /**
29878              * @event complete
29879              * Fires after editing is complete and any changed value has been written to the underlying field.
29880              * @param {Editor} this
29881              * @param {Mixed} value The current field value
29882              * @param {Mixed} startValue The original field value
29883              */
29884         "complete" : true,
29885         /**
29886          * @event specialkey
29887          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29888          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29889          * @param {Roo.form.Field} this
29890          * @param {Roo.EventObject} e The event object
29891          */
29892         "specialkey" : true
29893     });
29894 };
29895
29896 Roo.extend(Roo.Editor, Roo.Component, {
29897     /**
29898      * @cfg {Boolean/String} autosize
29899      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29900      * or "height" to adopt the height only (defaults to false)
29901      */
29902     /**
29903      * @cfg {Boolean} revertInvalid
29904      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29905      * validation fails (defaults to true)
29906      */
29907     /**
29908      * @cfg {Boolean} ignoreNoChange
29909      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29910      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29911      * will never be ignored.
29912      */
29913     /**
29914      * @cfg {Boolean} hideEl
29915      * False to keep the bound element visible while the editor is displayed (defaults to true)
29916      */
29917     /**
29918      * @cfg {Mixed} value
29919      * The data value of the underlying field (defaults to "")
29920      */
29921     value : "",
29922     /**
29923      * @cfg {String} alignment
29924      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29925      */
29926     alignment: "c-c?",
29927     /**
29928      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29929      * for bottom-right shadow (defaults to "frame")
29930      */
29931     shadow : "frame",
29932     /**
29933      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29934      */
29935     constrain : false,
29936     /**
29937      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29938      */
29939     completeOnEnter : false,
29940     /**
29941      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29942      */
29943     cancelOnEsc : false,
29944     /**
29945      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29946      */
29947     updateEl : false,
29948
29949     // private
29950     onRender : function(ct, position){
29951         this.el = new Roo.Layer({
29952             shadow: this.shadow,
29953             cls: "x-editor",
29954             parentEl : ct,
29955             shim : this.shim,
29956             shadowOffset:4,
29957             id: this.id,
29958             constrain: this.constrain
29959         });
29960         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29961         if(this.field.msgTarget != 'title'){
29962             this.field.msgTarget = 'qtip';
29963         }
29964         this.field.render(this.el);
29965         if(Roo.isGecko){
29966             this.field.el.dom.setAttribute('autocomplete', 'off');
29967         }
29968         this.field.on("specialkey", this.onSpecialKey, this);
29969         if(this.swallowKeys){
29970             this.field.el.swallowEvent(['keydown','keypress']);
29971         }
29972         this.field.show();
29973         this.field.on("blur", this.onBlur, this);
29974         if(this.field.grow){
29975             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29976         }
29977     },
29978
29979     onSpecialKey : function(field, e)
29980     {
29981         //Roo.log('editor onSpecialKey');
29982         if(this.completeOnEnter && e.getKey() == e.ENTER){
29983             e.stopEvent();
29984             this.completeEdit();
29985             return;
29986         }
29987         // do not fire special key otherwise it might hide close the editor...
29988         if(e.getKey() == e.ENTER){    
29989             return;
29990         }
29991         if(this.cancelOnEsc && e.getKey() == e.ESC){
29992             this.cancelEdit();
29993             return;
29994         } 
29995         this.fireEvent('specialkey', field, e);
29996     
29997     },
29998
29999     /**
30000      * Starts the editing process and shows the editor.
30001      * @param {String/HTMLElement/Element} el The element to edit
30002      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30003       * to the innerHTML of el.
30004      */
30005     startEdit : function(el, value){
30006         if(this.editing){
30007             this.completeEdit();
30008         }
30009         this.boundEl = Roo.get(el);
30010         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30011         if(!this.rendered){
30012             this.render(this.parentEl || document.body);
30013         }
30014         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30015             return;
30016         }
30017         this.startValue = v;
30018         this.field.setValue(v);
30019         if(this.autoSize){
30020             var sz = this.boundEl.getSize();
30021             switch(this.autoSize){
30022                 case "width":
30023                 this.setSize(sz.width,  "");
30024                 break;
30025                 case "height":
30026                 this.setSize("",  sz.height);
30027                 break;
30028                 default:
30029                 this.setSize(sz.width,  sz.height);
30030             }
30031         }
30032         this.el.alignTo(this.boundEl, this.alignment);
30033         this.editing = true;
30034         if(Roo.QuickTips){
30035             Roo.QuickTips.disable();
30036         }
30037         this.show();
30038     },
30039
30040     /**
30041      * Sets the height and width of this editor.
30042      * @param {Number} width The new width
30043      * @param {Number} height The new height
30044      */
30045     setSize : function(w, h){
30046         this.field.setSize(w, h);
30047         if(this.el){
30048             this.el.sync();
30049         }
30050     },
30051
30052     /**
30053      * Realigns the editor to the bound field based on the current alignment config value.
30054      */
30055     realign : function(){
30056         this.el.alignTo(this.boundEl, this.alignment);
30057     },
30058
30059     /**
30060      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30061      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30062      */
30063     completeEdit : function(remainVisible){
30064         if(!this.editing){
30065             return;
30066         }
30067         var v = this.getValue();
30068         if(this.revertInvalid !== false && !this.field.isValid()){
30069             v = this.startValue;
30070             this.cancelEdit(true);
30071         }
30072         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30073             this.editing = false;
30074             this.hide();
30075             return;
30076         }
30077         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30078             this.editing = false;
30079             if(this.updateEl && this.boundEl){
30080                 this.boundEl.update(v);
30081             }
30082             if(remainVisible !== true){
30083                 this.hide();
30084             }
30085             this.fireEvent("complete", this, v, this.startValue);
30086         }
30087     },
30088
30089     // private
30090     onShow : function(){
30091         this.el.show();
30092         if(this.hideEl !== false){
30093             this.boundEl.hide();
30094         }
30095         this.field.show();
30096         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30097             this.fixIEFocus = true;
30098             this.deferredFocus.defer(50, this);
30099         }else{
30100             this.field.focus();
30101         }
30102         this.fireEvent("startedit", this.boundEl, this.startValue);
30103     },
30104
30105     deferredFocus : function(){
30106         if(this.editing){
30107             this.field.focus();
30108         }
30109     },
30110
30111     /**
30112      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30113      * reverted to the original starting value.
30114      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30115      * cancel (defaults to false)
30116      */
30117     cancelEdit : function(remainVisible){
30118         if(this.editing){
30119             this.setValue(this.startValue);
30120             if(remainVisible !== true){
30121                 this.hide();
30122             }
30123         }
30124     },
30125
30126     // private
30127     onBlur : function(){
30128         if(this.allowBlur !== true && this.editing){
30129             this.completeEdit();
30130         }
30131     },
30132
30133     // private
30134     onHide : function(){
30135         if(this.editing){
30136             this.completeEdit();
30137             return;
30138         }
30139         this.field.blur();
30140         if(this.field.collapse){
30141             this.field.collapse();
30142         }
30143         this.el.hide();
30144         if(this.hideEl !== false){
30145             this.boundEl.show();
30146         }
30147         if(Roo.QuickTips){
30148             Roo.QuickTips.enable();
30149         }
30150     },
30151
30152     /**
30153      * Sets the data value of the editor
30154      * @param {Mixed} value Any valid value supported by the underlying field
30155      */
30156     setValue : function(v){
30157         this.field.setValue(v);
30158     },
30159
30160     /**
30161      * Gets the data value of the editor
30162      * @return {Mixed} The data value
30163      */
30164     getValue : function(){
30165         return this.field.getValue();
30166     }
30167 });/*
30168  * Based on:
30169  * Ext JS Library 1.1.1
30170  * Copyright(c) 2006-2007, Ext JS, LLC.
30171  *
30172  * Originally Released Under LGPL - original licence link has changed is not relivant.
30173  *
30174  * Fork - LGPL
30175  * <script type="text/javascript">
30176  */
30177  
30178 /**
30179  * @class Roo.BasicDialog
30180  * @extends Roo.util.Observable
30181  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30182  * <pre><code>
30183 var dlg = new Roo.BasicDialog("my-dlg", {
30184     height: 200,
30185     width: 300,
30186     minHeight: 100,
30187     minWidth: 150,
30188     modal: true,
30189     proxyDrag: true,
30190     shadow: true
30191 });
30192 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30193 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30194 dlg.addButton('Cancel', dlg.hide, dlg);
30195 dlg.show();
30196 </code></pre>
30197   <b>A Dialog should always be a direct child of the body element.</b>
30198  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30199  * @cfg {String} title Default text to display in the title bar (defaults to null)
30200  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30201  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30202  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30203  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30204  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30205  * (defaults to null with no animation)
30206  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30207  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30208  * property for valid values (defaults to 'all')
30209  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30210  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30211  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30212  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30213  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30214  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30215  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30216  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30217  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30218  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30219  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30220  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30221  * draggable = true (defaults to false)
30222  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30223  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30224  * shadow (defaults to false)
30225  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30226  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30227  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30228  * @cfg {Array} buttons Array of buttons
30229  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30230  * @constructor
30231  * Create a new BasicDialog.
30232  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30233  * @param {Object} config Configuration options
30234  */
30235 Roo.BasicDialog = function(el, config){
30236     this.el = Roo.get(el);
30237     var dh = Roo.DomHelper;
30238     if(!this.el && config && config.autoCreate){
30239         if(typeof config.autoCreate == "object"){
30240             if(!config.autoCreate.id){
30241                 config.autoCreate.id = el;
30242             }
30243             this.el = dh.append(document.body,
30244                         config.autoCreate, true);
30245         }else{
30246             this.el = dh.append(document.body,
30247                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30248         }
30249     }
30250     el = this.el;
30251     el.setDisplayed(true);
30252     el.hide = this.hideAction;
30253     this.id = el.id;
30254     el.addClass("x-dlg");
30255
30256     Roo.apply(this, config);
30257
30258     this.proxy = el.createProxy("x-dlg-proxy");
30259     this.proxy.hide = this.hideAction;
30260     this.proxy.setOpacity(.5);
30261     this.proxy.hide();
30262
30263     if(config.width){
30264         el.setWidth(config.width);
30265     }
30266     if(config.height){
30267         el.setHeight(config.height);
30268     }
30269     this.size = el.getSize();
30270     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30271         this.xy = [config.x,config.y];
30272     }else{
30273         this.xy = el.getCenterXY(true);
30274     }
30275     /** The header element @type Roo.Element */
30276     this.header = el.child("> .x-dlg-hd");
30277     /** The body element @type Roo.Element */
30278     this.body = el.child("> .x-dlg-bd");
30279     /** The footer element @type Roo.Element */
30280     this.footer = el.child("> .x-dlg-ft");
30281
30282     if(!this.header){
30283         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30284     }
30285     if(!this.body){
30286         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30287     }
30288
30289     this.header.unselectable();
30290     if(this.title){
30291         this.header.update(this.title);
30292     }
30293     // this element allows the dialog to be focused for keyboard event
30294     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30295     this.focusEl.swallowEvent("click", true);
30296
30297     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30298
30299     // wrap the body and footer for special rendering
30300     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30301     if(this.footer){
30302         this.bwrap.dom.appendChild(this.footer.dom);
30303     }
30304
30305     this.bg = this.el.createChild({
30306         tag: "div", cls:"x-dlg-bg",
30307         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30308     });
30309     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30310
30311
30312     if(this.autoScroll !== false && !this.autoTabs){
30313         this.body.setStyle("overflow", "auto");
30314     }
30315
30316     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30317
30318     if(this.closable !== false){
30319         this.el.addClass("x-dlg-closable");
30320         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30321         this.close.on("click", this.closeClick, this);
30322         this.close.addClassOnOver("x-dlg-close-over");
30323     }
30324     if(this.collapsible !== false){
30325         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30326         this.collapseBtn.on("click", this.collapseClick, this);
30327         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30328         this.header.on("dblclick", this.collapseClick, this);
30329     }
30330     if(this.resizable !== false){
30331         this.el.addClass("x-dlg-resizable");
30332         this.resizer = new Roo.Resizable(el, {
30333             minWidth: this.minWidth || 80,
30334             minHeight:this.minHeight || 80,
30335             handles: this.resizeHandles || "all",
30336             pinned: true
30337         });
30338         this.resizer.on("beforeresize", this.beforeResize, this);
30339         this.resizer.on("resize", this.onResize, this);
30340     }
30341     if(this.draggable !== false){
30342         el.addClass("x-dlg-draggable");
30343         if (!this.proxyDrag) {
30344             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30345         }
30346         else {
30347             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30348         }
30349         dd.setHandleElId(this.header.id);
30350         dd.endDrag = this.endMove.createDelegate(this);
30351         dd.startDrag = this.startMove.createDelegate(this);
30352         dd.onDrag = this.onDrag.createDelegate(this);
30353         dd.scroll = false;
30354         this.dd = dd;
30355     }
30356     if(this.modal){
30357         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30358         this.mask.enableDisplayMode("block");
30359         this.mask.hide();
30360         this.el.addClass("x-dlg-modal");
30361     }
30362     if(this.shadow){
30363         this.shadow = new Roo.Shadow({
30364             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30365             offset : this.shadowOffset
30366         });
30367     }else{
30368         this.shadowOffset = 0;
30369     }
30370     if(Roo.useShims && this.shim !== false){
30371         this.shim = this.el.createShim();
30372         this.shim.hide = this.hideAction;
30373         this.shim.hide();
30374     }else{
30375         this.shim = false;
30376     }
30377     if(this.autoTabs){
30378         this.initTabs();
30379     }
30380     if (this.buttons) { 
30381         var bts= this.buttons;
30382         this.buttons = [];
30383         Roo.each(bts, function(b) {
30384             this.addButton(b);
30385         }, this);
30386     }
30387     
30388     
30389     this.addEvents({
30390         /**
30391          * @event keydown
30392          * Fires when a key is pressed
30393          * @param {Roo.BasicDialog} this
30394          * @param {Roo.EventObject} e
30395          */
30396         "keydown" : true,
30397         /**
30398          * @event move
30399          * Fires when this dialog is moved by the user.
30400          * @param {Roo.BasicDialog} this
30401          * @param {Number} x The new page X
30402          * @param {Number} y The new page Y
30403          */
30404         "move" : true,
30405         /**
30406          * @event resize
30407          * Fires when this dialog is resized by the user.
30408          * @param {Roo.BasicDialog} this
30409          * @param {Number} width The new width
30410          * @param {Number} height The new height
30411          */
30412         "resize" : true,
30413         /**
30414          * @event beforehide
30415          * Fires before this dialog is hidden.
30416          * @param {Roo.BasicDialog} this
30417          */
30418         "beforehide" : true,
30419         /**
30420          * @event hide
30421          * Fires when this dialog is hidden.
30422          * @param {Roo.BasicDialog} this
30423          */
30424         "hide" : true,
30425         /**
30426          * @event beforeshow
30427          * Fires before this dialog is shown.
30428          * @param {Roo.BasicDialog} this
30429          */
30430         "beforeshow" : true,
30431         /**
30432          * @event show
30433          * Fires when this dialog is shown.
30434          * @param {Roo.BasicDialog} this
30435          */
30436         "show" : true
30437     });
30438     el.on("keydown", this.onKeyDown, this);
30439     el.on("mousedown", this.toFront, this);
30440     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30441     this.el.hide();
30442     Roo.DialogManager.register(this);
30443     Roo.BasicDialog.superclass.constructor.call(this);
30444 };
30445
30446 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30447     shadowOffset: Roo.isIE ? 6 : 5,
30448     minHeight: 80,
30449     minWidth: 200,
30450     minButtonWidth: 75,
30451     defaultButton: null,
30452     buttonAlign: "right",
30453     tabTag: 'div',
30454     firstShow: true,
30455
30456     /**
30457      * Sets the dialog title text
30458      * @param {String} text The title text to display
30459      * @return {Roo.BasicDialog} this
30460      */
30461     setTitle : function(text){
30462         this.header.update(text);
30463         return this;
30464     },
30465
30466     // private
30467     closeClick : function(){
30468         this.hide();
30469     },
30470
30471     // private
30472     collapseClick : function(){
30473         this[this.collapsed ? "expand" : "collapse"]();
30474     },
30475
30476     /**
30477      * Collapses the dialog to its minimized state (only the title bar is visible).
30478      * Equivalent to the user clicking the collapse dialog button.
30479      */
30480     collapse : function(){
30481         if(!this.collapsed){
30482             this.collapsed = true;
30483             this.el.addClass("x-dlg-collapsed");
30484             this.restoreHeight = this.el.getHeight();
30485             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30486         }
30487     },
30488
30489     /**
30490      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30491      * clicking the expand dialog button.
30492      */
30493     expand : function(){
30494         if(this.collapsed){
30495             this.collapsed = false;
30496             this.el.removeClass("x-dlg-collapsed");
30497             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30498         }
30499     },
30500
30501     /**
30502      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30503      * @return {Roo.TabPanel} The tabs component
30504      */
30505     initTabs : function(){
30506         var tabs = this.getTabs();
30507         while(tabs.getTab(0)){
30508             tabs.removeTab(0);
30509         }
30510         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30511             var dom = el.dom;
30512             tabs.addTab(Roo.id(dom), dom.title);
30513             dom.title = "";
30514         });
30515         tabs.activate(0);
30516         return tabs;
30517     },
30518
30519     // private
30520     beforeResize : function(){
30521         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30522     },
30523
30524     // private
30525     onResize : function(){
30526         this.refreshSize();
30527         this.syncBodyHeight();
30528         this.adjustAssets();
30529         this.focus();
30530         this.fireEvent("resize", this, this.size.width, this.size.height);
30531     },
30532
30533     // private
30534     onKeyDown : function(e){
30535         if(this.isVisible()){
30536             this.fireEvent("keydown", this, e);
30537         }
30538     },
30539
30540     /**
30541      * Resizes the dialog.
30542      * @param {Number} width
30543      * @param {Number} height
30544      * @return {Roo.BasicDialog} this
30545      */
30546     resizeTo : function(width, height){
30547         this.el.setSize(width, height);
30548         this.size = {width: width, height: height};
30549         this.syncBodyHeight();
30550         if(this.fixedcenter){
30551             this.center();
30552         }
30553         if(this.isVisible()){
30554             this.constrainXY();
30555             this.adjustAssets();
30556         }
30557         this.fireEvent("resize", this, width, height);
30558         return this;
30559     },
30560
30561
30562     /**
30563      * Resizes the dialog to fit the specified content size.
30564      * @param {Number} width
30565      * @param {Number} height
30566      * @return {Roo.BasicDialog} this
30567      */
30568     setContentSize : function(w, h){
30569         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30570         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30571         //if(!this.el.isBorderBox()){
30572             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30573             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30574         //}
30575         if(this.tabs){
30576             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30577             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30578         }
30579         this.resizeTo(w, h);
30580         return this;
30581     },
30582
30583     /**
30584      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30585      * executed in response to a particular key being pressed while the dialog is active.
30586      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30587      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30588      * @param {Function} fn The function to call
30589      * @param {Object} scope (optional) The scope of the function
30590      * @return {Roo.BasicDialog} this
30591      */
30592     addKeyListener : function(key, fn, scope){
30593         var keyCode, shift, ctrl, alt;
30594         if(typeof key == "object" && !(key instanceof Array)){
30595             keyCode = key["key"];
30596             shift = key["shift"];
30597             ctrl = key["ctrl"];
30598             alt = key["alt"];
30599         }else{
30600             keyCode = key;
30601         }
30602         var handler = function(dlg, e){
30603             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30604                 var k = e.getKey();
30605                 if(keyCode instanceof Array){
30606                     for(var i = 0, len = keyCode.length; i < len; i++){
30607                         if(keyCode[i] == k){
30608                           fn.call(scope || window, dlg, k, e);
30609                           return;
30610                         }
30611                     }
30612                 }else{
30613                     if(k == keyCode){
30614                         fn.call(scope || window, dlg, k, e);
30615                     }
30616                 }
30617             }
30618         };
30619         this.on("keydown", handler);
30620         return this;
30621     },
30622
30623     /**
30624      * Returns the TabPanel component (creates it if it doesn't exist).
30625      * Note: If you wish to simply check for the existence of tabs without creating them,
30626      * check for a null 'tabs' property.
30627      * @return {Roo.TabPanel} The tabs component
30628      */
30629     getTabs : function(){
30630         if(!this.tabs){
30631             this.el.addClass("x-dlg-auto-tabs");
30632             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30633             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30634         }
30635         return this.tabs;
30636     },
30637
30638     /**
30639      * Adds a button to the footer section of the dialog.
30640      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30641      * object or a valid Roo.DomHelper element config
30642      * @param {Function} handler The function called when the button is clicked
30643      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30644      * @return {Roo.Button} The new button
30645      */
30646     addButton : function(config, handler, scope){
30647         var dh = Roo.DomHelper;
30648         if(!this.footer){
30649             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30650         }
30651         if(!this.btnContainer){
30652             var tb = this.footer.createChild({
30653
30654                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30655                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30656             }, null, true);
30657             this.btnContainer = tb.firstChild.firstChild.firstChild;
30658         }
30659         var bconfig = {
30660             handler: handler,
30661             scope: scope,
30662             minWidth: this.minButtonWidth,
30663             hideParent:true
30664         };
30665         if(typeof config == "string"){
30666             bconfig.text = config;
30667         }else{
30668             if(config.tag){
30669                 bconfig.dhconfig = config;
30670             }else{
30671                 Roo.apply(bconfig, config);
30672             }
30673         }
30674         var fc = false;
30675         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30676             bconfig.position = Math.max(0, bconfig.position);
30677             fc = this.btnContainer.childNodes[bconfig.position];
30678         }
30679          
30680         var btn = new Roo.Button(
30681             fc ? 
30682                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30683                 : this.btnContainer.appendChild(document.createElement("td")),
30684             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30685             bconfig
30686         );
30687         this.syncBodyHeight();
30688         if(!this.buttons){
30689             /**
30690              * Array of all the buttons that have been added to this dialog via addButton
30691              * @type Array
30692              */
30693             this.buttons = [];
30694         }
30695         this.buttons.push(btn);
30696         return btn;
30697     },
30698
30699     /**
30700      * Sets the default button to be focused when the dialog is displayed.
30701      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30702      * @return {Roo.BasicDialog} this
30703      */
30704     setDefaultButton : function(btn){
30705         this.defaultButton = btn;
30706         return this;
30707     },
30708
30709     // private
30710     getHeaderFooterHeight : function(safe){
30711         var height = 0;
30712         if(this.header){
30713            height += this.header.getHeight();
30714         }
30715         if(this.footer){
30716            var fm = this.footer.getMargins();
30717             height += (this.footer.getHeight()+fm.top+fm.bottom);
30718         }
30719         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30720         height += this.centerBg.getPadding("tb");
30721         return height;
30722     },
30723
30724     // private
30725     syncBodyHeight : function()
30726     {
30727         var bd = this.body, // the text
30728             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30729             bw = this.bwrap;
30730         var height = this.size.height - this.getHeaderFooterHeight(false);
30731         bd.setHeight(height-bd.getMargins("tb"));
30732         var hh = this.header.getHeight();
30733         var h = this.size.height-hh;
30734         cb.setHeight(h);
30735         
30736         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30737         bw.setHeight(h-cb.getPadding("tb"));
30738         
30739         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30740         bd.setWidth(bw.getWidth(true));
30741         if(this.tabs){
30742             this.tabs.syncHeight();
30743             if(Roo.isIE){
30744                 this.tabs.el.repaint();
30745             }
30746         }
30747     },
30748
30749     /**
30750      * Restores the previous state of the dialog if Roo.state is configured.
30751      * @return {Roo.BasicDialog} this
30752      */
30753     restoreState : function(){
30754         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30755         if(box && box.width){
30756             this.xy = [box.x, box.y];
30757             this.resizeTo(box.width, box.height);
30758         }
30759         return this;
30760     },
30761
30762     // private
30763     beforeShow : function(){
30764         this.expand();
30765         if(this.fixedcenter){
30766             this.xy = this.el.getCenterXY(true);
30767         }
30768         if(this.modal){
30769             Roo.get(document.body).addClass("x-body-masked");
30770             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30771             this.mask.show();
30772         }
30773         this.constrainXY();
30774     },
30775
30776     // private
30777     animShow : function(){
30778         var b = Roo.get(this.animateTarget).getBox();
30779         this.proxy.setSize(b.width, b.height);
30780         this.proxy.setLocation(b.x, b.y);
30781         this.proxy.show();
30782         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30783                     true, .35, this.showEl.createDelegate(this));
30784     },
30785
30786     /**
30787      * Shows the dialog.
30788      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30789      * @return {Roo.BasicDialog} this
30790      */
30791     show : function(animateTarget){
30792         if (this.fireEvent("beforeshow", this) === false){
30793             return;
30794         }
30795         if(this.syncHeightBeforeShow){
30796             this.syncBodyHeight();
30797         }else if(this.firstShow){
30798             this.firstShow = false;
30799             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30800         }
30801         this.animateTarget = animateTarget || this.animateTarget;
30802         if(!this.el.isVisible()){
30803             this.beforeShow();
30804             if(this.animateTarget && Roo.get(this.animateTarget)){
30805                 this.animShow();
30806             }else{
30807                 this.showEl();
30808             }
30809         }
30810         return this;
30811     },
30812
30813     // private
30814     showEl : function(){
30815         this.proxy.hide();
30816         this.el.setXY(this.xy);
30817         this.el.show();
30818         this.adjustAssets(true);
30819         this.toFront();
30820         this.focus();
30821         // IE peekaboo bug - fix found by Dave Fenwick
30822         if(Roo.isIE){
30823             this.el.repaint();
30824         }
30825         this.fireEvent("show", this);
30826     },
30827
30828     /**
30829      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30830      * dialog itself will receive focus.
30831      */
30832     focus : function(){
30833         if(this.defaultButton){
30834             this.defaultButton.focus();
30835         }else{
30836             this.focusEl.focus();
30837         }
30838     },
30839
30840     // private
30841     constrainXY : function(){
30842         if(this.constraintoviewport !== false){
30843             if(!this.viewSize){
30844                 if(this.container){
30845                     var s = this.container.getSize();
30846                     this.viewSize = [s.width, s.height];
30847                 }else{
30848                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30849                 }
30850             }
30851             var s = Roo.get(this.container||document).getScroll();
30852
30853             var x = this.xy[0], y = this.xy[1];
30854             var w = this.size.width, h = this.size.height;
30855             var vw = this.viewSize[0], vh = this.viewSize[1];
30856             // only move it if it needs it
30857             var moved = false;
30858             // first validate right/bottom
30859             if(x + w > vw+s.left){
30860                 x = vw - w;
30861                 moved = true;
30862             }
30863             if(y + h > vh+s.top){
30864                 y = vh - h;
30865                 moved = true;
30866             }
30867             // then make sure top/left isn't negative
30868             if(x < s.left){
30869                 x = s.left;
30870                 moved = true;
30871             }
30872             if(y < s.top){
30873                 y = s.top;
30874                 moved = true;
30875             }
30876             if(moved){
30877                 // cache xy
30878                 this.xy = [x, y];
30879                 if(this.isVisible()){
30880                     this.el.setLocation(x, y);
30881                     this.adjustAssets();
30882                 }
30883             }
30884         }
30885     },
30886
30887     // private
30888     onDrag : function(){
30889         if(!this.proxyDrag){
30890             this.xy = this.el.getXY();
30891             this.adjustAssets();
30892         }
30893     },
30894
30895     // private
30896     adjustAssets : function(doShow){
30897         var x = this.xy[0], y = this.xy[1];
30898         var w = this.size.width, h = this.size.height;
30899         if(doShow === true){
30900             if(this.shadow){
30901                 this.shadow.show(this.el);
30902             }
30903             if(this.shim){
30904                 this.shim.show();
30905             }
30906         }
30907         if(this.shadow && this.shadow.isVisible()){
30908             this.shadow.show(this.el);
30909         }
30910         if(this.shim && this.shim.isVisible()){
30911             this.shim.setBounds(x, y, w, h);
30912         }
30913     },
30914
30915     // private
30916     adjustViewport : function(w, h){
30917         if(!w || !h){
30918             w = Roo.lib.Dom.getViewWidth();
30919             h = Roo.lib.Dom.getViewHeight();
30920         }
30921         // cache the size
30922         this.viewSize = [w, h];
30923         if(this.modal && this.mask.isVisible()){
30924             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30925             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30926         }
30927         if(this.isVisible()){
30928             this.constrainXY();
30929         }
30930     },
30931
30932     /**
30933      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30934      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30935      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30936      */
30937     destroy : function(removeEl){
30938         if(this.isVisible()){
30939             this.animateTarget = null;
30940             this.hide();
30941         }
30942         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30943         if(this.tabs){
30944             this.tabs.destroy(removeEl);
30945         }
30946         Roo.destroy(
30947              this.shim,
30948              this.proxy,
30949              this.resizer,
30950              this.close,
30951              this.mask
30952         );
30953         if(this.dd){
30954             this.dd.unreg();
30955         }
30956         if(this.buttons){
30957            for(var i = 0, len = this.buttons.length; i < len; i++){
30958                this.buttons[i].destroy();
30959            }
30960         }
30961         this.el.removeAllListeners();
30962         if(removeEl === true){
30963             this.el.update("");
30964             this.el.remove();
30965         }
30966         Roo.DialogManager.unregister(this);
30967     },
30968
30969     // private
30970     startMove : function(){
30971         if(this.proxyDrag){
30972             this.proxy.show();
30973         }
30974         if(this.constraintoviewport !== false){
30975             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30976         }
30977     },
30978
30979     // private
30980     endMove : function(){
30981         if(!this.proxyDrag){
30982             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30983         }else{
30984             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30985             this.proxy.hide();
30986         }
30987         this.refreshSize();
30988         this.adjustAssets();
30989         this.focus();
30990         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30991     },
30992
30993     /**
30994      * Brings this dialog to the front of any other visible dialogs
30995      * @return {Roo.BasicDialog} this
30996      */
30997     toFront : function(){
30998         Roo.DialogManager.bringToFront(this);
30999         return this;
31000     },
31001
31002     /**
31003      * Sends this dialog to the back (under) of any other visible dialogs
31004      * @return {Roo.BasicDialog} this
31005      */
31006     toBack : function(){
31007         Roo.DialogManager.sendToBack(this);
31008         return this;
31009     },
31010
31011     /**
31012      * Centers this dialog in the viewport
31013      * @return {Roo.BasicDialog} this
31014      */
31015     center : function(){
31016         var xy = this.el.getCenterXY(true);
31017         this.moveTo(xy[0], xy[1]);
31018         return this;
31019     },
31020
31021     /**
31022      * Moves the dialog's top-left corner to the specified point
31023      * @param {Number} x
31024      * @param {Number} y
31025      * @return {Roo.BasicDialog} this
31026      */
31027     moveTo : function(x, y){
31028         this.xy = [x,y];
31029         if(this.isVisible()){
31030             this.el.setXY(this.xy);
31031             this.adjustAssets();
31032         }
31033         return this;
31034     },
31035
31036     /**
31037      * Aligns the dialog to the specified element
31038      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31039      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31040      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31041      * @return {Roo.BasicDialog} this
31042      */
31043     alignTo : function(element, position, offsets){
31044         this.xy = this.el.getAlignToXY(element, position, offsets);
31045         if(this.isVisible()){
31046             this.el.setXY(this.xy);
31047             this.adjustAssets();
31048         }
31049         return this;
31050     },
31051
31052     /**
31053      * Anchors an element to another element and realigns it when the window is resized.
31054      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31055      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31056      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31057      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31058      * is a number, it is used as the buffer delay (defaults to 50ms).
31059      * @return {Roo.BasicDialog} this
31060      */
31061     anchorTo : function(el, alignment, offsets, monitorScroll){
31062         var action = function(){
31063             this.alignTo(el, alignment, offsets);
31064         };
31065         Roo.EventManager.onWindowResize(action, this);
31066         var tm = typeof monitorScroll;
31067         if(tm != 'undefined'){
31068             Roo.EventManager.on(window, 'scroll', action, this,
31069                 {buffer: tm == 'number' ? monitorScroll : 50});
31070         }
31071         action.call(this);
31072         return this;
31073     },
31074
31075     /**
31076      * Returns true if the dialog is visible
31077      * @return {Boolean}
31078      */
31079     isVisible : function(){
31080         return this.el.isVisible();
31081     },
31082
31083     // private
31084     animHide : function(callback){
31085         var b = Roo.get(this.animateTarget).getBox();
31086         this.proxy.show();
31087         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31088         this.el.hide();
31089         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31090                     this.hideEl.createDelegate(this, [callback]));
31091     },
31092
31093     /**
31094      * Hides the dialog.
31095      * @param {Function} callback (optional) Function to call when the dialog is hidden
31096      * @return {Roo.BasicDialog} this
31097      */
31098     hide : function(callback){
31099         if (this.fireEvent("beforehide", this) === false){
31100             return;
31101         }
31102         if(this.shadow){
31103             this.shadow.hide();
31104         }
31105         if(this.shim) {
31106           this.shim.hide();
31107         }
31108         // sometimes animateTarget seems to get set.. causing problems...
31109         // this just double checks..
31110         if(this.animateTarget && Roo.get(this.animateTarget)) {
31111            this.animHide(callback);
31112         }else{
31113             this.el.hide();
31114             this.hideEl(callback);
31115         }
31116         return this;
31117     },
31118
31119     // private
31120     hideEl : function(callback){
31121         this.proxy.hide();
31122         if(this.modal){
31123             this.mask.hide();
31124             Roo.get(document.body).removeClass("x-body-masked");
31125         }
31126         this.fireEvent("hide", this);
31127         if(typeof callback == "function"){
31128             callback();
31129         }
31130     },
31131
31132     // private
31133     hideAction : function(){
31134         this.setLeft("-10000px");
31135         this.setTop("-10000px");
31136         this.setStyle("visibility", "hidden");
31137     },
31138
31139     // private
31140     refreshSize : function(){
31141         this.size = this.el.getSize();
31142         this.xy = this.el.getXY();
31143         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31144     },
31145
31146     // private
31147     // z-index is managed by the DialogManager and may be overwritten at any time
31148     setZIndex : function(index){
31149         if(this.modal){
31150             this.mask.setStyle("z-index", index);
31151         }
31152         if(this.shim){
31153             this.shim.setStyle("z-index", ++index);
31154         }
31155         if(this.shadow){
31156             this.shadow.setZIndex(++index);
31157         }
31158         this.el.setStyle("z-index", ++index);
31159         if(this.proxy){
31160             this.proxy.setStyle("z-index", ++index);
31161         }
31162         if(this.resizer){
31163             this.resizer.proxy.setStyle("z-index", ++index);
31164         }
31165
31166         this.lastZIndex = index;
31167     },
31168
31169     /**
31170      * Returns the element for this dialog
31171      * @return {Roo.Element} The underlying dialog Element
31172      */
31173     getEl : function(){
31174         return this.el;
31175     }
31176 });
31177
31178 /**
31179  * @class Roo.DialogManager
31180  * Provides global access to BasicDialogs that have been created and
31181  * support for z-indexing (layering) multiple open dialogs.
31182  */
31183 Roo.DialogManager = function(){
31184     var list = {};
31185     var accessList = [];
31186     var front = null;
31187
31188     // private
31189     var sortDialogs = function(d1, d2){
31190         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31191     };
31192
31193     // private
31194     var orderDialogs = function(){
31195         accessList.sort(sortDialogs);
31196         var seed = Roo.DialogManager.zseed;
31197         for(var i = 0, len = accessList.length; i < len; i++){
31198             var dlg = accessList[i];
31199             if(dlg){
31200                 dlg.setZIndex(seed + (i*10));
31201             }
31202         }
31203     };
31204
31205     return {
31206         /**
31207          * The starting z-index for BasicDialogs (defaults to 9000)
31208          * @type Number The z-index value
31209          */
31210         zseed : 9000,
31211
31212         // private
31213         register : function(dlg){
31214             list[dlg.id] = dlg;
31215             accessList.push(dlg);
31216         },
31217
31218         // private
31219         unregister : function(dlg){
31220             delete list[dlg.id];
31221             var i=0;
31222             var len=0;
31223             if(!accessList.indexOf){
31224                 for(  i = 0, len = accessList.length; i < len; i++){
31225                     if(accessList[i] == dlg){
31226                         accessList.splice(i, 1);
31227                         return;
31228                     }
31229                 }
31230             }else{
31231                  i = accessList.indexOf(dlg);
31232                 if(i != -1){
31233                     accessList.splice(i, 1);
31234                 }
31235             }
31236         },
31237
31238         /**
31239          * Gets a registered dialog by id
31240          * @param {String/Object} id The id of the dialog or a dialog
31241          * @return {Roo.BasicDialog} this
31242          */
31243         get : function(id){
31244             return typeof id == "object" ? id : list[id];
31245         },
31246
31247         /**
31248          * Brings the specified dialog to the front
31249          * @param {String/Object} dlg The id of the dialog or a dialog
31250          * @return {Roo.BasicDialog} this
31251          */
31252         bringToFront : function(dlg){
31253             dlg = this.get(dlg);
31254             if(dlg != front){
31255                 front = dlg;
31256                 dlg._lastAccess = new Date().getTime();
31257                 orderDialogs();
31258             }
31259             return dlg;
31260         },
31261
31262         /**
31263          * Sends the specified dialog to the back
31264          * @param {String/Object} dlg The id of the dialog or a dialog
31265          * @return {Roo.BasicDialog} this
31266          */
31267         sendToBack : function(dlg){
31268             dlg = this.get(dlg);
31269             dlg._lastAccess = -(new Date().getTime());
31270             orderDialogs();
31271             return dlg;
31272         },
31273
31274         /**
31275          * Hides all dialogs
31276          */
31277         hideAll : function(){
31278             for(var id in list){
31279                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31280                     list[id].hide();
31281                 }
31282             }
31283         }
31284     };
31285 }();
31286
31287 /**
31288  * @class Roo.LayoutDialog
31289  * @extends Roo.BasicDialog
31290  * Dialog which provides adjustments for working with a layout in a Dialog.
31291  * Add your necessary layout config options to the dialog's config.<br>
31292  * Example usage (including a nested layout):
31293  * <pre><code>
31294 if(!dialog){
31295     dialog = new Roo.LayoutDialog("download-dlg", {
31296         modal: true,
31297         width:600,
31298         height:450,
31299         shadow:true,
31300         minWidth:500,
31301         minHeight:350,
31302         autoTabs:true,
31303         proxyDrag:true,
31304         // layout config merges with the dialog config
31305         center:{
31306             tabPosition: "top",
31307             alwaysShowTabs: true
31308         }
31309     });
31310     dialog.addKeyListener(27, dialog.hide, dialog);
31311     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31312     dialog.addButton("Build It!", this.getDownload, this);
31313
31314     // we can even add nested layouts
31315     var innerLayout = new Roo.BorderLayout("dl-inner", {
31316         east: {
31317             initialSize: 200,
31318             autoScroll:true,
31319             split:true
31320         },
31321         center: {
31322             autoScroll:true
31323         }
31324     });
31325     innerLayout.beginUpdate();
31326     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31327     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31328     innerLayout.endUpdate(true);
31329
31330     var layout = dialog.getLayout();
31331     layout.beginUpdate();
31332     layout.add("center", new Roo.ContentPanel("standard-panel",
31333                         {title: "Download the Source", fitToFrame:true}));
31334     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31335                {title: "Build your own roo.js"}));
31336     layout.getRegion("center").showPanel(sp);
31337     layout.endUpdate();
31338 }
31339 </code></pre>
31340     * @constructor
31341     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31342     * @param {Object} config configuration options
31343   */
31344 Roo.LayoutDialog = function(el, cfg){
31345     
31346     var config=  cfg;
31347     if (typeof(cfg) == 'undefined') {
31348         config = Roo.apply({}, el);
31349         // not sure why we use documentElement here.. - it should always be body.
31350         // IE7 borks horribly if we use documentElement.
31351         // webkit also does not like documentElement - it creates a body element...
31352         el = Roo.get( document.body || document.documentElement ).createChild();
31353         //config.autoCreate = true;
31354     }
31355     
31356     
31357     config.autoTabs = false;
31358     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31359     this.body.setStyle({overflow:"hidden", position:"relative"});
31360     this.layout = new Roo.BorderLayout(this.body.dom, config);
31361     this.layout.monitorWindowResize = false;
31362     this.el.addClass("x-dlg-auto-layout");
31363     // fix case when center region overwrites center function
31364     this.center = Roo.BasicDialog.prototype.center;
31365     this.on("show", this.layout.layout, this.layout, true);
31366     if (config.items) {
31367         var xitems = config.items;
31368         delete config.items;
31369         Roo.each(xitems, this.addxtype, this);
31370     }
31371     
31372     
31373 };
31374 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31375     /**
31376      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31377      * @deprecated
31378      */
31379     endUpdate : function(){
31380         this.layout.endUpdate();
31381     },
31382
31383     /**
31384      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31385      *  @deprecated
31386      */
31387     beginUpdate : function(){
31388         this.layout.beginUpdate();
31389     },
31390
31391     /**
31392      * Get the BorderLayout for this dialog
31393      * @return {Roo.BorderLayout}
31394      */
31395     getLayout : function(){
31396         return this.layout;
31397     },
31398
31399     showEl : function(){
31400         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31401         if(Roo.isIE7){
31402             this.layout.layout();
31403         }
31404     },
31405
31406     // private
31407     // Use the syncHeightBeforeShow config option to control this automatically
31408     syncBodyHeight : function(){
31409         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31410         if(this.layout){this.layout.layout();}
31411     },
31412     
31413       /**
31414      * Add an xtype element (actually adds to the layout.)
31415      * @return {Object} xdata xtype object data.
31416      */
31417     
31418     addxtype : function(c) {
31419         return this.layout.addxtype(c);
31420     }
31421 });/*
31422  * Based on:
31423  * Ext JS Library 1.1.1
31424  * Copyright(c) 2006-2007, Ext JS, LLC.
31425  *
31426  * Originally Released Under LGPL - original licence link has changed is not relivant.
31427  *
31428  * Fork - LGPL
31429  * <script type="text/javascript">
31430  */
31431  
31432 /**
31433  * @class Roo.MessageBox
31434  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31435  * Example usage:
31436  *<pre><code>
31437 // Basic alert:
31438 Roo.Msg.alert('Status', 'Changes saved successfully.');
31439
31440 // Prompt for user data:
31441 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31442     if (btn == 'ok'){
31443         // process text value...
31444     }
31445 });
31446
31447 // Show a dialog using config options:
31448 Roo.Msg.show({
31449    title:'Save Changes?',
31450    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31451    buttons: Roo.Msg.YESNOCANCEL,
31452    fn: processResult,
31453    animEl: 'elId'
31454 });
31455 </code></pre>
31456  * @singleton
31457  */
31458 Roo.MessageBox = function(){
31459     var dlg, opt, mask, waitTimer;
31460     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31461     var buttons, activeTextEl, bwidth;
31462
31463     // private
31464     var handleButton = function(button){
31465         dlg.hide();
31466         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31467     };
31468
31469     // private
31470     var handleHide = function(){
31471         if(opt && opt.cls){
31472             dlg.el.removeClass(opt.cls);
31473         }
31474         if(waitTimer){
31475             Roo.TaskMgr.stop(waitTimer);
31476             waitTimer = null;
31477         }
31478     };
31479
31480     // private
31481     var updateButtons = function(b){
31482         var width = 0;
31483         if(!b){
31484             buttons["ok"].hide();
31485             buttons["cancel"].hide();
31486             buttons["yes"].hide();
31487             buttons["no"].hide();
31488             dlg.footer.dom.style.display = 'none';
31489             return width;
31490         }
31491         dlg.footer.dom.style.display = '';
31492         for(var k in buttons){
31493             if(typeof buttons[k] != "function"){
31494                 if(b[k]){
31495                     buttons[k].show();
31496                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31497                     width += buttons[k].el.getWidth()+15;
31498                 }else{
31499                     buttons[k].hide();
31500                 }
31501             }
31502         }
31503         return width;
31504     };
31505
31506     // private
31507     var handleEsc = function(d, k, e){
31508         if(opt && opt.closable !== false){
31509             dlg.hide();
31510         }
31511         if(e){
31512             e.stopEvent();
31513         }
31514     };
31515
31516     return {
31517         /**
31518          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31519          * @return {Roo.BasicDialog} The BasicDialog element
31520          */
31521         getDialog : function(){
31522            if(!dlg){
31523                 dlg = new Roo.BasicDialog("x-msg-box", {
31524                     autoCreate : true,
31525                     shadow: true,
31526                     draggable: true,
31527                     resizable:false,
31528                     constraintoviewport:false,
31529                     fixedcenter:true,
31530                     collapsible : false,
31531                     shim:true,
31532                     modal: true,
31533                     width:400, height:100,
31534                     buttonAlign:"center",
31535                     closeClick : function(){
31536                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31537                             handleButton("no");
31538                         }else{
31539                             handleButton("cancel");
31540                         }
31541                     }
31542                 });
31543                 dlg.on("hide", handleHide);
31544                 mask = dlg.mask;
31545                 dlg.addKeyListener(27, handleEsc);
31546                 buttons = {};
31547                 var bt = this.buttonText;
31548                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31549                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31550                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31551                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31552                 bodyEl = dlg.body.createChild({
31553
31554                     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>'
31555                 });
31556                 msgEl = bodyEl.dom.firstChild;
31557                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31558                 textboxEl.enableDisplayMode();
31559                 textboxEl.addKeyListener([10,13], function(){
31560                     if(dlg.isVisible() && opt && opt.buttons){
31561                         if(opt.buttons.ok){
31562                             handleButton("ok");
31563                         }else if(opt.buttons.yes){
31564                             handleButton("yes");
31565                         }
31566                     }
31567                 });
31568                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31569                 textareaEl.enableDisplayMode();
31570                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31571                 progressEl.enableDisplayMode();
31572                 var pf = progressEl.dom.firstChild;
31573                 if (pf) {
31574                     pp = Roo.get(pf.firstChild);
31575                     pp.setHeight(pf.offsetHeight);
31576                 }
31577                 
31578             }
31579             return dlg;
31580         },
31581
31582         /**
31583          * Updates the message box body text
31584          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31585          * the XHTML-compliant non-breaking space character '&amp;#160;')
31586          * @return {Roo.MessageBox} This message box
31587          */
31588         updateText : function(text){
31589             if(!dlg.isVisible() && !opt.width){
31590                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31591             }
31592             msgEl.innerHTML = text || '&#160;';
31593       
31594             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31595             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31596             var w = Math.max(
31597                     Math.min(opt.width || cw , this.maxWidth), 
31598                     Math.max(opt.minWidth || this.minWidth, bwidth)
31599             );
31600             if(opt.prompt){
31601                 activeTextEl.setWidth(w);
31602             }
31603             if(dlg.isVisible()){
31604                 dlg.fixedcenter = false;
31605             }
31606             // to big, make it scroll. = But as usual stupid IE does not support
31607             // !important..
31608             
31609             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31610                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31611                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31612             } else {
31613                 bodyEl.dom.style.height = '';
31614                 bodyEl.dom.style.overflowY = '';
31615             }
31616             if (cw > w) {
31617                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31618             } else {
31619                 bodyEl.dom.style.overflowX = '';
31620             }
31621             
31622             dlg.setContentSize(w, bodyEl.getHeight());
31623             if(dlg.isVisible()){
31624                 dlg.fixedcenter = true;
31625             }
31626             return this;
31627         },
31628
31629         /**
31630          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31631          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31632          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31633          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31634          * @return {Roo.MessageBox} This message box
31635          */
31636         updateProgress : function(value, text){
31637             if(text){
31638                 this.updateText(text);
31639             }
31640             if (pp) { // weird bug on my firefox - for some reason this is not defined
31641                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31642             }
31643             return this;
31644         },        
31645
31646         /**
31647          * Returns true if the message box is currently displayed
31648          * @return {Boolean} True if the message box is visible, else false
31649          */
31650         isVisible : function(){
31651             return dlg && dlg.isVisible();  
31652         },
31653
31654         /**
31655          * Hides the message box if it is displayed
31656          */
31657         hide : function(){
31658             if(this.isVisible()){
31659                 dlg.hide();
31660             }  
31661         },
31662
31663         /**
31664          * Displays a new message box, or reinitializes an existing message box, based on the config options
31665          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31666          * The following config object properties are supported:
31667          * <pre>
31668 Property    Type             Description
31669 ----------  ---------------  ------------------------------------------------------------------------------------
31670 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31671                                    closes (defaults to undefined)
31672 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31673                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31674 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31675                                    progress and wait dialogs will ignore this property and always hide the
31676                                    close button as they can only be closed programmatically.
31677 cls               String           A custom CSS class to apply to the message box element
31678 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31679                                    displayed (defaults to 75)
31680 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31681                                    function will be btn (the name of the button that was clicked, if applicable,
31682                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31683                                    Progress and wait dialogs will ignore this option since they do not respond to
31684                                    user actions and can only be closed programmatically, so any required function
31685                                    should be called by the same code after it closes the dialog.
31686 icon              String           A CSS class that provides a background image to be used as an icon for
31687                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31688 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31689 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31690 modal             Boolean          False to allow user interaction with the page while the message box is
31691                                    displayed (defaults to true)
31692 msg               String           A string that will replace the existing message box body text (defaults
31693                                    to the XHTML-compliant non-breaking space character '&#160;')
31694 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31695 progress          Boolean          True to display a progress bar (defaults to false)
31696 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31697 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31698 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31699 title             String           The title text
31700 value             String           The string value to set into the active textbox element if displayed
31701 wait              Boolean          True to display a progress bar (defaults to false)
31702 width             Number           The width of the dialog in pixels
31703 </pre>
31704          *
31705          * Example usage:
31706          * <pre><code>
31707 Roo.Msg.show({
31708    title: 'Address',
31709    msg: 'Please enter your address:',
31710    width: 300,
31711    buttons: Roo.MessageBox.OKCANCEL,
31712    multiline: true,
31713    fn: saveAddress,
31714    animEl: 'addAddressBtn'
31715 });
31716 </code></pre>
31717          * @param {Object} config Configuration options
31718          * @return {Roo.MessageBox} This message box
31719          */
31720         show : function(options)
31721         {
31722             
31723             // this causes nightmares if you show one dialog after another
31724             // especially on callbacks..
31725              
31726             if(this.isVisible()){
31727                 
31728                 this.hide();
31729                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31730                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31731                 Roo.log("New Dialog Message:" +  options.msg )
31732                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31733                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31734                 
31735             }
31736             var d = this.getDialog();
31737             opt = options;
31738             d.setTitle(opt.title || "&#160;");
31739             d.close.setDisplayed(opt.closable !== false);
31740             activeTextEl = textboxEl;
31741             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31742             if(opt.prompt){
31743                 if(opt.multiline){
31744                     textboxEl.hide();
31745                     textareaEl.show();
31746                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31747                         opt.multiline : this.defaultTextHeight);
31748                     activeTextEl = textareaEl;
31749                 }else{
31750                     textboxEl.show();
31751                     textareaEl.hide();
31752                 }
31753             }else{
31754                 textboxEl.hide();
31755                 textareaEl.hide();
31756             }
31757             progressEl.setDisplayed(opt.progress === true);
31758             this.updateProgress(0);
31759             activeTextEl.dom.value = opt.value || "";
31760             if(opt.prompt){
31761                 dlg.setDefaultButton(activeTextEl);
31762             }else{
31763                 var bs = opt.buttons;
31764                 var db = null;
31765                 if(bs && bs.ok){
31766                     db = buttons["ok"];
31767                 }else if(bs && bs.yes){
31768                     db = buttons["yes"];
31769                 }
31770                 dlg.setDefaultButton(db);
31771             }
31772             bwidth = updateButtons(opt.buttons);
31773             this.updateText(opt.msg);
31774             if(opt.cls){
31775                 d.el.addClass(opt.cls);
31776             }
31777             d.proxyDrag = opt.proxyDrag === true;
31778             d.modal = opt.modal !== false;
31779             d.mask = opt.modal !== false ? mask : false;
31780             if(!d.isVisible()){
31781                 // force it to the end of the z-index stack so it gets a cursor in FF
31782                 document.body.appendChild(dlg.el.dom);
31783                 d.animateTarget = null;
31784                 d.show(options.animEl);
31785             }
31786             return this;
31787         },
31788
31789         /**
31790          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31791          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31792          * and closing the message box when the process is complete.
31793          * @param {String} title The title bar text
31794          * @param {String} msg The message box body text
31795          * @return {Roo.MessageBox} This message box
31796          */
31797         progress : function(title, msg){
31798             this.show({
31799                 title : title,
31800                 msg : msg,
31801                 buttons: false,
31802                 progress:true,
31803                 closable:false,
31804                 minWidth: this.minProgressWidth,
31805                 modal : true
31806             });
31807             return this;
31808         },
31809
31810         /**
31811          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31812          * If a callback function is passed it will be called after the user clicks the button, and the
31813          * id of the button that was clicked will be passed as the only parameter to the callback
31814          * (could also be the top-right close button).
31815          * @param {String} title The title bar text
31816          * @param {String} msg The message box body text
31817          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31818          * @param {Object} scope (optional) The scope of the callback function
31819          * @return {Roo.MessageBox} This message box
31820          */
31821         alert : function(title, msg, fn, scope){
31822             this.show({
31823                 title : title,
31824                 msg : msg,
31825                 buttons: this.OK,
31826                 fn: fn,
31827                 scope : scope,
31828                 modal : true
31829             });
31830             return this;
31831         },
31832
31833         /**
31834          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31835          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31836          * You are responsible for closing the message box when the process is complete.
31837          * @param {String} msg The message box body text
31838          * @param {String} title (optional) The title bar text
31839          * @return {Roo.MessageBox} This message box
31840          */
31841         wait : function(msg, title){
31842             this.show({
31843                 title : title,
31844                 msg : msg,
31845                 buttons: false,
31846                 closable:false,
31847                 progress:true,
31848                 modal:true,
31849                 width:300,
31850                 wait:true
31851             });
31852             waitTimer = Roo.TaskMgr.start({
31853                 run: function(i){
31854                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31855                 },
31856                 interval: 1000
31857             });
31858             return this;
31859         },
31860
31861         /**
31862          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31863          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31864          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31865          * @param {String} title The title bar text
31866          * @param {String} msg The message box body text
31867          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31868          * @param {Object} scope (optional) The scope of the callback function
31869          * @return {Roo.MessageBox} This message box
31870          */
31871         confirm : function(title, msg, fn, scope){
31872             this.show({
31873                 title : title,
31874                 msg : msg,
31875                 buttons: this.YESNO,
31876                 fn: fn,
31877                 scope : scope,
31878                 modal : true
31879             });
31880             return this;
31881         },
31882
31883         /**
31884          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31885          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31886          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31887          * (could also be the top-right close button) and the text that was entered will be passed as the two
31888          * parameters to the callback.
31889          * @param {String} title The title bar text
31890          * @param {String} msg The message box body text
31891          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31892          * @param {Object} scope (optional) The scope of the callback function
31893          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31894          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31895          * @return {Roo.MessageBox} This message box
31896          */
31897         prompt : function(title, msg, fn, scope, multiline){
31898             this.show({
31899                 title : title,
31900                 msg : msg,
31901                 buttons: this.OKCANCEL,
31902                 fn: fn,
31903                 minWidth:250,
31904                 scope : scope,
31905                 prompt:true,
31906                 multiline: multiline,
31907                 modal : true
31908             });
31909             return this;
31910         },
31911
31912         /**
31913          * Button config that displays a single OK button
31914          * @type Object
31915          */
31916         OK : {ok:true},
31917         /**
31918          * Button config that displays Yes and No buttons
31919          * @type Object
31920          */
31921         YESNO : {yes:true, no:true},
31922         /**
31923          * Button config that displays OK and Cancel buttons
31924          * @type Object
31925          */
31926         OKCANCEL : {ok:true, cancel:true},
31927         /**
31928          * Button config that displays Yes, No and Cancel buttons
31929          * @type Object
31930          */
31931         YESNOCANCEL : {yes:true, no:true, cancel:true},
31932
31933         /**
31934          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31935          * @type Number
31936          */
31937         defaultTextHeight : 75,
31938         /**
31939          * The maximum width in pixels of the message box (defaults to 600)
31940          * @type Number
31941          */
31942         maxWidth : 600,
31943         /**
31944          * The minimum width in pixels of the message box (defaults to 100)
31945          * @type Number
31946          */
31947         minWidth : 100,
31948         /**
31949          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31950          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31951          * @type Number
31952          */
31953         minProgressWidth : 250,
31954         /**
31955          * An object containing the default button text strings that can be overriden for localized language support.
31956          * Supported properties are: ok, cancel, yes and no.
31957          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31958          * @type Object
31959          */
31960         buttonText : {
31961             ok : "OK",
31962             cancel : "Cancel",
31963             yes : "Yes",
31964             no : "No"
31965         }
31966     };
31967 }();
31968
31969 /**
31970  * Shorthand for {@link Roo.MessageBox}
31971  */
31972 Roo.Msg = Roo.MessageBox;/*
31973  * Based on:
31974  * Ext JS Library 1.1.1
31975  * Copyright(c) 2006-2007, Ext JS, LLC.
31976  *
31977  * Originally Released Under LGPL - original licence link has changed is not relivant.
31978  *
31979  * Fork - LGPL
31980  * <script type="text/javascript">
31981  */
31982 /**
31983  * @class Roo.QuickTips
31984  * Provides attractive and customizable tooltips for any element.
31985  * @singleton
31986  */
31987 Roo.QuickTips = function(){
31988     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31989     var ce, bd, xy, dd;
31990     var visible = false, disabled = true, inited = false;
31991     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31992     
31993     var onOver = function(e){
31994         if(disabled){
31995             return;
31996         }
31997         var t = e.getTarget();
31998         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31999             return;
32000         }
32001         if(ce && t == ce.el){
32002             clearTimeout(hideProc);
32003             return;
32004         }
32005         if(t && tagEls[t.id]){
32006             tagEls[t.id].el = t;
32007             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32008             return;
32009         }
32010         var ttp, et = Roo.fly(t);
32011         var ns = cfg.namespace;
32012         if(tm.interceptTitles && t.title){
32013             ttp = t.title;
32014             t.qtip = ttp;
32015             t.removeAttribute("title");
32016             e.preventDefault();
32017         }else{
32018             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32019         }
32020         if(ttp){
32021             showProc = show.defer(tm.showDelay, tm, [{
32022                 el: t, 
32023                 text: ttp, 
32024                 width: et.getAttributeNS(ns, cfg.width),
32025                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32026                 title: et.getAttributeNS(ns, cfg.title),
32027                     cls: et.getAttributeNS(ns, cfg.cls)
32028             }]);
32029         }
32030     };
32031     
32032     var onOut = function(e){
32033         clearTimeout(showProc);
32034         var t = e.getTarget();
32035         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32036             hideProc = setTimeout(hide, tm.hideDelay);
32037         }
32038     };
32039     
32040     var onMove = function(e){
32041         if(disabled){
32042             return;
32043         }
32044         xy = e.getXY();
32045         xy[1] += 18;
32046         if(tm.trackMouse && ce){
32047             el.setXY(xy);
32048         }
32049     };
32050     
32051     var onDown = function(e){
32052         clearTimeout(showProc);
32053         clearTimeout(hideProc);
32054         if(!e.within(el)){
32055             if(tm.hideOnClick){
32056                 hide();
32057                 tm.disable();
32058                 tm.enable.defer(100, tm);
32059             }
32060         }
32061     };
32062     
32063     var getPad = function(){
32064         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32065     };
32066
32067     var show = function(o){
32068         if(disabled){
32069             return;
32070         }
32071         clearTimeout(dismissProc);
32072         ce = o;
32073         if(removeCls){ // in case manually hidden
32074             el.removeClass(removeCls);
32075             removeCls = null;
32076         }
32077         if(ce.cls){
32078             el.addClass(ce.cls);
32079             removeCls = ce.cls;
32080         }
32081         if(ce.title){
32082             tipTitle.update(ce.title);
32083             tipTitle.show();
32084         }else{
32085             tipTitle.update('');
32086             tipTitle.hide();
32087         }
32088         el.dom.style.width  = tm.maxWidth+'px';
32089         //tipBody.dom.style.width = '';
32090         tipBodyText.update(o.text);
32091         var p = getPad(), w = ce.width;
32092         if(!w){
32093             var td = tipBodyText.dom;
32094             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32095             if(aw > tm.maxWidth){
32096                 w = tm.maxWidth;
32097             }else if(aw < tm.minWidth){
32098                 w = tm.minWidth;
32099             }else{
32100                 w = aw;
32101             }
32102         }
32103         //tipBody.setWidth(w);
32104         el.setWidth(parseInt(w, 10) + p);
32105         if(ce.autoHide === false){
32106             close.setDisplayed(true);
32107             if(dd){
32108                 dd.unlock();
32109             }
32110         }else{
32111             close.setDisplayed(false);
32112             if(dd){
32113                 dd.lock();
32114             }
32115         }
32116         if(xy){
32117             el.avoidY = xy[1]-18;
32118             el.setXY(xy);
32119         }
32120         if(tm.animate){
32121             el.setOpacity(.1);
32122             el.setStyle("visibility", "visible");
32123             el.fadeIn({callback: afterShow});
32124         }else{
32125             afterShow();
32126         }
32127     };
32128     
32129     var afterShow = function(){
32130         if(ce){
32131             el.show();
32132             esc.enable();
32133             if(tm.autoDismiss && ce.autoHide !== false){
32134                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32135             }
32136         }
32137     };
32138     
32139     var hide = function(noanim){
32140         clearTimeout(dismissProc);
32141         clearTimeout(hideProc);
32142         ce = null;
32143         if(el.isVisible()){
32144             esc.disable();
32145             if(noanim !== true && tm.animate){
32146                 el.fadeOut({callback: afterHide});
32147             }else{
32148                 afterHide();
32149             } 
32150         }
32151     };
32152     
32153     var afterHide = function(){
32154         el.hide();
32155         if(removeCls){
32156             el.removeClass(removeCls);
32157             removeCls = null;
32158         }
32159     };
32160     
32161     return {
32162         /**
32163         * @cfg {Number} minWidth
32164         * The minimum width of the quick tip (defaults to 40)
32165         */
32166        minWidth : 40,
32167         /**
32168         * @cfg {Number} maxWidth
32169         * The maximum width of the quick tip (defaults to 300)
32170         */
32171        maxWidth : 300,
32172         /**
32173         * @cfg {Boolean} interceptTitles
32174         * True to automatically use the element's DOM title value if available (defaults to false)
32175         */
32176        interceptTitles : false,
32177         /**
32178         * @cfg {Boolean} trackMouse
32179         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32180         */
32181        trackMouse : false,
32182         /**
32183         * @cfg {Boolean} hideOnClick
32184         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32185         */
32186        hideOnClick : true,
32187         /**
32188         * @cfg {Number} showDelay
32189         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32190         */
32191        showDelay : 500,
32192         /**
32193         * @cfg {Number} hideDelay
32194         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32195         */
32196        hideDelay : 200,
32197         /**
32198         * @cfg {Boolean} autoHide
32199         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32200         * Used in conjunction with hideDelay.
32201         */
32202        autoHide : true,
32203         /**
32204         * @cfg {Boolean}
32205         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32206         * (defaults to true).  Used in conjunction with autoDismissDelay.
32207         */
32208        autoDismiss : true,
32209         /**
32210         * @cfg {Number}
32211         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32212         */
32213        autoDismissDelay : 5000,
32214        /**
32215         * @cfg {Boolean} animate
32216         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32217         */
32218        animate : false,
32219
32220        /**
32221         * @cfg {String} title
32222         * Title text to display (defaults to '').  This can be any valid HTML markup.
32223         */
32224         title: '',
32225        /**
32226         * @cfg {String} text
32227         * Body text to display (defaults to '').  This can be any valid HTML markup.
32228         */
32229         text : '',
32230        /**
32231         * @cfg {String} cls
32232         * A CSS class to apply to the base quick tip element (defaults to '').
32233         */
32234         cls : '',
32235        /**
32236         * @cfg {Number} width
32237         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32238         * minWidth or maxWidth.
32239         */
32240         width : null,
32241
32242     /**
32243      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32244      * or display QuickTips in a page.
32245      */
32246        init : function(){
32247           tm = Roo.QuickTips;
32248           cfg = tm.tagConfig;
32249           if(!inited){
32250               if(!Roo.isReady){ // allow calling of init() before onReady
32251                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32252                   return;
32253               }
32254               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32255               el.fxDefaults = {stopFx: true};
32256               // maximum custom styling
32257               //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>');
32258               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>');              
32259               tipTitle = el.child('h3');
32260               tipTitle.enableDisplayMode("block");
32261               tipBody = el.child('div.x-tip-bd');
32262               tipBodyText = el.child('div.x-tip-bd-inner');
32263               //bdLeft = el.child('div.x-tip-bd-left');
32264               //bdRight = el.child('div.x-tip-bd-right');
32265               close = el.child('div.x-tip-close');
32266               close.enableDisplayMode("block");
32267               close.on("click", hide);
32268               var d = Roo.get(document);
32269               d.on("mousedown", onDown);
32270               d.on("mouseover", onOver);
32271               d.on("mouseout", onOut);
32272               d.on("mousemove", onMove);
32273               esc = d.addKeyListener(27, hide);
32274               esc.disable();
32275               if(Roo.dd.DD){
32276                   dd = el.initDD("default", null, {
32277                       onDrag : function(){
32278                           el.sync();  
32279                       }
32280                   });
32281                   dd.setHandleElId(tipTitle.id);
32282                   dd.lock();
32283               }
32284               inited = true;
32285           }
32286           this.enable(); 
32287        },
32288
32289     /**
32290      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32291      * are supported:
32292      * <pre>
32293 Property    Type                   Description
32294 ----------  ---------------------  ------------------------------------------------------------------------
32295 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32296      * </ul>
32297      * @param {Object} config The config object
32298      */
32299        register : function(config){
32300            var cs = config instanceof Array ? config : arguments;
32301            for(var i = 0, len = cs.length; i < len; i++) {
32302                var c = cs[i];
32303                var target = c.target;
32304                if(target){
32305                    if(target instanceof Array){
32306                        for(var j = 0, jlen = target.length; j < jlen; j++){
32307                            tagEls[target[j]] = c;
32308                        }
32309                    }else{
32310                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32311                    }
32312                }
32313            }
32314        },
32315
32316     /**
32317      * Removes this quick tip from its element and destroys it.
32318      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32319      */
32320        unregister : function(el){
32321            delete tagEls[Roo.id(el)];
32322        },
32323
32324     /**
32325      * Enable this quick tip.
32326      */
32327        enable : function(){
32328            if(inited && disabled){
32329                locks.pop();
32330                if(locks.length < 1){
32331                    disabled = false;
32332                }
32333            }
32334        },
32335
32336     /**
32337      * Disable this quick tip.
32338      */
32339        disable : function(){
32340           disabled = true;
32341           clearTimeout(showProc);
32342           clearTimeout(hideProc);
32343           clearTimeout(dismissProc);
32344           if(ce){
32345               hide(true);
32346           }
32347           locks.push(1);
32348        },
32349
32350     /**
32351      * Returns true if the quick tip is enabled, else false.
32352      */
32353        isEnabled : function(){
32354             return !disabled;
32355        },
32356
32357         // private
32358        tagConfig : {
32359            namespace : "ext",
32360            attribute : "qtip",
32361            width : "width",
32362            target : "target",
32363            title : "qtitle",
32364            hide : "hide",
32365            cls : "qclass"
32366        }
32367    };
32368 }();
32369
32370 // backwards compat
32371 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32372  * Based on:
32373  * Ext JS Library 1.1.1
32374  * Copyright(c) 2006-2007, Ext JS, LLC.
32375  *
32376  * Originally Released Under LGPL - original licence link has changed is not relivant.
32377  *
32378  * Fork - LGPL
32379  * <script type="text/javascript">
32380  */
32381  
32382
32383 /**
32384  * @class Roo.tree.TreePanel
32385  * @extends Roo.data.Tree
32386
32387  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32388  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32389  * @cfg {Boolean} enableDD true to enable drag and drop
32390  * @cfg {Boolean} enableDrag true to enable just drag
32391  * @cfg {Boolean} enableDrop true to enable just drop
32392  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32393  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32394  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32395  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32396  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32397  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32398  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32399  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32400  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32401  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32402  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32403  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32404  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32405  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32406  * @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>
32407  * @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>
32408  * 
32409  * @constructor
32410  * @param {String/HTMLElement/Element} el The container element
32411  * @param {Object} config
32412  */
32413 Roo.tree.TreePanel = function(el, config){
32414     var root = false;
32415     var loader = false;
32416     if (config.root) {
32417         root = config.root;
32418         delete config.root;
32419     }
32420     if (config.loader) {
32421         loader = config.loader;
32422         delete config.loader;
32423     }
32424     
32425     Roo.apply(this, config);
32426     Roo.tree.TreePanel.superclass.constructor.call(this);
32427     this.el = Roo.get(el);
32428     this.el.addClass('x-tree');
32429     //console.log(root);
32430     if (root) {
32431         this.setRootNode( Roo.factory(root, Roo.tree));
32432     }
32433     if (loader) {
32434         this.loader = Roo.factory(loader, Roo.tree);
32435     }
32436    /**
32437     * Read-only. The id of the container element becomes this TreePanel's id.
32438     */
32439     this.id = this.el.id;
32440     this.addEvents({
32441         /**
32442         * @event beforeload
32443         * Fires before a node is loaded, return false to cancel
32444         * @param {Node} node The node being loaded
32445         */
32446         "beforeload" : true,
32447         /**
32448         * @event load
32449         * Fires when a node is loaded
32450         * @param {Node} node The node that was loaded
32451         */
32452         "load" : true,
32453         /**
32454         * @event textchange
32455         * Fires when the text for a node is changed
32456         * @param {Node} node The node
32457         * @param {String} text The new text
32458         * @param {String} oldText The old text
32459         */
32460         "textchange" : true,
32461         /**
32462         * @event beforeexpand
32463         * Fires before a node is expanded, return false to cancel.
32464         * @param {Node} node The node
32465         * @param {Boolean} deep
32466         * @param {Boolean} anim
32467         */
32468         "beforeexpand" : true,
32469         /**
32470         * @event beforecollapse
32471         * Fires before a node is collapsed, return false to cancel.
32472         * @param {Node} node The node
32473         * @param {Boolean} deep
32474         * @param {Boolean} anim
32475         */
32476         "beforecollapse" : true,
32477         /**
32478         * @event expand
32479         * Fires when a node is expanded
32480         * @param {Node} node The node
32481         */
32482         "expand" : true,
32483         /**
32484         * @event disabledchange
32485         * Fires when the disabled status of a node changes
32486         * @param {Node} node The node
32487         * @param {Boolean} disabled
32488         */
32489         "disabledchange" : true,
32490         /**
32491         * @event collapse
32492         * Fires when a node is collapsed
32493         * @param {Node} node The node
32494         */
32495         "collapse" : true,
32496         /**
32497         * @event beforeclick
32498         * Fires before click processing on a node. Return false to cancel the default action.
32499         * @param {Node} node The node
32500         * @param {Roo.EventObject} e The event object
32501         */
32502         "beforeclick":true,
32503         /**
32504         * @event checkchange
32505         * Fires when a node with a checkbox's checked property changes
32506         * @param {Node} this This node
32507         * @param {Boolean} checked
32508         */
32509         "checkchange":true,
32510         /**
32511         * @event click
32512         * Fires when a node is clicked
32513         * @param {Node} node The node
32514         * @param {Roo.EventObject} e The event object
32515         */
32516         "click":true,
32517         /**
32518         * @event dblclick
32519         * Fires when a node is double clicked
32520         * @param {Node} node The node
32521         * @param {Roo.EventObject} e The event object
32522         */
32523         "dblclick":true,
32524         /**
32525         * @event contextmenu
32526         * Fires when a node is right clicked
32527         * @param {Node} node The node
32528         * @param {Roo.EventObject} e The event object
32529         */
32530         "contextmenu":true,
32531         /**
32532         * @event beforechildrenrendered
32533         * Fires right before the child nodes for a node are rendered
32534         * @param {Node} node The node
32535         */
32536         "beforechildrenrendered":true,
32537         /**
32538         * @event startdrag
32539         * Fires when a node starts being dragged
32540         * @param {Roo.tree.TreePanel} this
32541         * @param {Roo.tree.TreeNode} node
32542         * @param {event} e The raw browser event
32543         */ 
32544        "startdrag" : true,
32545        /**
32546         * @event enddrag
32547         * Fires when a drag operation is complete
32548         * @param {Roo.tree.TreePanel} this
32549         * @param {Roo.tree.TreeNode} node
32550         * @param {event} e The raw browser event
32551         */
32552        "enddrag" : true,
32553        /**
32554         * @event dragdrop
32555         * Fires when a dragged node is dropped on a valid DD target
32556         * @param {Roo.tree.TreePanel} this
32557         * @param {Roo.tree.TreeNode} node
32558         * @param {DD} dd The dd it was dropped on
32559         * @param {event} e The raw browser event
32560         */
32561        "dragdrop" : true,
32562        /**
32563         * @event beforenodedrop
32564         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32565         * passed to handlers has the following properties:<br />
32566         * <ul style="padding:5px;padding-left:16px;">
32567         * <li>tree - The TreePanel</li>
32568         * <li>target - The node being targeted for the drop</li>
32569         * <li>data - The drag data from the drag source</li>
32570         * <li>point - The point of the drop - append, above or below</li>
32571         * <li>source - The drag source</li>
32572         * <li>rawEvent - Raw mouse event</li>
32573         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32574         * to be inserted by setting them on this object.</li>
32575         * <li>cancel - Set this to true to cancel the drop.</li>
32576         * </ul>
32577         * @param {Object} dropEvent
32578         */
32579        "beforenodedrop" : true,
32580        /**
32581         * @event nodedrop
32582         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32583         * passed to handlers has the following properties:<br />
32584         * <ul style="padding:5px;padding-left:16px;">
32585         * <li>tree - The TreePanel</li>
32586         * <li>target - The node being targeted for the drop</li>
32587         * <li>data - The drag data from the drag source</li>
32588         * <li>point - The point of the drop - append, above or below</li>
32589         * <li>source - The drag source</li>
32590         * <li>rawEvent - Raw mouse event</li>
32591         * <li>dropNode - Dropped node(s).</li>
32592         * </ul>
32593         * @param {Object} dropEvent
32594         */
32595        "nodedrop" : true,
32596         /**
32597         * @event nodedragover
32598         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32599         * passed to handlers has the following properties:<br />
32600         * <ul style="padding:5px;padding-left:16px;">
32601         * <li>tree - The TreePanel</li>
32602         * <li>target - The node being targeted for the drop</li>
32603         * <li>data - The drag data from the drag source</li>
32604         * <li>point - The point of the drop - append, above or below</li>
32605         * <li>source - The drag source</li>
32606         * <li>rawEvent - Raw mouse event</li>
32607         * <li>dropNode - Drop node(s) provided by the source.</li>
32608         * <li>cancel - Set this to true to signal drop not allowed.</li>
32609         * </ul>
32610         * @param {Object} dragOverEvent
32611         */
32612        "nodedragover" : true
32613         
32614     });
32615     if(this.singleExpand){
32616        this.on("beforeexpand", this.restrictExpand, this);
32617     }
32618     if (this.editor) {
32619         this.editor.tree = this;
32620         this.editor = Roo.factory(this.editor, Roo.tree);
32621     }
32622     
32623     if (this.selModel) {
32624         this.selModel = Roo.factory(this.selModel, Roo.tree);
32625     }
32626    
32627 };
32628 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32629     rootVisible : true,
32630     animate: Roo.enableFx,
32631     lines : true,
32632     enableDD : false,
32633     hlDrop : Roo.enableFx,
32634   
32635     renderer: false,
32636     
32637     rendererTip: false,
32638     // private
32639     restrictExpand : function(node){
32640         var p = node.parentNode;
32641         if(p){
32642             if(p.expandedChild && p.expandedChild.parentNode == p){
32643                 p.expandedChild.collapse();
32644             }
32645             p.expandedChild = node;
32646         }
32647     },
32648
32649     // private override
32650     setRootNode : function(node){
32651         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32652         if(!this.rootVisible){
32653             node.ui = new Roo.tree.RootTreeNodeUI(node);
32654         }
32655         return node;
32656     },
32657
32658     /**
32659      * Returns the container element for this TreePanel
32660      */
32661     getEl : function(){
32662         return this.el;
32663     },
32664
32665     /**
32666      * Returns the default TreeLoader for this TreePanel
32667      */
32668     getLoader : function(){
32669         return this.loader;
32670     },
32671
32672     /**
32673      * Expand all nodes
32674      */
32675     expandAll : function(){
32676         this.root.expand(true);
32677     },
32678
32679     /**
32680      * Collapse all nodes
32681      */
32682     collapseAll : function(){
32683         this.root.collapse(true);
32684     },
32685
32686     /**
32687      * Returns the selection model used by this TreePanel
32688      */
32689     getSelectionModel : function(){
32690         if(!this.selModel){
32691             this.selModel = new Roo.tree.DefaultSelectionModel();
32692         }
32693         return this.selModel;
32694     },
32695
32696     /**
32697      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32698      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32699      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32700      * @return {Array}
32701      */
32702     getChecked : function(a, startNode){
32703         startNode = startNode || this.root;
32704         var r = [];
32705         var f = function(){
32706             if(this.attributes.checked){
32707                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32708             }
32709         }
32710         startNode.cascade(f);
32711         return r;
32712     },
32713
32714     /**
32715      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32716      * @param {String} path
32717      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32718      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32719      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32720      */
32721     expandPath : function(path, attr, callback){
32722         attr = attr || "id";
32723         var keys = path.split(this.pathSeparator);
32724         var curNode = this.root;
32725         if(curNode.attributes[attr] != keys[1]){ // invalid root
32726             if(callback){
32727                 callback(false, null);
32728             }
32729             return;
32730         }
32731         var index = 1;
32732         var f = function(){
32733             if(++index == keys.length){
32734                 if(callback){
32735                     callback(true, curNode);
32736                 }
32737                 return;
32738             }
32739             var c = curNode.findChild(attr, keys[index]);
32740             if(!c){
32741                 if(callback){
32742                     callback(false, curNode);
32743                 }
32744                 return;
32745             }
32746             curNode = c;
32747             c.expand(false, false, f);
32748         };
32749         curNode.expand(false, false, f);
32750     },
32751
32752     /**
32753      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32754      * @param {String} path
32755      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32756      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32757      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32758      */
32759     selectPath : function(path, attr, callback){
32760         attr = attr || "id";
32761         var keys = path.split(this.pathSeparator);
32762         var v = keys.pop();
32763         if(keys.length > 0){
32764             var f = function(success, node){
32765                 if(success && node){
32766                     var n = node.findChild(attr, v);
32767                     if(n){
32768                         n.select();
32769                         if(callback){
32770                             callback(true, n);
32771                         }
32772                     }else if(callback){
32773                         callback(false, n);
32774                     }
32775                 }else{
32776                     if(callback){
32777                         callback(false, n);
32778                     }
32779                 }
32780             };
32781             this.expandPath(keys.join(this.pathSeparator), attr, f);
32782         }else{
32783             this.root.select();
32784             if(callback){
32785                 callback(true, this.root);
32786             }
32787         }
32788     },
32789
32790     getTreeEl : function(){
32791         return this.el;
32792     },
32793
32794     /**
32795      * Trigger rendering of this TreePanel
32796      */
32797     render : function(){
32798         if (this.innerCt) {
32799             return this; // stop it rendering more than once!!
32800         }
32801         
32802         this.innerCt = this.el.createChild({tag:"ul",
32803                cls:"x-tree-root-ct " +
32804                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32805
32806         if(this.containerScroll){
32807             Roo.dd.ScrollManager.register(this.el);
32808         }
32809         if((this.enableDD || this.enableDrop) && !this.dropZone){
32810            /**
32811             * The dropZone used by this tree if drop is enabled
32812             * @type Roo.tree.TreeDropZone
32813             */
32814              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32815                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32816            });
32817         }
32818         if((this.enableDD || this.enableDrag) && !this.dragZone){
32819            /**
32820             * The dragZone used by this tree if drag is enabled
32821             * @type Roo.tree.TreeDragZone
32822             */
32823             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32824                ddGroup: this.ddGroup || "TreeDD",
32825                scroll: this.ddScroll
32826            });
32827         }
32828         this.getSelectionModel().init(this);
32829         if (!this.root) {
32830             Roo.log("ROOT not set in tree");
32831             return this;
32832         }
32833         this.root.render();
32834         if(!this.rootVisible){
32835             this.root.renderChildren();
32836         }
32837         return this;
32838     }
32839 });/*
32840  * Based on:
32841  * Ext JS Library 1.1.1
32842  * Copyright(c) 2006-2007, Ext JS, LLC.
32843  *
32844  * Originally Released Under LGPL - original licence link has changed is not relivant.
32845  *
32846  * Fork - LGPL
32847  * <script type="text/javascript">
32848  */
32849  
32850
32851 /**
32852  * @class Roo.tree.DefaultSelectionModel
32853  * @extends Roo.util.Observable
32854  * The default single selection for a TreePanel.
32855  * @param {Object} cfg Configuration
32856  */
32857 Roo.tree.DefaultSelectionModel = function(cfg){
32858    this.selNode = null;
32859    
32860    
32861    
32862    this.addEvents({
32863        /**
32864         * @event selectionchange
32865         * Fires when the selected node changes
32866         * @param {DefaultSelectionModel} this
32867         * @param {TreeNode} node the new selection
32868         */
32869        "selectionchange" : true,
32870
32871        /**
32872         * @event beforeselect
32873         * Fires before the selected node changes, return false to cancel the change
32874         * @param {DefaultSelectionModel} this
32875         * @param {TreeNode} node the new selection
32876         * @param {TreeNode} node the old selection
32877         */
32878        "beforeselect" : true
32879    });
32880    
32881     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32882 };
32883
32884 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32885     init : function(tree){
32886         this.tree = tree;
32887         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32888         tree.on("click", this.onNodeClick, this);
32889     },
32890     
32891     onNodeClick : function(node, e){
32892         if (e.ctrlKey && this.selNode == node)  {
32893             this.unselect(node);
32894             return;
32895         }
32896         this.select(node);
32897     },
32898     
32899     /**
32900      * Select a node.
32901      * @param {TreeNode} node The node to select
32902      * @return {TreeNode} The selected node
32903      */
32904     select : function(node){
32905         var last = this.selNode;
32906         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32907             if(last){
32908                 last.ui.onSelectedChange(false);
32909             }
32910             this.selNode = node;
32911             node.ui.onSelectedChange(true);
32912             this.fireEvent("selectionchange", this, node, last);
32913         }
32914         return node;
32915     },
32916     
32917     /**
32918      * Deselect a node.
32919      * @param {TreeNode} node The node to unselect
32920      */
32921     unselect : function(node){
32922         if(this.selNode == node){
32923             this.clearSelections();
32924         }    
32925     },
32926     
32927     /**
32928      * Clear all selections
32929      */
32930     clearSelections : function(){
32931         var n = this.selNode;
32932         if(n){
32933             n.ui.onSelectedChange(false);
32934             this.selNode = null;
32935             this.fireEvent("selectionchange", this, null);
32936         }
32937         return n;
32938     },
32939     
32940     /**
32941      * Get the selected node
32942      * @return {TreeNode} The selected node
32943      */
32944     getSelectedNode : function(){
32945         return this.selNode;    
32946     },
32947     
32948     /**
32949      * Returns true if the node is selected
32950      * @param {TreeNode} node The node to check
32951      * @return {Boolean}
32952      */
32953     isSelected : function(node){
32954         return this.selNode == node;  
32955     },
32956
32957     /**
32958      * Selects the node above the selected node in the tree, intelligently walking the nodes
32959      * @return TreeNode The new selection
32960      */
32961     selectPrevious : function(){
32962         var s = this.selNode || this.lastSelNode;
32963         if(!s){
32964             return null;
32965         }
32966         var ps = s.previousSibling;
32967         if(ps){
32968             if(!ps.isExpanded() || ps.childNodes.length < 1){
32969                 return this.select(ps);
32970             } else{
32971                 var lc = ps.lastChild;
32972                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32973                     lc = lc.lastChild;
32974                 }
32975                 return this.select(lc);
32976             }
32977         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32978             return this.select(s.parentNode);
32979         }
32980         return null;
32981     },
32982
32983     /**
32984      * Selects the node above the selected node in the tree, intelligently walking the nodes
32985      * @return TreeNode The new selection
32986      */
32987     selectNext : function(){
32988         var s = this.selNode || this.lastSelNode;
32989         if(!s){
32990             return null;
32991         }
32992         if(s.firstChild && s.isExpanded()){
32993              return this.select(s.firstChild);
32994          }else if(s.nextSibling){
32995              return this.select(s.nextSibling);
32996          }else if(s.parentNode){
32997             var newS = null;
32998             s.parentNode.bubble(function(){
32999                 if(this.nextSibling){
33000                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
33001                     return false;
33002                 }
33003             });
33004             return newS;
33005          }
33006         return null;
33007     },
33008
33009     onKeyDown : function(e){
33010         var s = this.selNode || this.lastSelNode;
33011         // undesirable, but required
33012         var sm = this;
33013         if(!s){
33014             return;
33015         }
33016         var k = e.getKey();
33017         switch(k){
33018              case e.DOWN:
33019                  e.stopEvent();
33020                  this.selectNext();
33021              break;
33022              case e.UP:
33023                  e.stopEvent();
33024                  this.selectPrevious();
33025              break;
33026              case e.RIGHT:
33027                  e.preventDefault();
33028                  if(s.hasChildNodes()){
33029                      if(!s.isExpanded()){
33030                          s.expand();
33031                      }else if(s.firstChild){
33032                          this.select(s.firstChild, e);
33033                      }
33034                  }
33035              break;
33036              case e.LEFT:
33037                  e.preventDefault();
33038                  if(s.hasChildNodes() && s.isExpanded()){
33039                      s.collapse();
33040                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33041                      this.select(s.parentNode, e);
33042                  }
33043              break;
33044         };
33045     }
33046 });
33047
33048 /**
33049  * @class Roo.tree.MultiSelectionModel
33050  * @extends Roo.util.Observable
33051  * Multi selection for a TreePanel.
33052  * @param {Object} cfg Configuration
33053  */
33054 Roo.tree.MultiSelectionModel = function(){
33055    this.selNodes = [];
33056    this.selMap = {};
33057    this.addEvents({
33058        /**
33059         * @event selectionchange
33060         * Fires when the selected nodes change
33061         * @param {MultiSelectionModel} this
33062         * @param {Array} nodes Array of the selected nodes
33063         */
33064        "selectionchange" : true
33065    });
33066    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33067    
33068 };
33069
33070 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33071     init : function(tree){
33072         this.tree = tree;
33073         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33074         tree.on("click", this.onNodeClick, this);
33075     },
33076     
33077     onNodeClick : function(node, e){
33078         this.select(node, e, e.ctrlKey);
33079     },
33080     
33081     /**
33082      * Select a node.
33083      * @param {TreeNode} node The node to select
33084      * @param {EventObject} e (optional) An event associated with the selection
33085      * @param {Boolean} keepExisting True to retain existing selections
33086      * @return {TreeNode} The selected node
33087      */
33088     select : function(node, e, keepExisting){
33089         if(keepExisting !== true){
33090             this.clearSelections(true);
33091         }
33092         if(this.isSelected(node)){
33093             this.lastSelNode = node;
33094             return node;
33095         }
33096         this.selNodes.push(node);
33097         this.selMap[node.id] = node;
33098         this.lastSelNode = node;
33099         node.ui.onSelectedChange(true);
33100         this.fireEvent("selectionchange", this, this.selNodes);
33101         return node;
33102     },
33103     
33104     /**
33105      * Deselect a node.
33106      * @param {TreeNode} node The node to unselect
33107      */
33108     unselect : function(node){
33109         if(this.selMap[node.id]){
33110             node.ui.onSelectedChange(false);
33111             var sn = this.selNodes;
33112             var index = -1;
33113             if(sn.indexOf){
33114                 index = sn.indexOf(node);
33115             }else{
33116                 for(var i = 0, len = sn.length; i < len; i++){
33117                     if(sn[i] == node){
33118                         index = i;
33119                         break;
33120                     }
33121                 }
33122             }
33123             if(index != -1){
33124                 this.selNodes.splice(index, 1);
33125             }
33126             delete this.selMap[node.id];
33127             this.fireEvent("selectionchange", this, this.selNodes);
33128         }
33129     },
33130     
33131     /**
33132      * Clear all selections
33133      */
33134     clearSelections : function(suppressEvent){
33135         var sn = this.selNodes;
33136         if(sn.length > 0){
33137             for(var i = 0, len = sn.length; i < len; i++){
33138                 sn[i].ui.onSelectedChange(false);
33139             }
33140             this.selNodes = [];
33141             this.selMap = {};
33142             if(suppressEvent !== true){
33143                 this.fireEvent("selectionchange", this, this.selNodes);
33144             }
33145         }
33146     },
33147     
33148     /**
33149      * Returns true if the node is selected
33150      * @param {TreeNode} node The node to check
33151      * @return {Boolean}
33152      */
33153     isSelected : function(node){
33154         return this.selMap[node.id] ? true : false;  
33155     },
33156     
33157     /**
33158      * Returns an array of the selected nodes
33159      * @return {Array}
33160      */
33161     getSelectedNodes : function(){
33162         return this.selNodes;    
33163     },
33164
33165     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33166
33167     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33168
33169     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33170 });/*
33171  * Based on:
33172  * Ext JS Library 1.1.1
33173  * Copyright(c) 2006-2007, Ext JS, LLC.
33174  *
33175  * Originally Released Under LGPL - original licence link has changed is not relivant.
33176  *
33177  * Fork - LGPL
33178  * <script type="text/javascript">
33179  */
33180  
33181 /**
33182  * @class Roo.tree.TreeNode
33183  * @extends Roo.data.Node
33184  * @cfg {String} text The text for this node
33185  * @cfg {Boolean} expanded true to start the node expanded
33186  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33187  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33188  * @cfg {Boolean} disabled true to start the node disabled
33189  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33190  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33191  * @cfg {String} cls A css class to be added to the node
33192  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33193  * @cfg {String} href URL of the link used for the node (defaults to #)
33194  * @cfg {String} hrefTarget target frame for the link
33195  * @cfg {String} qtip An Ext QuickTip for the node
33196  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33197  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33198  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33199  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33200  * (defaults to undefined with no checkbox rendered)
33201  * @constructor
33202  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33203  */
33204 Roo.tree.TreeNode = function(attributes){
33205     attributes = attributes || {};
33206     if(typeof attributes == "string"){
33207         attributes = {text: attributes};
33208     }
33209     this.childrenRendered = false;
33210     this.rendered = false;
33211     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33212     this.expanded = attributes.expanded === true;
33213     this.isTarget = attributes.isTarget !== false;
33214     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33215     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33216
33217     /**
33218      * Read-only. The text for this node. To change it use setText().
33219      * @type String
33220      */
33221     this.text = attributes.text;
33222     /**
33223      * True if this node is disabled.
33224      * @type Boolean
33225      */
33226     this.disabled = attributes.disabled === true;
33227
33228     this.addEvents({
33229         /**
33230         * @event textchange
33231         * Fires when the text for this node is changed
33232         * @param {Node} this This node
33233         * @param {String} text The new text
33234         * @param {String} oldText The old text
33235         */
33236         "textchange" : true,
33237         /**
33238         * @event beforeexpand
33239         * Fires before this node is expanded, return false to cancel.
33240         * @param {Node} this This node
33241         * @param {Boolean} deep
33242         * @param {Boolean} anim
33243         */
33244         "beforeexpand" : true,
33245         /**
33246         * @event beforecollapse
33247         * Fires before this node is collapsed, return false to cancel.
33248         * @param {Node} this This node
33249         * @param {Boolean} deep
33250         * @param {Boolean} anim
33251         */
33252         "beforecollapse" : true,
33253         /**
33254         * @event expand
33255         * Fires when this node is expanded
33256         * @param {Node} this This node
33257         */
33258         "expand" : true,
33259         /**
33260         * @event disabledchange
33261         * Fires when the disabled status of this node changes
33262         * @param {Node} this This node
33263         * @param {Boolean} disabled
33264         */
33265         "disabledchange" : true,
33266         /**
33267         * @event collapse
33268         * Fires when this node is collapsed
33269         * @param {Node} this This node
33270         */
33271         "collapse" : true,
33272         /**
33273         * @event beforeclick
33274         * Fires before click processing. Return false to cancel the default action.
33275         * @param {Node} this This node
33276         * @param {Roo.EventObject} e The event object
33277         */
33278         "beforeclick":true,
33279         /**
33280         * @event checkchange
33281         * Fires when a node with a checkbox's checked property changes
33282         * @param {Node} this This node
33283         * @param {Boolean} checked
33284         */
33285         "checkchange":true,
33286         /**
33287         * @event click
33288         * Fires when this node is clicked
33289         * @param {Node} this This node
33290         * @param {Roo.EventObject} e The event object
33291         */
33292         "click":true,
33293         /**
33294         * @event dblclick
33295         * Fires when this node is double clicked
33296         * @param {Node} this This node
33297         * @param {Roo.EventObject} e The event object
33298         */
33299         "dblclick":true,
33300         /**
33301         * @event contextmenu
33302         * Fires when this node is right clicked
33303         * @param {Node} this This node
33304         * @param {Roo.EventObject} e The event object
33305         */
33306         "contextmenu":true,
33307         /**
33308         * @event beforechildrenrendered
33309         * Fires right before the child nodes for this node are rendered
33310         * @param {Node} this This node
33311         */
33312         "beforechildrenrendered":true
33313     });
33314
33315     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33316
33317     /**
33318      * Read-only. The UI for this node
33319      * @type TreeNodeUI
33320      */
33321     this.ui = new uiClass(this);
33322     
33323     // finally support items[]
33324     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33325         return;
33326     }
33327     
33328     
33329     Roo.each(this.attributes.items, function(c) {
33330         this.appendChild(Roo.factory(c,Roo.Tree));
33331     }, this);
33332     delete this.attributes.items;
33333     
33334     
33335     
33336 };
33337 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33338     preventHScroll: true,
33339     /**
33340      * Returns true if this node is expanded
33341      * @return {Boolean}
33342      */
33343     isExpanded : function(){
33344         return this.expanded;
33345     },
33346
33347     /**
33348      * Returns the UI object for this node
33349      * @return {TreeNodeUI}
33350      */
33351     getUI : function(){
33352         return this.ui;
33353     },
33354
33355     // private override
33356     setFirstChild : function(node){
33357         var of = this.firstChild;
33358         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33359         if(this.childrenRendered && of && node != of){
33360             of.renderIndent(true, true);
33361         }
33362         if(this.rendered){
33363             this.renderIndent(true, true);
33364         }
33365     },
33366
33367     // private override
33368     setLastChild : function(node){
33369         var ol = this.lastChild;
33370         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33371         if(this.childrenRendered && ol && node != ol){
33372             ol.renderIndent(true, true);
33373         }
33374         if(this.rendered){
33375             this.renderIndent(true, true);
33376         }
33377     },
33378
33379     // these methods are overridden to provide lazy rendering support
33380     // private override
33381     appendChild : function()
33382     {
33383         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33384         if(node && this.childrenRendered){
33385             node.render();
33386         }
33387         this.ui.updateExpandIcon();
33388         return node;
33389     },
33390
33391     // private override
33392     removeChild : function(node){
33393         this.ownerTree.getSelectionModel().unselect(node);
33394         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33395         // if it's been rendered remove dom node
33396         if(this.childrenRendered){
33397             node.ui.remove();
33398         }
33399         if(this.childNodes.length < 1){
33400             this.collapse(false, false);
33401         }else{
33402             this.ui.updateExpandIcon();
33403         }
33404         if(!this.firstChild) {
33405             this.childrenRendered = false;
33406         }
33407         return node;
33408     },
33409
33410     // private override
33411     insertBefore : function(node, refNode){
33412         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33413         if(newNode && refNode && this.childrenRendered){
33414             node.render();
33415         }
33416         this.ui.updateExpandIcon();
33417         return newNode;
33418     },
33419
33420     /**
33421      * Sets the text for this node
33422      * @param {String} text
33423      */
33424     setText : function(text){
33425         var oldText = this.text;
33426         this.text = text;
33427         this.attributes.text = text;
33428         if(this.rendered){ // event without subscribing
33429             this.ui.onTextChange(this, text, oldText);
33430         }
33431         this.fireEvent("textchange", this, text, oldText);
33432     },
33433
33434     /**
33435      * Triggers selection of this node
33436      */
33437     select : function(){
33438         this.getOwnerTree().getSelectionModel().select(this);
33439     },
33440
33441     /**
33442      * Triggers deselection of this node
33443      */
33444     unselect : function(){
33445         this.getOwnerTree().getSelectionModel().unselect(this);
33446     },
33447
33448     /**
33449      * Returns true if this node is selected
33450      * @return {Boolean}
33451      */
33452     isSelected : function(){
33453         return this.getOwnerTree().getSelectionModel().isSelected(this);
33454     },
33455
33456     /**
33457      * Expand this node.
33458      * @param {Boolean} deep (optional) True to expand all children as well
33459      * @param {Boolean} anim (optional) false to cancel the default animation
33460      * @param {Function} callback (optional) A callback to be called when
33461      * expanding this node completes (does not wait for deep expand to complete).
33462      * Called with 1 parameter, this node.
33463      */
33464     expand : function(deep, anim, callback){
33465         if(!this.expanded){
33466             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33467                 return;
33468             }
33469             if(!this.childrenRendered){
33470                 this.renderChildren();
33471             }
33472             this.expanded = true;
33473             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33474                 this.ui.animExpand(function(){
33475                     this.fireEvent("expand", this);
33476                     if(typeof callback == "function"){
33477                         callback(this);
33478                     }
33479                     if(deep === true){
33480                         this.expandChildNodes(true);
33481                     }
33482                 }.createDelegate(this));
33483                 return;
33484             }else{
33485                 this.ui.expand();
33486                 this.fireEvent("expand", this);
33487                 if(typeof callback == "function"){
33488                     callback(this);
33489                 }
33490             }
33491         }else{
33492            if(typeof callback == "function"){
33493                callback(this);
33494            }
33495         }
33496         if(deep === true){
33497             this.expandChildNodes(true);
33498         }
33499     },
33500
33501     isHiddenRoot : function(){
33502         return this.isRoot && !this.getOwnerTree().rootVisible;
33503     },
33504
33505     /**
33506      * Collapse this node.
33507      * @param {Boolean} deep (optional) True to collapse all children as well
33508      * @param {Boolean} anim (optional) false to cancel the default animation
33509      */
33510     collapse : function(deep, anim){
33511         if(this.expanded && !this.isHiddenRoot()){
33512             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33513                 return;
33514             }
33515             this.expanded = false;
33516             if((this.getOwnerTree().animate && anim !== false) || anim){
33517                 this.ui.animCollapse(function(){
33518                     this.fireEvent("collapse", this);
33519                     if(deep === true){
33520                         this.collapseChildNodes(true);
33521                     }
33522                 }.createDelegate(this));
33523                 return;
33524             }else{
33525                 this.ui.collapse();
33526                 this.fireEvent("collapse", this);
33527             }
33528         }
33529         if(deep === true){
33530             var cs = this.childNodes;
33531             for(var i = 0, len = cs.length; i < len; i++) {
33532                 cs[i].collapse(true, false);
33533             }
33534         }
33535     },
33536
33537     // private
33538     delayedExpand : function(delay){
33539         if(!this.expandProcId){
33540             this.expandProcId = this.expand.defer(delay, this);
33541         }
33542     },
33543
33544     // private
33545     cancelExpand : function(){
33546         if(this.expandProcId){
33547             clearTimeout(this.expandProcId);
33548         }
33549         this.expandProcId = false;
33550     },
33551
33552     /**
33553      * Toggles expanded/collapsed state of the node
33554      */
33555     toggle : function(){
33556         if(this.expanded){
33557             this.collapse();
33558         }else{
33559             this.expand();
33560         }
33561     },
33562
33563     /**
33564      * Ensures all parent nodes are expanded
33565      */
33566     ensureVisible : function(callback){
33567         var tree = this.getOwnerTree();
33568         tree.expandPath(this.parentNode.getPath(), false, function(){
33569             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33570             Roo.callback(callback);
33571         }.createDelegate(this));
33572     },
33573
33574     /**
33575      * Expand all child nodes
33576      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33577      */
33578     expandChildNodes : function(deep){
33579         var cs = this.childNodes;
33580         for(var i = 0, len = cs.length; i < len; i++) {
33581                 cs[i].expand(deep);
33582         }
33583     },
33584
33585     /**
33586      * Collapse all child nodes
33587      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33588      */
33589     collapseChildNodes : function(deep){
33590         var cs = this.childNodes;
33591         for(var i = 0, len = cs.length; i < len; i++) {
33592                 cs[i].collapse(deep);
33593         }
33594     },
33595
33596     /**
33597      * Disables this node
33598      */
33599     disable : function(){
33600         this.disabled = true;
33601         this.unselect();
33602         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33603             this.ui.onDisableChange(this, true);
33604         }
33605         this.fireEvent("disabledchange", this, true);
33606     },
33607
33608     /**
33609      * Enables this node
33610      */
33611     enable : function(){
33612         this.disabled = false;
33613         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33614             this.ui.onDisableChange(this, false);
33615         }
33616         this.fireEvent("disabledchange", this, false);
33617     },
33618
33619     // private
33620     renderChildren : function(suppressEvent){
33621         if(suppressEvent !== false){
33622             this.fireEvent("beforechildrenrendered", this);
33623         }
33624         var cs = this.childNodes;
33625         for(var i = 0, len = cs.length; i < len; i++){
33626             cs[i].render(true);
33627         }
33628         this.childrenRendered = true;
33629     },
33630
33631     // private
33632     sort : function(fn, scope){
33633         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33634         if(this.childrenRendered){
33635             var cs = this.childNodes;
33636             for(var i = 0, len = cs.length; i < len; i++){
33637                 cs[i].render(true);
33638             }
33639         }
33640     },
33641
33642     // private
33643     render : function(bulkRender){
33644         this.ui.render(bulkRender);
33645         if(!this.rendered){
33646             this.rendered = true;
33647             if(this.expanded){
33648                 this.expanded = false;
33649                 this.expand(false, false);
33650             }
33651         }
33652     },
33653
33654     // private
33655     renderIndent : function(deep, refresh){
33656         if(refresh){
33657             this.ui.childIndent = null;
33658         }
33659         this.ui.renderIndent();
33660         if(deep === true && this.childrenRendered){
33661             var cs = this.childNodes;
33662             for(var i = 0, len = cs.length; i < len; i++){
33663                 cs[i].renderIndent(true, refresh);
33664             }
33665         }
33666     }
33667 });/*
33668  * Based on:
33669  * Ext JS Library 1.1.1
33670  * Copyright(c) 2006-2007, Ext JS, LLC.
33671  *
33672  * Originally Released Under LGPL - original licence link has changed is not relivant.
33673  *
33674  * Fork - LGPL
33675  * <script type="text/javascript">
33676  */
33677  
33678 /**
33679  * @class Roo.tree.AsyncTreeNode
33680  * @extends Roo.tree.TreeNode
33681  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33682  * @constructor
33683  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33684  */
33685  Roo.tree.AsyncTreeNode = function(config){
33686     this.loaded = false;
33687     this.loading = false;
33688     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33689     /**
33690     * @event beforeload
33691     * Fires before this node is loaded, return false to cancel
33692     * @param {Node} this This node
33693     */
33694     this.addEvents({'beforeload':true, 'load': true});
33695     /**
33696     * @event load
33697     * Fires when this node is loaded
33698     * @param {Node} this This node
33699     */
33700     /**
33701      * The loader used by this node (defaults to using the tree's defined loader)
33702      * @type TreeLoader
33703      * @property loader
33704      */
33705 };
33706 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33707     expand : function(deep, anim, callback){
33708         if(this.loading){ // if an async load is already running, waiting til it's done
33709             var timer;
33710             var f = function(){
33711                 if(!this.loading){ // done loading
33712                     clearInterval(timer);
33713                     this.expand(deep, anim, callback);
33714                 }
33715             }.createDelegate(this);
33716             timer = setInterval(f, 200);
33717             return;
33718         }
33719         if(!this.loaded){
33720             if(this.fireEvent("beforeload", this) === false){
33721                 return;
33722             }
33723             this.loading = true;
33724             this.ui.beforeLoad(this);
33725             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33726             if(loader){
33727                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33728                 return;
33729             }
33730         }
33731         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33732     },
33733     
33734     /**
33735      * Returns true if this node is currently loading
33736      * @return {Boolean}
33737      */
33738     isLoading : function(){
33739         return this.loading;  
33740     },
33741     
33742     loadComplete : function(deep, anim, callback){
33743         this.loading = false;
33744         this.loaded = true;
33745         this.ui.afterLoad(this);
33746         this.fireEvent("load", this);
33747         this.expand(deep, anim, callback);
33748     },
33749     
33750     /**
33751      * Returns true if this node has been loaded
33752      * @return {Boolean}
33753      */
33754     isLoaded : function(){
33755         return this.loaded;
33756     },
33757     
33758     hasChildNodes : function(){
33759         if(!this.isLeaf() && !this.loaded){
33760             return true;
33761         }else{
33762             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33763         }
33764     },
33765
33766     /**
33767      * Trigger a reload for this node
33768      * @param {Function} callback
33769      */
33770     reload : function(callback){
33771         this.collapse(false, false);
33772         while(this.firstChild){
33773             this.removeChild(this.firstChild);
33774         }
33775         this.childrenRendered = false;
33776         this.loaded = false;
33777         if(this.isHiddenRoot()){
33778             this.expanded = false;
33779         }
33780         this.expand(false, false, callback);
33781     }
33782 });/*
33783  * Based on:
33784  * Ext JS Library 1.1.1
33785  * Copyright(c) 2006-2007, Ext JS, LLC.
33786  *
33787  * Originally Released Under LGPL - original licence link has changed is not relivant.
33788  *
33789  * Fork - LGPL
33790  * <script type="text/javascript">
33791  */
33792  
33793 /**
33794  * @class Roo.tree.TreeNodeUI
33795  * @constructor
33796  * @param {Object} node The node to render
33797  * The TreeNode UI implementation is separate from the
33798  * tree implementation. Unless you are customizing the tree UI,
33799  * you should never have to use this directly.
33800  */
33801 Roo.tree.TreeNodeUI = function(node){
33802     this.node = node;
33803     this.rendered = false;
33804     this.animating = false;
33805     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33806 };
33807
33808 Roo.tree.TreeNodeUI.prototype = {
33809     removeChild : function(node){
33810         if(this.rendered){
33811             this.ctNode.removeChild(node.ui.getEl());
33812         }
33813     },
33814
33815     beforeLoad : function(){
33816          this.addClass("x-tree-node-loading");
33817     },
33818
33819     afterLoad : function(){
33820          this.removeClass("x-tree-node-loading");
33821     },
33822
33823     onTextChange : function(node, text, oldText){
33824         if(this.rendered){
33825             this.textNode.innerHTML = text;
33826         }
33827     },
33828
33829     onDisableChange : function(node, state){
33830         this.disabled = state;
33831         if(state){
33832             this.addClass("x-tree-node-disabled");
33833         }else{
33834             this.removeClass("x-tree-node-disabled");
33835         }
33836     },
33837
33838     onSelectedChange : function(state){
33839         if(state){
33840             this.focus();
33841             this.addClass("x-tree-selected");
33842         }else{
33843             //this.blur();
33844             this.removeClass("x-tree-selected");
33845         }
33846     },
33847
33848     onMove : function(tree, node, oldParent, newParent, index, refNode){
33849         this.childIndent = null;
33850         if(this.rendered){
33851             var targetNode = newParent.ui.getContainer();
33852             if(!targetNode){//target not rendered
33853                 this.holder = document.createElement("div");
33854                 this.holder.appendChild(this.wrap);
33855                 return;
33856             }
33857             var insertBefore = refNode ? refNode.ui.getEl() : null;
33858             if(insertBefore){
33859                 targetNode.insertBefore(this.wrap, insertBefore);
33860             }else{
33861                 targetNode.appendChild(this.wrap);
33862             }
33863             this.node.renderIndent(true);
33864         }
33865     },
33866
33867     addClass : function(cls){
33868         if(this.elNode){
33869             Roo.fly(this.elNode).addClass(cls);
33870         }
33871     },
33872
33873     removeClass : function(cls){
33874         if(this.elNode){
33875             Roo.fly(this.elNode).removeClass(cls);
33876         }
33877     },
33878
33879     remove : function(){
33880         if(this.rendered){
33881             this.holder = document.createElement("div");
33882             this.holder.appendChild(this.wrap);
33883         }
33884     },
33885
33886     fireEvent : function(){
33887         return this.node.fireEvent.apply(this.node, arguments);
33888     },
33889
33890     initEvents : function(){
33891         this.node.on("move", this.onMove, this);
33892         var E = Roo.EventManager;
33893         var a = this.anchor;
33894
33895         var el = Roo.fly(a, '_treeui');
33896
33897         if(Roo.isOpera){ // opera render bug ignores the CSS
33898             el.setStyle("text-decoration", "none");
33899         }
33900
33901         el.on("click", this.onClick, this);
33902         el.on("dblclick", this.onDblClick, this);
33903
33904         if(this.checkbox){
33905             Roo.EventManager.on(this.checkbox,
33906                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33907         }
33908
33909         el.on("contextmenu", this.onContextMenu, this);
33910
33911         var icon = Roo.fly(this.iconNode);
33912         icon.on("click", this.onClick, this);
33913         icon.on("dblclick", this.onDblClick, this);
33914         icon.on("contextmenu", this.onContextMenu, this);
33915         E.on(this.ecNode, "click", this.ecClick, this, true);
33916
33917         if(this.node.disabled){
33918             this.addClass("x-tree-node-disabled");
33919         }
33920         if(this.node.hidden){
33921             this.addClass("x-tree-node-disabled");
33922         }
33923         var ot = this.node.getOwnerTree();
33924         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33925         if(dd && (!this.node.isRoot || ot.rootVisible)){
33926             Roo.dd.Registry.register(this.elNode, {
33927                 node: this.node,
33928                 handles: this.getDDHandles(),
33929                 isHandle: false
33930             });
33931         }
33932     },
33933
33934     getDDHandles : function(){
33935         return [this.iconNode, this.textNode];
33936     },
33937
33938     hide : function(){
33939         if(this.rendered){
33940             this.wrap.style.display = "none";
33941         }
33942     },
33943
33944     show : function(){
33945         if(this.rendered){
33946             this.wrap.style.display = "";
33947         }
33948     },
33949
33950     onContextMenu : function(e){
33951         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33952             e.preventDefault();
33953             this.focus();
33954             this.fireEvent("contextmenu", this.node, e);
33955         }
33956     },
33957
33958     onClick : function(e){
33959         if(this.dropping){
33960             e.stopEvent();
33961             return;
33962         }
33963         if(this.fireEvent("beforeclick", this.node, e) !== false){
33964             if(!this.disabled && this.node.attributes.href){
33965                 this.fireEvent("click", this.node, e);
33966                 return;
33967             }
33968             e.preventDefault();
33969             if(this.disabled){
33970                 return;
33971             }
33972
33973             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33974                 this.node.toggle();
33975             }
33976
33977             this.fireEvent("click", this.node, e);
33978         }else{
33979             e.stopEvent();
33980         }
33981     },
33982
33983     onDblClick : function(e){
33984         e.preventDefault();
33985         if(this.disabled){
33986             return;
33987         }
33988         if(this.checkbox){
33989             this.toggleCheck();
33990         }
33991         if(!this.animating && this.node.hasChildNodes()){
33992             this.node.toggle();
33993         }
33994         this.fireEvent("dblclick", this.node, e);
33995     },
33996
33997     onCheckChange : function(){
33998         var checked = this.checkbox.checked;
33999         this.node.attributes.checked = checked;
34000         this.fireEvent('checkchange', this.node, checked);
34001     },
34002
34003     ecClick : function(e){
34004         if(!this.animating && this.node.hasChildNodes()){
34005             this.node.toggle();
34006         }
34007     },
34008
34009     startDrop : function(){
34010         this.dropping = true;
34011     },
34012
34013     // delayed drop so the click event doesn't get fired on a drop
34014     endDrop : function(){
34015        setTimeout(function(){
34016            this.dropping = false;
34017        }.createDelegate(this), 50);
34018     },
34019
34020     expand : function(){
34021         this.updateExpandIcon();
34022         this.ctNode.style.display = "";
34023     },
34024
34025     focus : function(){
34026         if(!this.node.preventHScroll){
34027             try{this.anchor.focus();
34028             }catch(e){}
34029         }else if(!Roo.isIE){
34030             try{
34031                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34032                 var l = noscroll.scrollLeft;
34033                 this.anchor.focus();
34034                 noscroll.scrollLeft = l;
34035             }catch(e){}
34036         }
34037     },
34038
34039     toggleCheck : function(value){
34040         var cb = this.checkbox;
34041         if(cb){
34042             cb.checked = (value === undefined ? !cb.checked : value);
34043         }
34044     },
34045
34046     blur : function(){
34047         try{
34048             this.anchor.blur();
34049         }catch(e){}
34050     },
34051
34052     animExpand : function(callback){
34053         var ct = Roo.get(this.ctNode);
34054         ct.stopFx();
34055         if(!this.node.hasChildNodes()){
34056             this.updateExpandIcon();
34057             this.ctNode.style.display = "";
34058             Roo.callback(callback);
34059             return;
34060         }
34061         this.animating = true;
34062         this.updateExpandIcon();
34063
34064         ct.slideIn('t', {
34065            callback : function(){
34066                this.animating = false;
34067                Roo.callback(callback);
34068             },
34069             scope: this,
34070             duration: this.node.ownerTree.duration || .25
34071         });
34072     },
34073
34074     highlight : function(){
34075         var tree = this.node.getOwnerTree();
34076         Roo.fly(this.wrap).highlight(
34077             tree.hlColor || "C3DAF9",
34078             {endColor: tree.hlBaseColor}
34079         );
34080     },
34081
34082     collapse : function(){
34083         this.updateExpandIcon();
34084         this.ctNode.style.display = "none";
34085     },
34086
34087     animCollapse : function(callback){
34088         var ct = Roo.get(this.ctNode);
34089         ct.enableDisplayMode('block');
34090         ct.stopFx();
34091
34092         this.animating = true;
34093         this.updateExpandIcon();
34094
34095         ct.slideOut('t', {
34096             callback : function(){
34097                this.animating = false;
34098                Roo.callback(callback);
34099             },
34100             scope: this,
34101             duration: this.node.ownerTree.duration || .25
34102         });
34103     },
34104
34105     getContainer : function(){
34106         return this.ctNode;
34107     },
34108
34109     getEl : function(){
34110         return this.wrap;
34111     },
34112
34113     appendDDGhost : function(ghostNode){
34114         ghostNode.appendChild(this.elNode.cloneNode(true));
34115     },
34116
34117     getDDRepairXY : function(){
34118         return Roo.lib.Dom.getXY(this.iconNode);
34119     },
34120
34121     onRender : function(){
34122         this.render();
34123     },
34124
34125     render : function(bulkRender){
34126         var n = this.node, a = n.attributes;
34127         var targetNode = n.parentNode ?
34128               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34129
34130         if(!this.rendered){
34131             this.rendered = true;
34132
34133             this.renderElements(n, a, targetNode, bulkRender);
34134
34135             if(a.qtip){
34136                if(this.textNode.setAttributeNS){
34137                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34138                    if(a.qtipTitle){
34139                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34140                    }
34141                }else{
34142                    this.textNode.setAttribute("ext:qtip", a.qtip);
34143                    if(a.qtipTitle){
34144                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34145                    }
34146                }
34147             }else if(a.qtipCfg){
34148                 a.qtipCfg.target = Roo.id(this.textNode);
34149                 Roo.QuickTips.register(a.qtipCfg);
34150             }
34151             this.initEvents();
34152             if(!this.node.expanded){
34153                 this.updateExpandIcon();
34154             }
34155         }else{
34156             if(bulkRender === true) {
34157                 targetNode.appendChild(this.wrap);
34158             }
34159         }
34160     },
34161
34162     renderElements : function(n, a, targetNode, bulkRender)
34163     {
34164         // add some indent caching, this helps performance when rendering a large tree
34165         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34166         var t = n.getOwnerTree();
34167         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34168         if (typeof(n.attributes.html) != 'undefined') {
34169             txt = n.attributes.html;
34170         }
34171         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34172         var cb = typeof a.checked == 'boolean';
34173         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34174         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34175             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34176             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34177             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34178             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34179             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34180              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34181                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34182             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34183             "</li>"];
34184
34185         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34186             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34187                                 n.nextSibling.ui.getEl(), buf.join(""));
34188         }else{
34189             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34190         }
34191
34192         this.elNode = this.wrap.childNodes[0];
34193         this.ctNode = this.wrap.childNodes[1];
34194         var cs = this.elNode.childNodes;
34195         this.indentNode = cs[0];
34196         this.ecNode = cs[1];
34197         this.iconNode = cs[2];
34198         var index = 3;
34199         if(cb){
34200             this.checkbox = cs[3];
34201             index++;
34202         }
34203         this.anchor = cs[index];
34204         this.textNode = cs[index].firstChild;
34205     },
34206
34207     getAnchor : function(){
34208         return this.anchor;
34209     },
34210
34211     getTextEl : function(){
34212         return this.textNode;
34213     },
34214
34215     getIconEl : function(){
34216         return this.iconNode;
34217     },
34218
34219     isChecked : function(){
34220         return this.checkbox ? this.checkbox.checked : false;
34221     },
34222
34223     updateExpandIcon : function(){
34224         if(this.rendered){
34225             var n = this.node, c1, c2;
34226             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34227             var hasChild = n.hasChildNodes();
34228             if(hasChild){
34229                 if(n.expanded){
34230                     cls += "-minus";
34231                     c1 = "x-tree-node-collapsed";
34232                     c2 = "x-tree-node-expanded";
34233                 }else{
34234                     cls += "-plus";
34235                     c1 = "x-tree-node-expanded";
34236                     c2 = "x-tree-node-collapsed";
34237                 }
34238                 if(this.wasLeaf){
34239                     this.removeClass("x-tree-node-leaf");
34240                     this.wasLeaf = false;
34241                 }
34242                 if(this.c1 != c1 || this.c2 != c2){
34243                     Roo.fly(this.elNode).replaceClass(c1, c2);
34244                     this.c1 = c1; this.c2 = c2;
34245                 }
34246             }else{
34247                 // this changes non-leafs into leafs if they have no children.
34248                 // it's not very rational behaviour..
34249                 
34250                 if(!this.wasLeaf && this.node.leaf){
34251                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34252                     delete this.c1;
34253                     delete this.c2;
34254                     this.wasLeaf = true;
34255                 }
34256             }
34257             var ecc = "x-tree-ec-icon "+cls;
34258             if(this.ecc != ecc){
34259                 this.ecNode.className = ecc;
34260                 this.ecc = ecc;
34261             }
34262         }
34263     },
34264
34265     getChildIndent : function(){
34266         if(!this.childIndent){
34267             var buf = [];
34268             var p = this.node;
34269             while(p){
34270                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34271                     if(!p.isLast()) {
34272                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34273                     } else {
34274                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34275                     }
34276                 }
34277                 p = p.parentNode;
34278             }
34279             this.childIndent = buf.join("");
34280         }
34281         return this.childIndent;
34282     },
34283
34284     renderIndent : function(){
34285         if(this.rendered){
34286             var indent = "";
34287             var p = this.node.parentNode;
34288             if(p){
34289                 indent = p.ui.getChildIndent();
34290             }
34291             if(this.indentMarkup != indent){ // don't rerender if not required
34292                 this.indentNode.innerHTML = indent;
34293                 this.indentMarkup = indent;
34294             }
34295             this.updateExpandIcon();
34296         }
34297     }
34298 };
34299
34300 Roo.tree.RootTreeNodeUI = function(){
34301     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34302 };
34303 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34304     render : function(){
34305         if(!this.rendered){
34306             var targetNode = this.node.ownerTree.innerCt.dom;
34307             this.node.expanded = true;
34308             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34309             this.wrap = this.ctNode = targetNode.firstChild;
34310         }
34311     },
34312     collapse : function(){
34313     },
34314     expand : function(){
34315     }
34316 });/*
34317  * Based on:
34318  * Ext JS Library 1.1.1
34319  * Copyright(c) 2006-2007, Ext JS, LLC.
34320  *
34321  * Originally Released Under LGPL - original licence link has changed is not relivant.
34322  *
34323  * Fork - LGPL
34324  * <script type="text/javascript">
34325  */
34326 /**
34327  * @class Roo.tree.TreeLoader
34328  * @extends Roo.util.Observable
34329  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34330  * nodes from a specified URL. The response must be a javascript Array definition
34331  * who's elements are node definition objects. eg:
34332  * <pre><code>
34333 {  success : true,
34334    data :      [
34335    
34336     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34337     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34338     ]
34339 }
34340
34341
34342 </code></pre>
34343  * <br><br>
34344  * The old style respose with just an array is still supported, but not recommended.
34345  * <br><br>
34346  *
34347  * A server request is sent, and child nodes are loaded only when a node is expanded.
34348  * The loading node's id is passed to the server under the parameter name "node" to
34349  * enable the server to produce the correct child nodes.
34350  * <br><br>
34351  * To pass extra parameters, an event handler may be attached to the "beforeload"
34352  * event, and the parameters specified in the TreeLoader's baseParams property:
34353  * <pre><code>
34354     myTreeLoader.on("beforeload", function(treeLoader, node) {
34355         this.baseParams.category = node.attributes.category;
34356     }, this);
34357 </code></pre><
34358  * This would pass an HTTP parameter called "category" to the server containing
34359  * the value of the Node's "category" attribute.
34360  * @constructor
34361  * Creates a new Treeloader.
34362  * @param {Object} config A config object containing config properties.
34363  */
34364 Roo.tree.TreeLoader = function(config){
34365     this.baseParams = {};
34366     this.requestMethod = "POST";
34367     Roo.apply(this, config);
34368
34369     this.addEvents({
34370     
34371         /**
34372          * @event beforeload
34373          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34374          * @param {Object} This TreeLoader object.
34375          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34376          * @param {Object} callback The callback function specified in the {@link #load} call.
34377          */
34378         beforeload : true,
34379         /**
34380          * @event load
34381          * Fires when the node has been successfuly loaded.
34382          * @param {Object} This TreeLoader object.
34383          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34384          * @param {Object} response The response object containing the data from the server.
34385          */
34386         load : true,
34387         /**
34388          * @event loadexception
34389          * Fires if the network request failed.
34390          * @param {Object} This TreeLoader object.
34391          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34392          * @param {Object} response The response object containing the data from the server.
34393          */
34394         loadexception : true,
34395         /**
34396          * @event create
34397          * Fires before a node is created, enabling you to return custom Node types 
34398          * @param {Object} This TreeLoader object.
34399          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34400          */
34401         create : true
34402     });
34403
34404     Roo.tree.TreeLoader.superclass.constructor.call(this);
34405 };
34406
34407 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34408     /**
34409     * @cfg {String} dataUrl The URL from which to request a Json string which
34410     * specifies an array of node definition object representing the child nodes
34411     * to be loaded.
34412     */
34413     /**
34414     * @cfg {String} requestMethod either GET or POST
34415     * defaults to POST (due to BC)
34416     * to be loaded.
34417     */
34418     /**
34419     * @cfg {Object} baseParams (optional) An object containing properties which
34420     * specify HTTP parameters to be passed to each request for child nodes.
34421     */
34422     /**
34423     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34424     * created by this loader. If the attributes sent by the server have an attribute in this object,
34425     * they take priority.
34426     */
34427     /**
34428     * @cfg {Object} uiProviders (optional) An object containing properties which
34429     * 
34430     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34431     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34432     * <i>uiProvider</i> attribute of a returned child node is a string rather
34433     * than a reference to a TreeNodeUI implementation, this that string value
34434     * is used as a property name in the uiProviders object. You can define the provider named
34435     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34436     */
34437     uiProviders : {},
34438
34439     /**
34440     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34441     * child nodes before loading.
34442     */
34443     clearOnLoad : true,
34444
34445     /**
34446     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34447     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34448     * Grid query { data : [ .....] }
34449     */
34450     
34451     root : false,
34452      /**
34453     * @cfg {String} queryParam (optional) 
34454     * Name of the query as it will be passed on the querystring (defaults to 'node')
34455     * eg. the request will be ?node=[id]
34456     */
34457     
34458     
34459     queryParam: false,
34460     
34461     /**
34462      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34463      * This is called automatically when a node is expanded, but may be used to reload
34464      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34465      * @param {Roo.tree.TreeNode} node
34466      * @param {Function} callback
34467      */
34468     load : function(node, callback){
34469         if(this.clearOnLoad){
34470             while(node.firstChild){
34471                 node.removeChild(node.firstChild);
34472             }
34473         }
34474         if(node.attributes.children){ // preloaded json children
34475             var cs = node.attributes.children;
34476             for(var i = 0, len = cs.length; i < len; i++){
34477                 node.appendChild(this.createNode(cs[i]));
34478             }
34479             if(typeof callback == "function"){
34480                 callback();
34481             }
34482         }else if(this.dataUrl){
34483             this.requestData(node, callback);
34484         }
34485     },
34486
34487     getParams: function(node){
34488         var buf = [], bp = this.baseParams;
34489         for(var key in bp){
34490             if(typeof bp[key] != "function"){
34491                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34492             }
34493         }
34494         var n = this.queryParam === false ? 'node' : this.queryParam;
34495         buf.push(n + "=", encodeURIComponent(node.id));
34496         return buf.join("");
34497     },
34498
34499     requestData : function(node, callback){
34500         if(this.fireEvent("beforeload", this, node, callback) !== false){
34501             this.transId = Roo.Ajax.request({
34502                 method:this.requestMethod,
34503                 url: this.dataUrl||this.url,
34504                 success: this.handleResponse,
34505                 failure: this.handleFailure,
34506                 scope: this,
34507                 argument: {callback: callback, node: node},
34508                 params: this.getParams(node)
34509             });
34510         }else{
34511             // if the load is cancelled, make sure we notify
34512             // the node that we are done
34513             if(typeof callback == "function"){
34514                 callback();
34515             }
34516         }
34517     },
34518
34519     isLoading : function(){
34520         return this.transId ? true : false;
34521     },
34522
34523     abort : function(){
34524         if(this.isLoading()){
34525             Roo.Ajax.abort(this.transId);
34526         }
34527     },
34528
34529     // private
34530     createNode : function(attr)
34531     {
34532         // apply baseAttrs, nice idea Corey!
34533         if(this.baseAttrs){
34534             Roo.applyIf(attr, this.baseAttrs);
34535         }
34536         if(this.applyLoader !== false){
34537             attr.loader = this;
34538         }
34539         // uiProvider = depreciated..
34540         
34541         if(typeof(attr.uiProvider) == 'string'){
34542            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34543                 /**  eval:var:attr */ eval(attr.uiProvider);
34544         }
34545         if(typeof(this.uiProviders['default']) != 'undefined') {
34546             attr.uiProvider = this.uiProviders['default'];
34547         }
34548         
34549         this.fireEvent('create', this, attr);
34550         
34551         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34552         return(attr.leaf ?
34553                         new Roo.tree.TreeNode(attr) :
34554                         new Roo.tree.AsyncTreeNode(attr));
34555     },
34556
34557     processResponse : function(response, node, callback)
34558     {
34559         var json = response.responseText;
34560         try {
34561             
34562             var o = Roo.decode(json);
34563             
34564             if (this.root === false && typeof(o.success) != undefined) {
34565                 this.root = 'data'; // the default behaviour for list like data..
34566                 }
34567                 
34568             if (this.root !== false &&  !o.success) {
34569                 // it's a failure condition.
34570                 var a = response.argument;
34571                 this.fireEvent("loadexception", this, a.node, response);
34572                 Roo.log("Load failed - should have a handler really");
34573                 return;
34574             }
34575             
34576             
34577             
34578             if (this.root !== false) {
34579                  o = o[this.root];
34580             }
34581             
34582             for(var i = 0, len = o.length; i < len; i++){
34583                 var n = this.createNode(o[i]);
34584                 if(n){
34585                     node.appendChild(n);
34586                 }
34587             }
34588             if(typeof callback == "function"){
34589                 callback(this, node);
34590             }
34591         }catch(e){
34592             this.handleFailure(response);
34593         }
34594     },
34595
34596     handleResponse : function(response){
34597         this.transId = false;
34598         var a = response.argument;
34599         this.processResponse(response, a.node, a.callback);
34600         this.fireEvent("load", this, a.node, response);
34601     },
34602
34603     handleFailure : function(response)
34604     {
34605         // should handle failure better..
34606         this.transId = false;
34607         var a = response.argument;
34608         this.fireEvent("loadexception", this, a.node, response);
34609         if(typeof a.callback == "function"){
34610             a.callback(this, a.node);
34611         }
34612     }
34613 });/*
34614  * Based on:
34615  * Ext JS Library 1.1.1
34616  * Copyright(c) 2006-2007, Ext JS, LLC.
34617  *
34618  * Originally Released Under LGPL - original licence link has changed is not relivant.
34619  *
34620  * Fork - LGPL
34621  * <script type="text/javascript">
34622  */
34623
34624 /**
34625 * @class Roo.tree.TreeFilter
34626 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34627 * @param {TreePanel} tree
34628 * @param {Object} config (optional)
34629  */
34630 Roo.tree.TreeFilter = function(tree, config){
34631     this.tree = tree;
34632     this.filtered = {};
34633     Roo.apply(this, config);
34634 };
34635
34636 Roo.tree.TreeFilter.prototype = {
34637     clearBlank:false,
34638     reverse:false,
34639     autoClear:false,
34640     remove:false,
34641
34642      /**
34643      * Filter the data by a specific attribute.
34644      * @param {String/RegExp} value Either string that the attribute value
34645      * should start with or a RegExp to test against the attribute
34646      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34647      * @param {TreeNode} startNode (optional) The node to start the filter at.
34648      */
34649     filter : function(value, attr, startNode){
34650         attr = attr || "text";
34651         var f;
34652         if(typeof value == "string"){
34653             var vlen = value.length;
34654             // auto clear empty filter
34655             if(vlen == 0 && this.clearBlank){
34656                 this.clear();
34657                 return;
34658             }
34659             value = value.toLowerCase();
34660             f = function(n){
34661                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34662             };
34663         }else if(value.exec){ // regex?
34664             f = function(n){
34665                 return value.test(n.attributes[attr]);
34666             };
34667         }else{
34668             throw 'Illegal filter type, must be string or regex';
34669         }
34670         this.filterBy(f, null, startNode);
34671         },
34672
34673     /**
34674      * Filter by a function. The passed function will be called with each
34675      * node in the tree (or from the startNode). If the function returns true, the node is kept
34676      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34677      * @param {Function} fn The filter function
34678      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34679      */
34680     filterBy : function(fn, scope, startNode){
34681         startNode = startNode || this.tree.root;
34682         if(this.autoClear){
34683             this.clear();
34684         }
34685         var af = this.filtered, rv = this.reverse;
34686         var f = function(n){
34687             if(n == startNode){
34688                 return true;
34689             }
34690             if(af[n.id]){
34691                 return false;
34692             }
34693             var m = fn.call(scope || n, n);
34694             if(!m || rv){
34695                 af[n.id] = n;
34696                 n.ui.hide();
34697                 return false;
34698             }
34699             return true;
34700         };
34701         startNode.cascade(f);
34702         if(this.remove){
34703            for(var id in af){
34704                if(typeof id != "function"){
34705                    var n = af[id];
34706                    if(n && n.parentNode){
34707                        n.parentNode.removeChild(n);
34708                    }
34709                }
34710            }
34711         }
34712     },
34713
34714     /**
34715      * Clears the current filter. Note: with the "remove" option
34716      * set a filter cannot be cleared.
34717      */
34718     clear : function(){
34719         var t = this.tree;
34720         var af = this.filtered;
34721         for(var id in af){
34722             if(typeof id != "function"){
34723                 var n = af[id];
34724                 if(n){
34725                     n.ui.show();
34726                 }
34727             }
34728         }
34729         this.filtered = {};
34730     }
34731 };
34732 /*
34733  * Based on:
34734  * Ext JS Library 1.1.1
34735  * Copyright(c) 2006-2007, Ext JS, LLC.
34736  *
34737  * Originally Released Under LGPL - original licence link has changed is not relivant.
34738  *
34739  * Fork - LGPL
34740  * <script type="text/javascript">
34741  */
34742  
34743
34744 /**
34745  * @class Roo.tree.TreeSorter
34746  * Provides sorting of nodes in a TreePanel
34747  * 
34748  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34749  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34750  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34751  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34752  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34753  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34754  * @constructor
34755  * @param {TreePanel} tree
34756  * @param {Object} config
34757  */
34758 Roo.tree.TreeSorter = function(tree, config){
34759     Roo.apply(this, config);
34760     tree.on("beforechildrenrendered", this.doSort, this);
34761     tree.on("append", this.updateSort, this);
34762     tree.on("insert", this.updateSort, this);
34763     
34764     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34765     var p = this.property || "text";
34766     var sortType = this.sortType;
34767     var fs = this.folderSort;
34768     var cs = this.caseSensitive === true;
34769     var leafAttr = this.leafAttr || 'leaf';
34770
34771     this.sortFn = function(n1, n2){
34772         if(fs){
34773             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34774                 return 1;
34775             }
34776             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34777                 return -1;
34778             }
34779         }
34780         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34781         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34782         if(v1 < v2){
34783                         return dsc ? +1 : -1;
34784                 }else if(v1 > v2){
34785                         return dsc ? -1 : +1;
34786         }else{
34787                 return 0;
34788         }
34789     };
34790 };
34791
34792 Roo.tree.TreeSorter.prototype = {
34793     doSort : function(node){
34794         node.sort(this.sortFn);
34795     },
34796     
34797     compareNodes : function(n1, n2){
34798         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34799     },
34800     
34801     updateSort : function(tree, node){
34802         if(node.childrenRendered){
34803             this.doSort.defer(1, this, [node]);
34804         }
34805     }
34806 };/*
34807  * Based on:
34808  * Ext JS Library 1.1.1
34809  * Copyright(c) 2006-2007, Ext JS, LLC.
34810  *
34811  * Originally Released Under LGPL - original licence link has changed is not relivant.
34812  *
34813  * Fork - LGPL
34814  * <script type="text/javascript">
34815  */
34816
34817 if(Roo.dd.DropZone){
34818     
34819 Roo.tree.TreeDropZone = function(tree, config){
34820     this.allowParentInsert = false;
34821     this.allowContainerDrop = false;
34822     this.appendOnly = false;
34823     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34824     this.tree = tree;
34825     this.lastInsertClass = "x-tree-no-status";
34826     this.dragOverData = {};
34827 };
34828
34829 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34830     ddGroup : "TreeDD",
34831     scroll:  true,
34832     
34833     expandDelay : 1000,
34834     
34835     expandNode : function(node){
34836         if(node.hasChildNodes() && !node.isExpanded()){
34837             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34838         }
34839     },
34840     
34841     queueExpand : function(node){
34842         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34843     },
34844     
34845     cancelExpand : function(){
34846         if(this.expandProcId){
34847             clearTimeout(this.expandProcId);
34848             this.expandProcId = false;
34849         }
34850     },
34851     
34852     isValidDropPoint : function(n, pt, dd, e, data){
34853         if(!n || !data){ return false; }
34854         var targetNode = n.node;
34855         var dropNode = data.node;
34856         // default drop rules
34857         if(!(targetNode && targetNode.isTarget && pt)){
34858             return false;
34859         }
34860         if(pt == "append" && targetNode.allowChildren === false){
34861             return false;
34862         }
34863         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34864             return false;
34865         }
34866         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34867             return false;
34868         }
34869         // reuse the object
34870         var overEvent = this.dragOverData;
34871         overEvent.tree = this.tree;
34872         overEvent.target = targetNode;
34873         overEvent.data = data;
34874         overEvent.point = pt;
34875         overEvent.source = dd;
34876         overEvent.rawEvent = e;
34877         overEvent.dropNode = dropNode;
34878         overEvent.cancel = false;  
34879         var result = this.tree.fireEvent("nodedragover", overEvent);
34880         return overEvent.cancel === false && result !== false;
34881     },
34882     
34883     getDropPoint : function(e, n, dd)
34884     {
34885         var tn = n.node;
34886         if(tn.isRoot){
34887             return tn.allowChildren !== false ? "append" : false; // always append for root
34888         }
34889         var dragEl = n.ddel;
34890         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34891         var y = Roo.lib.Event.getPageY(e);
34892         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34893         
34894         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34895         var noAppend = tn.allowChildren === false;
34896         if(this.appendOnly || tn.parentNode.allowChildren === false){
34897             return noAppend ? false : "append";
34898         }
34899         var noBelow = false;
34900         if(!this.allowParentInsert){
34901             noBelow = tn.hasChildNodes() && tn.isExpanded();
34902         }
34903         var q = (b - t) / (noAppend ? 2 : 3);
34904         if(y >= t && y < (t + q)){
34905             return "above";
34906         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34907             return "below";
34908         }else{
34909             return "append";
34910         }
34911     },
34912     
34913     onNodeEnter : function(n, dd, e, data)
34914     {
34915         this.cancelExpand();
34916     },
34917     
34918     onNodeOver : function(n, dd, e, data)
34919     {
34920        
34921         var pt = this.getDropPoint(e, n, dd);
34922         var node = n.node;
34923         
34924         // auto node expand check
34925         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34926             this.queueExpand(node);
34927         }else if(pt != "append"){
34928             this.cancelExpand();
34929         }
34930         
34931         // set the insert point style on the target node
34932         var returnCls = this.dropNotAllowed;
34933         if(this.isValidDropPoint(n, pt, dd, e, data)){
34934            if(pt){
34935                var el = n.ddel;
34936                var cls;
34937                if(pt == "above"){
34938                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34939                    cls = "x-tree-drag-insert-above";
34940                }else if(pt == "below"){
34941                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34942                    cls = "x-tree-drag-insert-below";
34943                }else{
34944                    returnCls = "x-tree-drop-ok-append";
34945                    cls = "x-tree-drag-append";
34946                }
34947                if(this.lastInsertClass != cls){
34948                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34949                    this.lastInsertClass = cls;
34950                }
34951            }
34952        }
34953        return returnCls;
34954     },
34955     
34956     onNodeOut : function(n, dd, e, data){
34957         
34958         this.cancelExpand();
34959         this.removeDropIndicators(n);
34960     },
34961     
34962     onNodeDrop : function(n, dd, e, data){
34963         var point = this.getDropPoint(e, n, dd);
34964         var targetNode = n.node;
34965         targetNode.ui.startDrop();
34966         if(!this.isValidDropPoint(n, point, dd, e, data)){
34967             targetNode.ui.endDrop();
34968             return false;
34969         }
34970         // first try to find the drop node
34971         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34972         var dropEvent = {
34973             tree : this.tree,
34974             target: targetNode,
34975             data: data,
34976             point: point,
34977             source: dd,
34978             rawEvent: e,
34979             dropNode: dropNode,
34980             cancel: !dropNode   
34981         };
34982         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34983         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34984             targetNode.ui.endDrop();
34985             return false;
34986         }
34987         // allow target changing
34988         targetNode = dropEvent.target;
34989         if(point == "append" && !targetNode.isExpanded()){
34990             targetNode.expand(false, null, function(){
34991                 this.completeDrop(dropEvent);
34992             }.createDelegate(this));
34993         }else{
34994             this.completeDrop(dropEvent);
34995         }
34996         return true;
34997     },
34998     
34999     completeDrop : function(de){
35000         var ns = de.dropNode, p = de.point, t = de.target;
35001         if(!(ns instanceof Array)){
35002             ns = [ns];
35003         }
35004         var n;
35005         for(var i = 0, len = ns.length; i < len; i++){
35006             n = ns[i];
35007             if(p == "above"){
35008                 t.parentNode.insertBefore(n, t);
35009             }else if(p == "below"){
35010                 t.parentNode.insertBefore(n, t.nextSibling);
35011             }else{
35012                 t.appendChild(n);
35013             }
35014         }
35015         n.ui.focus();
35016         if(this.tree.hlDrop){
35017             n.ui.highlight();
35018         }
35019         t.ui.endDrop();
35020         this.tree.fireEvent("nodedrop", de);
35021     },
35022     
35023     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35024         if(this.tree.hlDrop){
35025             dropNode.ui.focus();
35026             dropNode.ui.highlight();
35027         }
35028         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35029     },
35030     
35031     getTree : function(){
35032         return this.tree;
35033     },
35034     
35035     removeDropIndicators : function(n){
35036         if(n && n.ddel){
35037             var el = n.ddel;
35038             Roo.fly(el).removeClass([
35039                     "x-tree-drag-insert-above",
35040                     "x-tree-drag-insert-below",
35041                     "x-tree-drag-append"]);
35042             this.lastInsertClass = "_noclass";
35043         }
35044     },
35045     
35046     beforeDragDrop : function(target, e, id){
35047         this.cancelExpand();
35048         return true;
35049     },
35050     
35051     afterRepair : function(data){
35052         if(data && Roo.enableFx){
35053             data.node.ui.highlight();
35054         }
35055         this.hideProxy();
35056     } 
35057     
35058 });
35059
35060 }
35061 /*
35062  * Based on:
35063  * Ext JS Library 1.1.1
35064  * Copyright(c) 2006-2007, Ext JS, LLC.
35065  *
35066  * Originally Released Under LGPL - original licence link has changed is not relivant.
35067  *
35068  * Fork - LGPL
35069  * <script type="text/javascript">
35070  */
35071  
35072
35073 if(Roo.dd.DragZone){
35074 Roo.tree.TreeDragZone = function(tree, config){
35075     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35076     this.tree = tree;
35077 };
35078
35079 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35080     ddGroup : "TreeDD",
35081    
35082     onBeforeDrag : function(data, e){
35083         var n = data.node;
35084         return n && n.draggable && !n.disabled;
35085     },
35086      
35087     
35088     onInitDrag : function(e){
35089         var data = this.dragData;
35090         this.tree.getSelectionModel().select(data.node);
35091         this.proxy.update("");
35092         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35093         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35094     },
35095     
35096     getRepairXY : function(e, data){
35097         return data.node.ui.getDDRepairXY();
35098     },
35099     
35100     onEndDrag : function(data, e){
35101         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35102         
35103         
35104     },
35105     
35106     onValidDrop : function(dd, e, id){
35107         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35108         this.hideProxy();
35109     },
35110     
35111     beforeInvalidDrop : function(e, id){
35112         // this scrolls the original position back into view
35113         var sm = this.tree.getSelectionModel();
35114         sm.clearSelections();
35115         sm.select(this.dragData.node);
35116     }
35117 });
35118 }/*
35119  * Based on:
35120  * Ext JS Library 1.1.1
35121  * Copyright(c) 2006-2007, Ext JS, LLC.
35122  *
35123  * Originally Released Under LGPL - original licence link has changed is not relivant.
35124  *
35125  * Fork - LGPL
35126  * <script type="text/javascript">
35127  */
35128 /**
35129  * @class Roo.tree.TreeEditor
35130  * @extends Roo.Editor
35131  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35132  * as the editor field.
35133  * @constructor
35134  * @param {Object} config (used to be the tree panel.)
35135  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35136  * 
35137  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35138  * @cfg {Roo.form.TextField|Object} field The field configuration
35139  *
35140  * 
35141  */
35142 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35143     var tree = config;
35144     var field;
35145     if (oldconfig) { // old style..
35146         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35147     } else {
35148         // new style..
35149         tree = config.tree;
35150         config.field = config.field  || {};
35151         config.field.xtype = 'TextField';
35152         field = Roo.factory(config.field, Roo.form);
35153     }
35154     config = config || {};
35155     
35156     
35157     this.addEvents({
35158         /**
35159          * @event beforenodeedit
35160          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35161          * false from the handler of this event.
35162          * @param {Editor} this
35163          * @param {Roo.tree.Node} node 
35164          */
35165         "beforenodeedit" : true
35166     });
35167     
35168     //Roo.log(config);
35169     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35170
35171     this.tree = tree;
35172
35173     tree.on('beforeclick', this.beforeNodeClick, this);
35174     tree.getTreeEl().on('mousedown', this.hide, this);
35175     this.on('complete', this.updateNode, this);
35176     this.on('beforestartedit', this.fitToTree, this);
35177     this.on('startedit', this.bindScroll, this, {delay:10});
35178     this.on('specialkey', this.onSpecialKey, this);
35179 };
35180
35181 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35182     /**
35183      * @cfg {String} alignment
35184      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35185      */
35186     alignment: "l-l",
35187     // inherit
35188     autoSize: false,
35189     /**
35190      * @cfg {Boolean} hideEl
35191      * True to hide the bound element while the editor is displayed (defaults to false)
35192      */
35193     hideEl : false,
35194     /**
35195      * @cfg {String} cls
35196      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35197      */
35198     cls: "x-small-editor x-tree-editor",
35199     /**
35200      * @cfg {Boolean} shim
35201      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35202      */
35203     shim:false,
35204     // inherit
35205     shadow:"frame",
35206     /**
35207      * @cfg {Number} maxWidth
35208      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35209      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35210      * scroll and client offsets into account prior to each edit.
35211      */
35212     maxWidth: 250,
35213
35214     editDelay : 350,
35215
35216     // private
35217     fitToTree : function(ed, el){
35218         var td = this.tree.getTreeEl().dom, nd = el.dom;
35219         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35220             td.scrollLeft = nd.offsetLeft;
35221         }
35222         var w = Math.min(
35223                 this.maxWidth,
35224                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35225         this.setSize(w, '');
35226         
35227         return this.fireEvent('beforenodeedit', this, this.editNode);
35228         
35229     },
35230
35231     // private
35232     triggerEdit : function(node){
35233         this.completeEdit();
35234         this.editNode = node;
35235         this.startEdit(node.ui.textNode, node.text);
35236     },
35237
35238     // private
35239     bindScroll : function(){
35240         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35241     },
35242
35243     // private
35244     beforeNodeClick : function(node, e){
35245         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35246         this.lastClick = new Date();
35247         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35248             e.stopEvent();
35249             this.triggerEdit(node);
35250             return false;
35251         }
35252         return true;
35253     },
35254
35255     // private
35256     updateNode : function(ed, value){
35257         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35258         this.editNode.setText(value);
35259     },
35260
35261     // private
35262     onHide : function(){
35263         Roo.tree.TreeEditor.superclass.onHide.call(this);
35264         if(this.editNode){
35265             this.editNode.ui.focus();
35266         }
35267     },
35268
35269     // private
35270     onSpecialKey : function(field, e){
35271         var k = e.getKey();
35272         if(k == e.ESC){
35273             e.stopEvent();
35274             this.cancelEdit();
35275         }else if(k == e.ENTER && !e.hasModifier()){
35276             e.stopEvent();
35277             this.completeEdit();
35278         }
35279     }
35280 });//<Script type="text/javascript">
35281 /*
35282  * Based on:
35283  * Ext JS Library 1.1.1
35284  * Copyright(c) 2006-2007, Ext JS, LLC.
35285  *
35286  * Originally Released Under LGPL - original licence link has changed is not relivant.
35287  *
35288  * Fork - LGPL
35289  * <script type="text/javascript">
35290  */
35291  
35292 /**
35293  * Not documented??? - probably should be...
35294  */
35295
35296 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35297     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35298     
35299     renderElements : function(n, a, targetNode, bulkRender){
35300         //consel.log("renderElements?");
35301         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35302
35303         var t = n.getOwnerTree();
35304         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35305         
35306         var cols = t.columns;
35307         var bw = t.borderWidth;
35308         var c = cols[0];
35309         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35310          var cb = typeof a.checked == "boolean";
35311         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35312         var colcls = 'x-t-' + tid + '-c0';
35313         var buf = [
35314             '<li class="x-tree-node">',
35315             
35316                 
35317                 '<div class="x-tree-node-el ', a.cls,'">',
35318                     // extran...
35319                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35320                 
35321                 
35322                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35323                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35324                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35325                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35326                            (a.iconCls ? ' '+a.iconCls : ''),
35327                            '" unselectable="on" />',
35328                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35329                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35330                              
35331                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35332                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35333                             '<span unselectable="on" qtip="' + tx + '">',
35334                              tx,
35335                              '</span></a>' ,
35336                     '</div>',
35337                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35338                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35339                  ];
35340         for(var i = 1, len = cols.length; i < len; i++){
35341             c = cols[i];
35342             colcls = 'x-t-' + tid + '-c' +i;
35343             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35344             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35345                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35346                       "</div>");
35347          }
35348          
35349          buf.push(
35350             '</a>',
35351             '<div class="x-clear"></div></div>',
35352             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35353             "</li>");
35354         
35355         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35356             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35357                                 n.nextSibling.ui.getEl(), buf.join(""));
35358         }else{
35359             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35360         }
35361         var el = this.wrap.firstChild;
35362         this.elRow = el;
35363         this.elNode = el.firstChild;
35364         this.ranchor = el.childNodes[1];
35365         this.ctNode = this.wrap.childNodes[1];
35366         var cs = el.firstChild.childNodes;
35367         this.indentNode = cs[0];
35368         this.ecNode = cs[1];
35369         this.iconNode = cs[2];
35370         var index = 3;
35371         if(cb){
35372             this.checkbox = cs[3];
35373             index++;
35374         }
35375         this.anchor = cs[index];
35376         
35377         this.textNode = cs[index].firstChild;
35378         
35379         //el.on("click", this.onClick, this);
35380         //el.on("dblclick", this.onDblClick, this);
35381         
35382         
35383        // console.log(this);
35384     },
35385     initEvents : function(){
35386         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35387         
35388             
35389         var a = this.ranchor;
35390
35391         var el = Roo.get(a);
35392
35393         if(Roo.isOpera){ // opera render bug ignores the CSS
35394             el.setStyle("text-decoration", "none");
35395         }
35396
35397         el.on("click", this.onClick, this);
35398         el.on("dblclick", this.onDblClick, this);
35399         el.on("contextmenu", this.onContextMenu, this);
35400         
35401     },
35402     
35403     /*onSelectedChange : function(state){
35404         if(state){
35405             this.focus();
35406             this.addClass("x-tree-selected");
35407         }else{
35408             //this.blur();
35409             this.removeClass("x-tree-selected");
35410         }
35411     },*/
35412     addClass : function(cls){
35413         if(this.elRow){
35414             Roo.fly(this.elRow).addClass(cls);
35415         }
35416         
35417     },
35418     
35419     
35420     removeClass : function(cls){
35421         if(this.elRow){
35422             Roo.fly(this.elRow).removeClass(cls);
35423         }
35424     }
35425
35426     
35427     
35428 });//<Script type="text/javascript">
35429
35430 /*
35431  * Based on:
35432  * Ext JS Library 1.1.1
35433  * Copyright(c) 2006-2007, Ext JS, LLC.
35434  *
35435  * Originally Released Under LGPL - original licence link has changed is not relivant.
35436  *
35437  * Fork - LGPL
35438  * <script type="text/javascript">
35439  */
35440  
35441
35442 /**
35443  * @class Roo.tree.ColumnTree
35444  * @extends Roo.data.TreePanel
35445  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35446  * @cfg {int} borderWidth  compined right/left border allowance
35447  * @constructor
35448  * @param {String/HTMLElement/Element} el The container element
35449  * @param {Object} config
35450  */
35451 Roo.tree.ColumnTree =  function(el, config)
35452 {
35453    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35454    this.addEvents({
35455         /**
35456         * @event resize
35457         * Fire this event on a container when it resizes
35458         * @param {int} w Width
35459         * @param {int} h Height
35460         */
35461        "resize" : true
35462     });
35463     this.on('resize', this.onResize, this);
35464 };
35465
35466 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35467     //lines:false,
35468     
35469     
35470     borderWidth: Roo.isBorderBox ? 0 : 2, 
35471     headEls : false,
35472     
35473     render : function(){
35474         // add the header.....
35475        
35476         Roo.tree.ColumnTree.superclass.render.apply(this);
35477         
35478         this.el.addClass('x-column-tree');
35479         
35480         this.headers = this.el.createChild(
35481             {cls:'x-tree-headers'},this.innerCt.dom);
35482    
35483         var cols = this.columns, c;
35484         var totalWidth = 0;
35485         this.headEls = [];
35486         var  len = cols.length;
35487         for(var i = 0; i < len; i++){
35488              c = cols[i];
35489              totalWidth += c.width;
35490             this.headEls.push(this.headers.createChild({
35491                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35492                  cn: {
35493                      cls:'x-tree-hd-text',
35494                      html: c.header
35495                  },
35496                  style:'width:'+(c.width-this.borderWidth)+'px;'
35497              }));
35498         }
35499         this.headers.createChild({cls:'x-clear'});
35500         // prevent floats from wrapping when clipped
35501         this.headers.setWidth(totalWidth);
35502         //this.innerCt.setWidth(totalWidth);
35503         this.innerCt.setStyle({ overflow: 'auto' });
35504         this.onResize(this.width, this.height);
35505              
35506         
35507     },
35508     onResize : function(w,h)
35509     {
35510         this.height = h;
35511         this.width = w;
35512         // resize cols..
35513         this.innerCt.setWidth(this.width);
35514         this.innerCt.setHeight(this.height-20);
35515         
35516         // headers...
35517         var cols = this.columns, c;
35518         var totalWidth = 0;
35519         var expEl = false;
35520         var len = cols.length;
35521         for(var i = 0; i < len; i++){
35522             c = cols[i];
35523             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35524                 // it's the expander..
35525                 expEl  = this.headEls[i];
35526                 continue;
35527             }
35528             totalWidth += c.width;
35529             
35530         }
35531         if (expEl) {
35532             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35533         }
35534         this.headers.setWidth(w-20);
35535
35536         
35537         
35538         
35539     }
35540 });
35541 /*
35542  * Based on:
35543  * Ext JS Library 1.1.1
35544  * Copyright(c) 2006-2007, Ext JS, LLC.
35545  *
35546  * Originally Released Under LGPL - original licence link has changed is not relivant.
35547  *
35548  * Fork - LGPL
35549  * <script type="text/javascript">
35550  */
35551  
35552 /**
35553  * @class Roo.menu.Menu
35554  * @extends Roo.util.Observable
35555  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35556  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35557  * @constructor
35558  * Creates a new Menu
35559  * @param {Object} config Configuration options
35560  */
35561 Roo.menu.Menu = function(config){
35562     Roo.apply(this, config);
35563     this.id = this.id || Roo.id();
35564     this.addEvents({
35565         /**
35566          * @event beforeshow
35567          * Fires before this menu is displayed
35568          * @param {Roo.menu.Menu} this
35569          */
35570         beforeshow : true,
35571         /**
35572          * @event beforehide
35573          * Fires before this menu is hidden
35574          * @param {Roo.menu.Menu} this
35575          */
35576         beforehide : true,
35577         /**
35578          * @event show
35579          * Fires after this menu is displayed
35580          * @param {Roo.menu.Menu} this
35581          */
35582         show : true,
35583         /**
35584          * @event hide
35585          * Fires after this menu is hidden
35586          * @param {Roo.menu.Menu} this
35587          */
35588         hide : true,
35589         /**
35590          * @event click
35591          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35592          * @param {Roo.menu.Menu} this
35593          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35594          * @param {Roo.EventObject} e
35595          */
35596         click : true,
35597         /**
35598          * @event mouseover
35599          * Fires when the mouse is hovering over this menu
35600          * @param {Roo.menu.Menu} this
35601          * @param {Roo.EventObject} e
35602          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35603          */
35604         mouseover : true,
35605         /**
35606          * @event mouseout
35607          * Fires when the mouse exits this menu
35608          * @param {Roo.menu.Menu} this
35609          * @param {Roo.EventObject} e
35610          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35611          */
35612         mouseout : true,
35613         /**
35614          * @event itemclick
35615          * Fires when a menu item contained in this menu is clicked
35616          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35617          * @param {Roo.EventObject} e
35618          */
35619         itemclick: true
35620     });
35621     if (this.registerMenu) {
35622         Roo.menu.MenuMgr.register(this);
35623     }
35624     
35625     var mis = this.items;
35626     this.items = new Roo.util.MixedCollection();
35627     if(mis){
35628         this.add.apply(this, mis);
35629     }
35630 };
35631
35632 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35633     /**
35634      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35635      */
35636     minWidth : 120,
35637     /**
35638      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35639      * for bottom-right shadow (defaults to "sides")
35640      */
35641     shadow : "sides",
35642     /**
35643      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35644      * this menu (defaults to "tl-tr?")
35645      */
35646     subMenuAlign : "tl-tr?",
35647     /**
35648      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35649      * relative to its element of origin (defaults to "tl-bl?")
35650      */
35651     defaultAlign : "tl-bl?",
35652     /**
35653      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35654      */
35655     allowOtherMenus : false,
35656     /**
35657      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35658      */
35659     registerMenu : true,
35660
35661     hidden:true,
35662
35663     // private
35664     render : function(){
35665         if(this.el){
35666             return;
35667         }
35668         var el = this.el = new Roo.Layer({
35669             cls: "x-menu",
35670             shadow:this.shadow,
35671             constrain: false,
35672             parentEl: this.parentEl || document.body,
35673             zindex:15000
35674         });
35675
35676         this.keyNav = new Roo.menu.MenuNav(this);
35677
35678         if(this.plain){
35679             el.addClass("x-menu-plain");
35680         }
35681         if(this.cls){
35682             el.addClass(this.cls);
35683         }
35684         // generic focus element
35685         this.focusEl = el.createChild({
35686             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35687         });
35688         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35689         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35690         
35691         ul.on("mouseover", this.onMouseOver, this);
35692         ul.on("mouseout", this.onMouseOut, this);
35693         this.items.each(function(item){
35694             if (item.hidden) {
35695                 return;
35696             }
35697             
35698             var li = document.createElement("li");
35699             li.className = "x-menu-list-item";
35700             ul.dom.appendChild(li);
35701             item.render(li, this);
35702         }, this);
35703         this.ul = ul;
35704         this.autoWidth();
35705     },
35706
35707     // private
35708     autoWidth : function(){
35709         var el = this.el, ul = this.ul;
35710         if(!el){
35711             return;
35712         }
35713         var w = this.width;
35714         if(w){
35715             el.setWidth(w);
35716         }else if(Roo.isIE){
35717             el.setWidth(this.minWidth);
35718             var t = el.dom.offsetWidth; // force recalc
35719             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35720         }
35721     },
35722
35723     // private
35724     delayAutoWidth : function(){
35725         if(this.rendered){
35726             if(!this.awTask){
35727                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35728             }
35729             this.awTask.delay(20);
35730         }
35731     },
35732
35733     // private
35734     findTargetItem : function(e){
35735         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35736         if(t && t.menuItemId){
35737             return this.items.get(t.menuItemId);
35738         }
35739     },
35740
35741     // private
35742     onClick : function(e){
35743         Roo.log("menu.onClick");
35744         var t = this.findTargetItem(e);
35745         if(!t){
35746             return;
35747         }
35748         Roo.log(e);
35749         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35750             if(t == this.activeItem && t.shouldDeactivate(e)){
35751                 this.activeItem.deactivate();
35752                 delete this.activeItem;
35753                 return;
35754             }
35755             if(t.canActivate){
35756                 this.setActiveItem(t, true);
35757             }
35758             return;
35759             
35760             
35761         }
35762         
35763         t.onClick(e);
35764         this.fireEvent("click", this, t, e);
35765     },
35766
35767     // private
35768     setActiveItem : function(item, autoExpand){
35769         if(item != this.activeItem){
35770             if(this.activeItem){
35771                 this.activeItem.deactivate();
35772             }
35773             this.activeItem = item;
35774             item.activate(autoExpand);
35775         }else if(autoExpand){
35776             item.expandMenu();
35777         }
35778     },
35779
35780     // private
35781     tryActivate : function(start, step){
35782         var items = this.items;
35783         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35784             var item = items.get(i);
35785             if(!item.disabled && item.canActivate){
35786                 this.setActiveItem(item, false);
35787                 return item;
35788             }
35789         }
35790         return false;
35791     },
35792
35793     // private
35794     onMouseOver : function(e){
35795         var t;
35796         if(t = this.findTargetItem(e)){
35797             if(t.canActivate && !t.disabled){
35798                 this.setActiveItem(t, true);
35799             }
35800         }
35801         this.fireEvent("mouseover", this, e, t);
35802     },
35803
35804     // private
35805     onMouseOut : function(e){
35806         var t;
35807         if(t = this.findTargetItem(e)){
35808             if(t == this.activeItem && t.shouldDeactivate(e)){
35809                 this.activeItem.deactivate();
35810                 delete this.activeItem;
35811             }
35812         }
35813         this.fireEvent("mouseout", this, e, t);
35814     },
35815
35816     /**
35817      * Read-only.  Returns true if the menu is currently displayed, else false.
35818      * @type Boolean
35819      */
35820     isVisible : function(){
35821         return this.el && !this.hidden;
35822     },
35823
35824     /**
35825      * Displays this menu relative to another element
35826      * @param {String/HTMLElement/Roo.Element} element The element to align to
35827      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35828      * the element (defaults to this.defaultAlign)
35829      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35830      */
35831     show : function(el, pos, parentMenu){
35832         this.parentMenu = parentMenu;
35833         if(!this.el){
35834             this.render();
35835         }
35836         this.fireEvent("beforeshow", this);
35837         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35838     },
35839
35840     /**
35841      * Displays this menu at a specific xy position
35842      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35843      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35844      */
35845     showAt : function(xy, parentMenu, /* private: */_e){
35846         this.parentMenu = parentMenu;
35847         if(!this.el){
35848             this.render();
35849         }
35850         if(_e !== false){
35851             this.fireEvent("beforeshow", this);
35852             xy = this.el.adjustForConstraints(xy);
35853         }
35854         this.el.setXY(xy);
35855         this.el.show();
35856         this.hidden = false;
35857         this.focus();
35858         this.fireEvent("show", this);
35859     },
35860
35861     focus : function(){
35862         if(!this.hidden){
35863             this.doFocus.defer(50, this);
35864         }
35865     },
35866
35867     doFocus : function(){
35868         if(!this.hidden){
35869             this.focusEl.focus();
35870         }
35871     },
35872
35873     /**
35874      * Hides this menu and optionally all parent menus
35875      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35876      */
35877     hide : function(deep){
35878         if(this.el && this.isVisible()){
35879             this.fireEvent("beforehide", this);
35880             if(this.activeItem){
35881                 this.activeItem.deactivate();
35882                 this.activeItem = null;
35883             }
35884             this.el.hide();
35885             this.hidden = true;
35886             this.fireEvent("hide", this);
35887         }
35888         if(deep === true && this.parentMenu){
35889             this.parentMenu.hide(true);
35890         }
35891     },
35892
35893     /**
35894      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35895      * Any of the following are valid:
35896      * <ul>
35897      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35898      * <li>An HTMLElement object which will be converted to a menu item</li>
35899      * <li>A menu item config object that will be created as a new menu item</li>
35900      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35901      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35902      * </ul>
35903      * Usage:
35904      * <pre><code>
35905 // Create the menu
35906 var menu = new Roo.menu.Menu();
35907
35908 // Create a menu item to add by reference
35909 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35910
35911 // Add a bunch of items at once using different methods.
35912 // Only the last item added will be returned.
35913 var item = menu.add(
35914     menuItem,                // add existing item by ref
35915     'Dynamic Item',          // new TextItem
35916     '-',                     // new separator
35917     { text: 'Config Item' }  // new item by config
35918 );
35919 </code></pre>
35920      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35921      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35922      */
35923     add : function(){
35924         var a = arguments, l = a.length, item;
35925         for(var i = 0; i < l; i++){
35926             var el = a[i];
35927             if ((typeof(el) == "object") && el.xtype && el.xns) {
35928                 el = Roo.factory(el, Roo.menu);
35929             }
35930             
35931             if(el.render){ // some kind of Item
35932                 item = this.addItem(el);
35933             }else if(typeof el == "string"){ // string
35934                 if(el == "separator" || el == "-"){
35935                     item = this.addSeparator();
35936                 }else{
35937                     item = this.addText(el);
35938                 }
35939             }else if(el.tagName || el.el){ // element
35940                 item = this.addElement(el);
35941             }else if(typeof el == "object"){ // must be menu item config?
35942                 item = this.addMenuItem(el);
35943             }
35944         }
35945         return item;
35946     },
35947
35948     /**
35949      * Returns this menu's underlying {@link Roo.Element} object
35950      * @return {Roo.Element} The element
35951      */
35952     getEl : function(){
35953         if(!this.el){
35954             this.render();
35955         }
35956         return this.el;
35957     },
35958
35959     /**
35960      * Adds a separator bar to the menu
35961      * @return {Roo.menu.Item} The menu item that was added
35962      */
35963     addSeparator : function(){
35964         return this.addItem(new Roo.menu.Separator());
35965     },
35966
35967     /**
35968      * Adds an {@link Roo.Element} object to the menu
35969      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35970      * @return {Roo.menu.Item} The menu item that was added
35971      */
35972     addElement : function(el){
35973         return this.addItem(new Roo.menu.BaseItem(el));
35974     },
35975
35976     /**
35977      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35978      * @param {Roo.menu.Item} item The menu item to add
35979      * @return {Roo.menu.Item} The menu item that was added
35980      */
35981     addItem : function(item){
35982         this.items.add(item);
35983         if(this.ul){
35984             var li = document.createElement("li");
35985             li.className = "x-menu-list-item";
35986             this.ul.dom.appendChild(li);
35987             item.render(li, this);
35988             this.delayAutoWidth();
35989         }
35990         return item;
35991     },
35992
35993     /**
35994      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35995      * @param {Object} config A MenuItem config object
35996      * @return {Roo.menu.Item} The menu item that was added
35997      */
35998     addMenuItem : function(config){
35999         if(!(config instanceof Roo.menu.Item)){
36000             if(typeof config.checked == "boolean"){ // must be check menu item config?
36001                 config = new Roo.menu.CheckItem(config);
36002             }else{
36003                 config = new Roo.menu.Item(config);
36004             }
36005         }
36006         return this.addItem(config);
36007     },
36008
36009     /**
36010      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36011      * @param {String} text The text to display in the menu item
36012      * @return {Roo.menu.Item} The menu item that was added
36013      */
36014     addText : function(text){
36015         return this.addItem(new Roo.menu.TextItem({ text : text }));
36016     },
36017
36018     /**
36019      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36020      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36021      * @param {Roo.menu.Item} item The menu item to add
36022      * @return {Roo.menu.Item} The menu item that was added
36023      */
36024     insert : function(index, item){
36025         this.items.insert(index, item);
36026         if(this.ul){
36027             var li = document.createElement("li");
36028             li.className = "x-menu-list-item";
36029             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36030             item.render(li, this);
36031             this.delayAutoWidth();
36032         }
36033         return item;
36034     },
36035
36036     /**
36037      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36038      * @param {Roo.menu.Item} item The menu item to remove
36039      */
36040     remove : function(item){
36041         this.items.removeKey(item.id);
36042         item.destroy();
36043     },
36044
36045     /**
36046      * Removes and destroys all items in the menu
36047      */
36048     removeAll : function(){
36049         var f;
36050         while(f = this.items.first()){
36051             this.remove(f);
36052         }
36053     }
36054 });
36055
36056 // MenuNav is a private utility class used internally by the Menu
36057 Roo.menu.MenuNav = function(menu){
36058     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36059     this.scope = this.menu = menu;
36060 };
36061
36062 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36063     doRelay : function(e, h){
36064         var k = e.getKey();
36065         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36066             this.menu.tryActivate(0, 1);
36067             return false;
36068         }
36069         return h.call(this.scope || this, e, this.menu);
36070     },
36071
36072     up : function(e, m){
36073         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36074             m.tryActivate(m.items.length-1, -1);
36075         }
36076     },
36077
36078     down : function(e, m){
36079         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36080             m.tryActivate(0, 1);
36081         }
36082     },
36083
36084     right : function(e, m){
36085         if(m.activeItem){
36086             m.activeItem.expandMenu(true);
36087         }
36088     },
36089
36090     left : function(e, m){
36091         m.hide();
36092         if(m.parentMenu && m.parentMenu.activeItem){
36093             m.parentMenu.activeItem.activate();
36094         }
36095     },
36096
36097     enter : function(e, m){
36098         if(m.activeItem){
36099             e.stopPropagation();
36100             m.activeItem.onClick(e);
36101             m.fireEvent("click", this, m.activeItem);
36102             return true;
36103         }
36104     }
36105 });/*
36106  * Based on:
36107  * Ext JS Library 1.1.1
36108  * Copyright(c) 2006-2007, Ext JS, LLC.
36109  *
36110  * Originally Released Under LGPL - original licence link has changed is not relivant.
36111  *
36112  * Fork - LGPL
36113  * <script type="text/javascript">
36114  */
36115  
36116 /**
36117  * @class Roo.menu.MenuMgr
36118  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36119  * @singleton
36120  */
36121 Roo.menu.MenuMgr = function(){
36122    var menus, active, groups = {}, attached = false, lastShow = new Date();
36123
36124    // private - called when first menu is created
36125    function init(){
36126        menus = {};
36127        active = new Roo.util.MixedCollection();
36128        Roo.get(document).addKeyListener(27, function(){
36129            if(active.length > 0){
36130                hideAll();
36131            }
36132        });
36133    }
36134
36135    // private
36136    function hideAll(){
36137        if(active && active.length > 0){
36138            var c = active.clone();
36139            c.each(function(m){
36140                m.hide();
36141            });
36142        }
36143    }
36144
36145    // private
36146    function onHide(m){
36147        active.remove(m);
36148        if(active.length < 1){
36149            Roo.get(document).un("mousedown", onMouseDown);
36150            attached = false;
36151        }
36152    }
36153
36154    // private
36155    function onShow(m){
36156        var last = active.last();
36157        lastShow = new Date();
36158        active.add(m);
36159        if(!attached){
36160            Roo.get(document).on("mousedown", onMouseDown);
36161            attached = true;
36162        }
36163        if(m.parentMenu){
36164           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36165           m.parentMenu.activeChild = m;
36166        }else if(last && last.isVisible()){
36167           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36168        }
36169    }
36170
36171    // private
36172    function onBeforeHide(m){
36173        if(m.activeChild){
36174            m.activeChild.hide();
36175        }
36176        if(m.autoHideTimer){
36177            clearTimeout(m.autoHideTimer);
36178            delete m.autoHideTimer;
36179        }
36180    }
36181
36182    // private
36183    function onBeforeShow(m){
36184        var pm = m.parentMenu;
36185        if(!pm && !m.allowOtherMenus){
36186            hideAll();
36187        }else if(pm && pm.activeChild && active != m){
36188            pm.activeChild.hide();
36189        }
36190    }
36191
36192    // private
36193    function onMouseDown(e){
36194        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36195            hideAll();
36196        }
36197    }
36198
36199    // private
36200    function onBeforeCheck(mi, state){
36201        if(state){
36202            var g = groups[mi.group];
36203            for(var i = 0, l = g.length; i < l; i++){
36204                if(g[i] != mi){
36205                    g[i].setChecked(false);
36206                }
36207            }
36208        }
36209    }
36210
36211    return {
36212
36213        /**
36214         * Hides all menus that are currently visible
36215         */
36216        hideAll : function(){
36217             hideAll();  
36218        },
36219
36220        // private
36221        register : function(menu){
36222            if(!menus){
36223                init();
36224            }
36225            menus[menu.id] = menu;
36226            menu.on("beforehide", onBeforeHide);
36227            menu.on("hide", onHide);
36228            menu.on("beforeshow", onBeforeShow);
36229            menu.on("show", onShow);
36230            var g = menu.group;
36231            if(g && menu.events["checkchange"]){
36232                if(!groups[g]){
36233                    groups[g] = [];
36234                }
36235                groups[g].push(menu);
36236                menu.on("checkchange", onCheck);
36237            }
36238        },
36239
36240         /**
36241          * Returns a {@link Roo.menu.Menu} object
36242          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36243          * be used to generate and return a new Menu instance.
36244          */
36245        get : function(menu){
36246            if(typeof menu == "string"){ // menu id
36247                return menus[menu];
36248            }else if(menu.events){  // menu instance
36249                return menu;
36250            }else if(typeof menu.length == 'number'){ // array of menu items?
36251                return new Roo.menu.Menu({items:menu});
36252            }else{ // otherwise, must be a config
36253                return new Roo.menu.Menu(menu);
36254            }
36255        },
36256
36257        // private
36258        unregister : function(menu){
36259            delete menus[menu.id];
36260            menu.un("beforehide", onBeforeHide);
36261            menu.un("hide", onHide);
36262            menu.un("beforeshow", onBeforeShow);
36263            menu.un("show", onShow);
36264            var g = menu.group;
36265            if(g && menu.events["checkchange"]){
36266                groups[g].remove(menu);
36267                menu.un("checkchange", onCheck);
36268            }
36269        },
36270
36271        // private
36272        registerCheckable : function(menuItem){
36273            var g = menuItem.group;
36274            if(g){
36275                if(!groups[g]){
36276                    groups[g] = [];
36277                }
36278                groups[g].push(menuItem);
36279                menuItem.on("beforecheckchange", onBeforeCheck);
36280            }
36281        },
36282
36283        // private
36284        unregisterCheckable : function(menuItem){
36285            var g = menuItem.group;
36286            if(g){
36287                groups[g].remove(menuItem);
36288                menuItem.un("beforecheckchange", onBeforeCheck);
36289            }
36290        }
36291    };
36292 }();/*
36293  * Based on:
36294  * Ext JS Library 1.1.1
36295  * Copyright(c) 2006-2007, Ext JS, LLC.
36296  *
36297  * Originally Released Under LGPL - original licence link has changed is not relivant.
36298  *
36299  * Fork - LGPL
36300  * <script type="text/javascript">
36301  */
36302  
36303
36304 /**
36305  * @class Roo.menu.BaseItem
36306  * @extends Roo.Component
36307  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36308  * management and base configuration options shared by all menu components.
36309  * @constructor
36310  * Creates a new BaseItem
36311  * @param {Object} config Configuration options
36312  */
36313 Roo.menu.BaseItem = function(config){
36314     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36315
36316     this.addEvents({
36317         /**
36318          * @event click
36319          * Fires when this item is clicked
36320          * @param {Roo.menu.BaseItem} this
36321          * @param {Roo.EventObject} e
36322          */
36323         click: true,
36324         /**
36325          * @event activate
36326          * Fires when this item is activated
36327          * @param {Roo.menu.BaseItem} this
36328          */
36329         activate : true,
36330         /**
36331          * @event deactivate
36332          * Fires when this item is deactivated
36333          * @param {Roo.menu.BaseItem} this
36334          */
36335         deactivate : true
36336     });
36337
36338     if(this.handler){
36339         this.on("click", this.handler, this.scope, true);
36340     }
36341 };
36342
36343 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36344     /**
36345      * @cfg {Function} handler
36346      * A function that will handle the click event of this menu item (defaults to undefined)
36347      */
36348     /**
36349      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36350      */
36351     canActivate : false,
36352     
36353      /**
36354      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36355      */
36356     hidden: false,
36357     
36358     /**
36359      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36360      */
36361     activeClass : "x-menu-item-active",
36362     /**
36363      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36364      */
36365     hideOnClick : true,
36366     /**
36367      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36368      */
36369     hideDelay : 100,
36370
36371     // private
36372     ctype: "Roo.menu.BaseItem",
36373
36374     // private
36375     actionMode : "container",
36376
36377     // private
36378     render : function(container, parentMenu){
36379         this.parentMenu = parentMenu;
36380         Roo.menu.BaseItem.superclass.render.call(this, container);
36381         this.container.menuItemId = this.id;
36382     },
36383
36384     // private
36385     onRender : function(container, position){
36386         this.el = Roo.get(this.el);
36387         container.dom.appendChild(this.el.dom);
36388     },
36389
36390     // private
36391     onClick : function(e){
36392         if(!this.disabled && this.fireEvent("click", this, e) !== false
36393                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36394             this.handleClick(e);
36395         }else{
36396             e.stopEvent();
36397         }
36398     },
36399
36400     // private
36401     activate : function(){
36402         if(this.disabled){
36403             return false;
36404         }
36405         var li = this.container;
36406         li.addClass(this.activeClass);
36407         this.region = li.getRegion().adjust(2, 2, -2, -2);
36408         this.fireEvent("activate", this);
36409         return true;
36410     },
36411
36412     // private
36413     deactivate : function(){
36414         this.container.removeClass(this.activeClass);
36415         this.fireEvent("deactivate", this);
36416     },
36417
36418     // private
36419     shouldDeactivate : function(e){
36420         return !this.region || !this.region.contains(e.getPoint());
36421     },
36422
36423     // private
36424     handleClick : function(e){
36425         if(this.hideOnClick){
36426             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36427         }
36428     },
36429
36430     // private
36431     expandMenu : function(autoActivate){
36432         // do nothing
36433     },
36434
36435     // private
36436     hideMenu : function(){
36437         // do nothing
36438     }
36439 });/*
36440  * Based on:
36441  * Ext JS Library 1.1.1
36442  * Copyright(c) 2006-2007, Ext JS, LLC.
36443  *
36444  * Originally Released Under LGPL - original licence link has changed is not relivant.
36445  *
36446  * Fork - LGPL
36447  * <script type="text/javascript">
36448  */
36449  
36450 /**
36451  * @class Roo.menu.Adapter
36452  * @extends Roo.menu.BaseItem
36453  * 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.
36454  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36455  * @constructor
36456  * Creates a new Adapter
36457  * @param {Object} config Configuration options
36458  */
36459 Roo.menu.Adapter = function(component, config){
36460     Roo.menu.Adapter.superclass.constructor.call(this, config);
36461     this.component = component;
36462 };
36463 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36464     // private
36465     canActivate : true,
36466
36467     // private
36468     onRender : function(container, position){
36469         this.component.render(container);
36470         this.el = this.component.getEl();
36471     },
36472
36473     // private
36474     activate : function(){
36475         if(this.disabled){
36476             return false;
36477         }
36478         this.component.focus();
36479         this.fireEvent("activate", this);
36480         return true;
36481     },
36482
36483     // private
36484     deactivate : function(){
36485         this.fireEvent("deactivate", this);
36486     },
36487
36488     // private
36489     disable : function(){
36490         this.component.disable();
36491         Roo.menu.Adapter.superclass.disable.call(this);
36492     },
36493
36494     // private
36495     enable : function(){
36496         this.component.enable();
36497         Roo.menu.Adapter.superclass.enable.call(this);
36498     }
36499 });/*
36500  * Based on:
36501  * Ext JS Library 1.1.1
36502  * Copyright(c) 2006-2007, Ext JS, LLC.
36503  *
36504  * Originally Released Under LGPL - original licence link has changed is not relivant.
36505  *
36506  * Fork - LGPL
36507  * <script type="text/javascript">
36508  */
36509
36510 /**
36511  * @class Roo.menu.TextItem
36512  * @extends Roo.menu.BaseItem
36513  * Adds a static text string to a menu, usually used as either a heading or group separator.
36514  * Note: old style constructor with text is still supported.
36515  * 
36516  * @constructor
36517  * Creates a new TextItem
36518  * @param {Object} cfg Configuration
36519  */
36520 Roo.menu.TextItem = function(cfg){
36521     if (typeof(cfg) == 'string') {
36522         this.text = cfg;
36523     } else {
36524         Roo.apply(this,cfg);
36525     }
36526     
36527     Roo.menu.TextItem.superclass.constructor.call(this);
36528 };
36529
36530 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36531     /**
36532      * @cfg {Boolean} text Text to show on item.
36533      */
36534     text : '',
36535     
36536     /**
36537      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36538      */
36539     hideOnClick : false,
36540     /**
36541      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36542      */
36543     itemCls : "x-menu-text",
36544
36545     // private
36546     onRender : function(){
36547         var s = document.createElement("span");
36548         s.className = this.itemCls;
36549         s.innerHTML = this.text;
36550         this.el = s;
36551         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36552     }
36553 });/*
36554  * Based on:
36555  * Ext JS Library 1.1.1
36556  * Copyright(c) 2006-2007, Ext JS, LLC.
36557  *
36558  * Originally Released Under LGPL - original licence link has changed is not relivant.
36559  *
36560  * Fork - LGPL
36561  * <script type="text/javascript">
36562  */
36563
36564 /**
36565  * @class Roo.menu.Separator
36566  * @extends Roo.menu.BaseItem
36567  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36568  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36569  * @constructor
36570  * @param {Object} config Configuration options
36571  */
36572 Roo.menu.Separator = function(config){
36573     Roo.menu.Separator.superclass.constructor.call(this, config);
36574 };
36575
36576 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36577     /**
36578      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36579      */
36580     itemCls : "x-menu-sep",
36581     /**
36582      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36583      */
36584     hideOnClick : false,
36585
36586     // private
36587     onRender : function(li){
36588         var s = document.createElement("span");
36589         s.className = this.itemCls;
36590         s.innerHTML = "&#160;";
36591         this.el = s;
36592         li.addClass("x-menu-sep-li");
36593         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36594     }
36595 });/*
36596  * Based on:
36597  * Ext JS Library 1.1.1
36598  * Copyright(c) 2006-2007, Ext JS, LLC.
36599  *
36600  * Originally Released Under LGPL - original licence link has changed is not relivant.
36601  *
36602  * Fork - LGPL
36603  * <script type="text/javascript">
36604  */
36605 /**
36606  * @class Roo.menu.Item
36607  * @extends Roo.menu.BaseItem
36608  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36609  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36610  * activation and click handling.
36611  * @constructor
36612  * Creates a new Item
36613  * @param {Object} config Configuration options
36614  */
36615 Roo.menu.Item = function(config){
36616     Roo.menu.Item.superclass.constructor.call(this, config);
36617     if(this.menu){
36618         this.menu = Roo.menu.MenuMgr.get(this.menu);
36619     }
36620 };
36621 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36622     
36623     /**
36624      * @cfg {String} text
36625      * The text to show on the menu item.
36626      */
36627     text: '',
36628      /**
36629      * @cfg {String} HTML to render in menu
36630      * The text to show on the menu item (HTML version).
36631      */
36632     html: '',
36633     /**
36634      * @cfg {String} icon
36635      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36636      */
36637     icon: undefined,
36638     /**
36639      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36640      */
36641     itemCls : "x-menu-item",
36642     /**
36643      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36644      */
36645     canActivate : true,
36646     /**
36647      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36648      */
36649     showDelay: 200,
36650     // doc'd in BaseItem
36651     hideDelay: 200,
36652
36653     // private
36654     ctype: "Roo.menu.Item",
36655     
36656     // private
36657     onRender : function(container, position){
36658         var el = document.createElement("a");
36659         el.hideFocus = true;
36660         el.unselectable = "on";
36661         el.href = this.href || "#";
36662         if(this.hrefTarget){
36663             el.target = this.hrefTarget;
36664         }
36665         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36666         
36667         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36668         
36669         el.innerHTML = String.format(
36670                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36671                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36672         this.el = el;
36673         Roo.menu.Item.superclass.onRender.call(this, container, position);
36674     },
36675
36676     /**
36677      * Sets the text to display in this menu item
36678      * @param {String} text The text to display
36679      * @param {Boolean} isHTML true to indicate text is pure html.
36680      */
36681     setText : function(text, isHTML){
36682         if (isHTML) {
36683             this.html = text;
36684         } else {
36685             this.text = text;
36686             this.html = '';
36687         }
36688         if(this.rendered){
36689             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36690      
36691             this.el.update(String.format(
36692                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36693                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36694             this.parentMenu.autoWidth();
36695         }
36696     },
36697
36698     // private
36699     handleClick : function(e){
36700         if(!this.href){ // if no link defined, stop the event automatically
36701             e.stopEvent();
36702         }
36703         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36704     },
36705
36706     // private
36707     activate : function(autoExpand){
36708         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36709             this.focus();
36710             if(autoExpand){
36711                 this.expandMenu();
36712             }
36713         }
36714         return true;
36715     },
36716
36717     // private
36718     shouldDeactivate : function(e){
36719         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36720             if(this.menu && this.menu.isVisible()){
36721                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36722             }
36723             return true;
36724         }
36725         return false;
36726     },
36727
36728     // private
36729     deactivate : function(){
36730         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36731         this.hideMenu();
36732     },
36733
36734     // private
36735     expandMenu : function(autoActivate){
36736         if(!this.disabled && this.menu){
36737             clearTimeout(this.hideTimer);
36738             delete this.hideTimer;
36739             if(!this.menu.isVisible() && !this.showTimer){
36740                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36741             }else if (this.menu.isVisible() && autoActivate){
36742                 this.menu.tryActivate(0, 1);
36743             }
36744         }
36745     },
36746
36747     // private
36748     deferExpand : function(autoActivate){
36749         delete this.showTimer;
36750         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36751         if(autoActivate){
36752             this.menu.tryActivate(0, 1);
36753         }
36754     },
36755
36756     // private
36757     hideMenu : function(){
36758         clearTimeout(this.showTimer);
36759         delete this.showTimer;
36760         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36761             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36762         }
36763     },
36764
36765     // private
36766     deferHide : function(){
36767         delete this.hideTimer;
36768         this.menu.hide();
36769     }
36770 });/*
36771  * Based on:
36772  * Ext JS Library 1.1.1
36773  * Copyright(c) 2006-2007, Ext JS, LLC.
36774  *
36775  * Originally Released Under LGPL - original licence link has changed is not relivant.
36776  *
36777  * Fork - LGPL
36778  * <script type="text/javascript">
36779  */
36780  
36781 /**
36782  * @class Roo.menu.CheckItem
36783  * @extends Roo.menu.Item
36784  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36785  * @constructor
36786  * Creates a new CheckItem
36787  * @param {Object} config Configuration options
36788  */
36789 Roo.menu.CheckItem = function(config){
36790     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36791     this.addEvents({
36792         /**
36793          * @event beforecheckchange
36794          * Fires before the checked value is set, providing an opportunity to cancel if needed
36795          * @param {Roo.menu.CheckItem} this
36796          * @param {Boolean} checked The new checked value that will be set
36797          */
36798         "beforecheckchange" : true,
36799         /**
36800          * @event checkchange
36801          * Fires after the checked value has been set
36802          * @param {Roo.menu.CheckItem} this
36803          * @param {Boolean} checked The checked value that was set
36804          */
36805         "checkchange" : true
36806     });
36807     if(this.checkHandler){
36808         this.on('checkchange', this.checkHandler, this.scope);
36809     }
36810 };
36811 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36812     /**
36813      * @cfg {String} group
36814      * All check items with the same group name will automatically be grouped into a single-select
36815      * radio button group (defaults to '')
36816      */
36817     /**
36818      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36819      */
36820     itemCls : "x-menu-item x-menu-check-item",
36821     /**
36822      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36823      */
36824     groupClass : "x-menu-group-item",
36825
36826     /**
36827      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36828      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36829      * initialized with checked = true will be rendered as checked.
36830      */
36831     checked: false,
36832
36833     // private
36834     ctype: "Roo.menu.CheckItem",
36835
36836     // private
36837     onRender : function(c){
36838         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36839         if(this.group){
36840             this.el.addClass(this.groupClass);
36841         }
36842         Roo.menu.MenuMgr.registerCheckable(this);
36843         if(this.checked){
36844             this.checked = false;
36845             this.setChecked(true, true);
36846         }
36847     },
36848
36849     // private
36850     destroy : function(){
36851         if(this.rendered){
36852             Roo.menu.MenuMgr.unregisterCheckable(this);
36853         }
36854         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36855     },
36856
36857     /**
36858      * Set the checked state of this item
36859      * @param {Boolean} checked The new checked value
36860      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36861      */
36862     setChecked : function(state, suppressEvent){
36863         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36864             if(this.container){
36865                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36866             }
36867             this.checked = state;
36868             if(suppressEvent !== true){
36869                 this.fireEvent("checkchange", this, state);
36870             }
36871         }
36872     },
36873
36874     // private
36875     handleClick : function(e){
36876        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36877            this.setChecked(!this.checked);
36878        }
36879        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36880     }
36881 });/*
36882  * Based on:
36883  * Ext JS Library 1.1.1
36884  * Copyright(c) 2006-2007, Ext JS, LLC.
36885  *
36886  * Originally Released Under LGPL - original licence link has changed is not relivant.
36887  *
36888  * Fork - LGPL
36889  * <script type="text/javascript">
36890  */
36891  
36892 /**
36893  * @class Roo.menu.DateItem
36894  * @extends Roo.menu.Adapter
36895  * A menu item that wraps the {@link Roo.DatPicker} component.
36896  * @constructor
36897  * Creates a new DateItem
36898  * @param {Object} config Configuration options
36899  */
36900 Roo.menu.DateItem = function(config){
36901     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36902     /** The Roo.DatePicker object @type Roo.DatePicker */
36903     this.picker = this.component;
36904     this.addEvents({select: true});
36905     
36906     this.picker.on("render", function(picker){
36907         picker.getEl().swallowEvent("click");
36908         picker.container.addClass("x-menu-date-item");
36909     });
36910
36911     this.picker.on("select", this.onSelect, this);
36912 };
36913
36914 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36915     // private
36916     onSelect : function(picker, date){
36917         this.fireEvent("select", this, date, picker);
36918         Roo.menu.DateItem.superclass.handleClick.call(this);
36919     }
36920 });/*
36921  * Based on:
36922  * Ext JS Library 1.1.1
36923  * Copyright(c) 2006-2007, Ext JS, LLC.
36924  *
36925  * Originally Released Under LGPL - original licence link has changed is not relivant.
36926  *
36927  * Fork - LGPL
36928  * <script type="text/javascript">
36929  */
36930  
36931 /**
36932  * @class Roo.menu.ColorItem
36933  * @extends Roo.menu.Adapter
36934  * A menu item that wraps the {@link Roo.ColorPalette} component.
36935  * @constructor
36936  * Creates a new ColorItem
36937  * @param {Object} config Configuration options
36938  */
36939 Roo.menu.ColorItem = function(config){
36940     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36941     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36942     this.palette = this.component;
36943     this.relayEvents(this.palette, ["select"]);
36944     if(this.selectHandler){
36945         this.on('select', this.selectHandler, this.scope);
36946     }
36947 };
36948 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36949  * Based on:
36950  * Ext JS Library 1.1.1
36951  * Copyright(c) 2006-2007, Ext JS, LLC.
36952  *
36953  * Originally Released Under LGPL - original licence link has changed is not relivant.
36954  *
36955  * Fork - LGPL
36956  * <script type="text/javascript">
36957  */
36958  
36959
36960 /**
36961  * @class Roo.menu.DateMenu
36962  * @extends Roo.menu.Menu
36963  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36964  * @constructor
36965  * Creates a new DateMenu
36966  * @param {Object} config Configuration options
36967  */
36968 Roo.menu.DateMenu = function(config){
36969     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36970     this.plain = true;
36971     var di = new Roo.menu.DateItem(config);
36972     this.add(di);
36973     /**
36974      * The {@link Roo.DatePicker} instance for this DateMenu
36975      * @type DatePicker
36976      */
36977     this.picker = di.picker;
36978     /**
36979      * @event select
36980      * @param {DatePicker} picker
36981      * @param {Date} date
36982      */
36983     this.relayEvents(di, ["select"]);
36984     this.on('beforeshow', function(){
36985         if(this.picker){
36986             this.picker.hideMonthPicker(false);
36987         }
36988     }, this);
36989 };
36990 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36991     cls:'x-date-menu'
36992 });/*
36993  * Based on:
36994  * Ext JS Library 1.1.1
36995  * Copyright(c) 2006-2007, Ext JS, LLC.
36996  *
36997  * Originally Released Under LGPL - original licence link has changed is not relivant.
36998  *
36999  * Fork - LGPL
37000  * <script type="text/javascript">
37001  */
37002  
37003
37004 /**
37005  * @class Roo.menu.ColorMenu
37006  * @extends Roo.menu.Menu
37007  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37008  * @constructor
37009  * Creates a new ColorMenu
37010  * @param {Object} config Configuration options
37011  */
37012 Roo.menu.ColorMenu = function(config){
37013     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37014     this.plain = true;
37015     var ci = new Roo.menu.ColorItem(config);
37016     this.add(ci);
37017     /**
37018      * The {@link Roo.ColorPalette} instance for this ColorMenu
37019      * @type ColorPalette
37020      */
37021     this.palette = ci.palette;
37022     /**
37023      * @event select
37024      * @param {ColorPalette} palette
37025      * @param {String} color
37026      */
37027     this.relayEvents(ci, ["select"]);
37028 };
37029 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37030  * Based on:
37031  * Ext JS Library 1.1.1
37032  * Copyright(c) 2006-2007, Ext JS, LLC.
37033  *
37034  * Originally Released Under LGPL - original licence link has changed is not relivant.
37035  *
37036  * Fork - LGPL
37037  * <script type="text/javascript">
37038  */
37039  
37040 /**
37041  * @class Roo.form.Field
37042  * @extends Roo.BoxComponent
37043  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37044  * @constructor
37045  * Creates a new Field
37046  * @param {Object} config Configuration options
37047  */
37048 Roo.form.Field = function(config){
37049     Roo.form.Field.superclass.constructor.call(this, config);
37050 };
37051
37052 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37053     /**
37054      * @cfg {String} fieldLabel Label to use when rendering a form.
37055      */
37056        /**
37057      * @cfg {String} qtip Mouse over tip
37058      */
37059      
37060     /**
37061      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37062      */
37063     invalidClass : "x-form-invalid",
37064     /**
37065      * @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")
37066      */
37067     invalidText : "The value in this field is invalid",
37068     /**
37069      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37070      */
37071     focusClass : "x-form-focus",
37072     /**
37073      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37074       automatic validation (defaults to "keyup").
37075      */
37076     validationEvent : "keyup",
37077     /**
37078      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37079      */
37080     validateOnBlur : true,
37081     /**
37082      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37083      */
37084     validationDelay : 250,
37085     /**
37086      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37087      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37088      */
37089     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37090     /**
37091      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37092      */
37093     fieldClass : "x-form-field",
37094     /**
37095      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37096      *<pre>
37097 Value         Description
37098 -----------   ----------------------------------------------------------------------
37099 qtip          Display a quick tip when the user hovers over the field
37100 title         Display a default browser title attribute popup
37101 under         Add a block div beneath the field containing the error text
37102 side          Add an error icon to the right of the field with a popup on hover
37103 [element id]  Add the error text directly to the innerHTML of the specified element
37104 </pre>
37105      */
37106     msgTarget : 'qtip',
37107     /**
37108      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37109      */
37110     msgFx : 'normal',
37111
37112     /**
37113      * @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.
37114      */
37115     readOnly : false,
37116
37117     /**
37118      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37119      */
37120     disabled : false,
37121
37122     /**
37123      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37124      */
37125     inputType : undefined,
37126     
37127     /**
37128      * @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).
37129          */
37130         tabIndex : undefined,
37131         
37132     // private
37133     isFormField : true,
37134
37135     // private
37136     hasFocus : false,
37137     /**
37138      * @property {Roo.Element} fieldEl
37139      * Element Containing the rendered Field (with label etc.)
37140      */
37141     /**
37142      * @cfg {Mixed} value A value to initialize this field with.
37143      */
37144     value : undefined,
37145
37146     /**
37147      * @cfg {String} name The field's HTML name attribute.
37148      */
37149     /**
37150      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37151      */
37152
37153         // private ??
37154         initComponent : function(){
37155         Roo.form.Field.superclass.initComponent.call(this);
37156         this.addEvents({
37157             /**
37158              * @event focus
37159              * Fires when this field receives input focus.
37160              * @param {Roo.form.Field} this
37161              */
37162             focus : true,
37163             /**
37164              * @event blur
37165              * Fires when this field loses input focus.
37166              * @param {Roo.form.Field} this
37167              */
37168             blur : true,
37169             /**
37170              * @event specialkey
37171              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37172              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37173              * @param {Roo.form.Field} this
37174              * @param {Roo.EventObject} e The event object
37175              */
37176             specialkey : true,
37177             /**
37178              * @event change
37179              * Fires just before the field blurs if the field value has changed.
37180              * @param {Roo.form.Field} this
37181              * @param {Mixed} newValue The new value
37182              * @param {Mixed} oldValue The original value
37183              */
37184             change : true,
37185             /**
37186              * @event invalid
37187              * Fires after the field has been marked as invalid.
37188              * @param {Roo.form.Field} this
37189              * @param {String} msg The validation message
37190              */
37191             invalid : true,
37192             /**
37193              * @event valid
37194              * Fires after the field has been validated with no errors.
37195              * @param {Roo.form.Field} this
37196              */
37197             valid : true,
37198              /**
37199              * @event keyup
37200              * Fires after the key up
37201              * @param {Roo.form.Field} this
37202              * @param {Roo.EventObject}  e The event Object
37203              */
37204             keyup : true
37205         });
37206     },
37207
37208     /**
37209      * Returns the name attribute of the field if available
37210      * @return {String} name The field name
37211      */
37212     getName: function(){
37213          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37214     },
37215
37216     // private
37217     onRender : function(ct, position){
37218         Roo.form.Field.superclass.onRender.call(this, ct, position);
37219         if(!this.el){
37220             var cfg = this.getAutoCreate();
37221             if(!cfg.name){
37222                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37223             }
37224             if (!cfg.name.length) {
37225                 delete cfg.name;
37226             }
37227             if(this.inputType){
37228                 cfg.type = this.inputType;
37229             }
37230             this.el = ct.createChild(cfg, position);
37231         }
37232         var type = this.el.dom.type;
37233         if(type){
37234             if(type == 'password'){
37235                 type = 'text';
37236             }
37237             this.el.addClass('x-form-'+type);
37238         }
37239         if(this.readOnly){
37240             this.el.dom.readOnly = true;
37241         }
37242         if(this.tabIndex !== undefined){
37243             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37244         }
37245
37246         this.el.addClass([this.fieldClass, this.cls]);
37247         this.initValue();
37248     },
37249
37250     /**
37251      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37252      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37253      * @return {Roo.form.Field} this
37254      */
37255     applyTo : function(target){
37256         this.allowDomMove = false;
37257         this.el = Roo.get(target);
37258         this.render(this.el.dom.parentNode);
37259         return this;
37260     },
37261
37262     // private
37263     initValue : function(){
37264         if(this.value !== undefined){
37265             this.setValue(this.value);
37266         }else if(this.el.dom.value.length > 0){
37267             this.setValue(this.el.dom.value);
37268         }
37269     },
37270
37271     /**
37272      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37273      */
37274     isDirty : function() {
37275         if(this.disabled) {
37276             return false;
37277         }
37278         return String(this.getValue()) !== String(this.originalValue);
37279     },
37280
37281     // private
37282     afterRender : function(){
37283         Roo.form.Field.superclass.afterRender.call(this);
37284         this.initEvents();
37285     },
37286
37287     // private
37288     fireKey : function(e){
37289         //Roo.log('field ' + e.getKey());
37290         if(e.isNavKeyPress()){
37291             this.fireEvent("specialkey", this, e);
37292         }
37293     },
37294
37295     /**
37296      * Resets the current field value to the originally loaded value and clears any validation messages
37297      */
37298     reset : function(){
37299         this.setValue(this.resetValue);
37300         this.clearInvalid();
37301     },
37302
37303     // private
37304     initEvents : function(){
37305         // safari killled keypress - so keydown is now used..
37306         this.el.on("keydown" , this.fireKey,  this);
37307         this.el.on("focus", this.onFocus,  this);
37308         this.el.on("blur", this.onBlur,  this);
37309         this.el.relayEvent('keyup', this);
37310
37311         // reference to original value for reset
37312         this.originalValue = this.getValue();
37313         this.resetValue =  this.getValue();
37314     },
37315
37316     // private
37317     onFocus : function(){
37318         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37319             this.el.addClass(this.focusClass);
37320         }
37321         if(!this.hasFocus){
37322             this.hasFocus = true;
37323             this.startValue = this.getValue();
37324             this.fireEvent("focus", this);
37325         }
37326     },
37327
37328     beforeBlur : Roo.emptyFn,
37329
37330     // private
37331     onBlur : function(){
37332         this.beforeBlur();
37333         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37334             this.el.removeClass(this.focusClass);
37335         }
37336         this.hasFocus = false;
37337         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37338             this.validate();
37339         }
37340         var v = this.getValue();
37341         if(String(v) !== String(this.startValue)){
37342             this.fireEvent('change', this, v, this.startValue);
37343         }
37344         this.fireEvent("blur", this);
37345     },
37346
37347     /**
37348      * Returns whether or not the field value is currently valid
37349      * @param {Boolean} preventMark True to disable marking the field invalid
37350      * @return {Boolean} True if the value is valid, else false
37351      */
37352     isValid : function(preventMark){
37353         if(this.disabled){
37354             return true;
37355         }
37356         var restore = this.preventMark;
37357         this.preventMark = preventMark === true;
37358         var v = this.validateValue(this.processValue(this.getRawValue()));
37359         this.preventMark = restore;
37360         return v;
37361     },
37362
37363     /**
37364      * Validates the field value
37365      * @return {Boolean} True if the value is valid, else false
37366      */
37367     validate : function(){
37368         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37369             this.clearInvalid();
37370             return true;
37371         }
37372         return false;
37373     },
37374
37375     processValue : function(value){
37376         return value;
37377     },
37378
37379     // private
37380     // Subclasses should provide the validation implementation by overriding this
37381     validateValue : function(value){
37382         return true;
37383     },
37384
37385     /**
37386      * Mark this field as invalid
37387      * @param {String} msg The validation message
37388      */
37389     markInvalid : function(msg){
37390         if(!this.rendered || this.preventMark){ // not rendered
37391             return;
37392         }
37393         
37394         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37395         
37396         obj.el.addClass(this.invalidClass);
37397         msg = msg || this.invalidText;
37398         switch(this.msgTarget){
37399             case 'qtip':
37400                 obj.el.dom.qtip = msg;
37401                 obj.el.dom.qclass = 'x-form-invalid-tip';
37402                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37403                     Roo.QuickTips.enable();
37404                 }
37405                 break;
37406             case 'title':
37407                 this.el.dom.title = msg;
37408                 break;
37409             case 'under':
37410                 if(!this.errorEl){
37411                     var elp = this.el.findParent('.x-form-element', 5, true);
37412                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37413                     this.errorEl.setWidth(elp.getWidth(true)-20);
37414                 }
37415                 this.errorEl.update(msg);
37416                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37417                 break;
37418             case 'side':
37419                 if(!this.errorIcon){
37420                     var elp = this.el.findParent('.x-form-element', 5, true);
37421                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37422                 }
37423                 this.alignErrorIcon();
37424                 this.errorIcon.dom.qtip = msg;
37425                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37426                 this.errorIcon.show();
37427                 this.on('resize', this.alignErrorIcon, this);
37428                 break;
37429             default:
37430                 var t = Roo.getDom(this.msgTarget);
37431                 t.innerHTML = msg;
37432                 t.style.display = this.msgDisplay;
37433                 break;
37434         }
37435         this.fireEvent('invalid', this, msg);
37436     },
37437
37438     // private
37439     alignErrorIcon : function(){
37440         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37441     },
37442
37443     /**
37444      * Clear any invalid styles/messages for this field
37445      */
37446     clearInvalid : function(){
37447         if(!this.rendered || this.preventMark){ // not rendered
37448             return;
37449         }
37450         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37451         
37452         obj.el.removeClass(this.invalidClass);
37453         switch(this.msgTarget){
37454             case 'qtip':
37455                 obj.el.dom.qtip = '';
37456                 break;
37457             case 'title':
37458                 this.el.dom.title = '';
37459                 break;
37460             case 'under':
37461                 if(this.errorEl){
37462                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37463                 }
37464                 break;
37465             case 'side':
37466                 if(this.errorIcon){
37467                     this.errorIcon.dom.qtip = '';
37468                     this.errorIcon.hide();
37469                     this.un('resize', this.alignErrorIcon, this);
37470                 }
37471                 break;
37472             default:
37473                 var t = Roo.getDom(this.msgTarget);
37474                 t.innerHTML = '';
37475                 t.style.display = 'none';
37476                 break;
37477         }
37478         this.fireEvent('valid', this);
37479     },
37480
37481     /**
37482      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37483      * @return {Mixed} value The field value
37484      */
37485     getRawValue : function(){
37486         var v = this.el.getValue();
37487         
37488         return v;
37489     },
37490
37491     /**
37492      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37493      * @return {Mixed} value The field value
37494      */
37495     getValue : function(){
37496         var v = this.el.getValue();
37497          
37498         return v;
37499     },
37500
37501     /**
37502      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37503      * @param {Mixed} value The value to set
37504      */
37505     setRawValue : function(v){
37506         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37507     },
37508
37509     /**
37510      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37511      * @param {Mixed} value The value to set
37512      */
37513     setValue : function(v){
37514         this.value = v;
37515         if(this.rendered){
37516             this.el.dom.value = (v === null || v === undefined ? '' : v);
37517              this.validate();
37518         }
37519     },
37520
37521     adjustSize : function(w, h){
37522         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37523         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37524         return s;
37525     },
37526
37527     adjustWidth : function(tag, w){
37528         tag = tag.toLowerCase();
37529         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37530             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37531                 if(tag == 'input'){
37532                     return w + 2;
37533                 }
37534                 if(tag == 'textarea'){
37535                     return w-2;
37536                 }
37537             }else if(Roo.isOpera){
37538                 if(tag == 'input'){
37539                     return w + 2;
37540                 }
37541                 if(tag == 'textarea'){
37542                     return w-2;
37543                 }
37544             }
37545         }
37546         return w;
37547     }
37548 });
37549
37550
37551 // anything other than normal should be considered experimental
37552 Roo.form.Field.msgFx = {
37553     normal : {
37554         show: function(msgEl, f){
37555             msgEl.setDisplayed('block');
37556         },
37557
37558         hide : function(msgEl, f){
37559             msgEl.setDisplayed(false).update('');
37560         }
37561     },
37562
37563     slide : {
37564         show: function(msgEl, f){
37565             msgEl.slideIn('t', {stopFx:true});
37566         },
37567
37568         hide : function(msgEl, f){
37569             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37570         }
37571     },
37572
37573     slideRight : {
37574         show: function(msgEl, f){
37575             msgEl.fixDisplay();
37576             msgEl.alignTo(f.el, 'tl-tr');
37577             msgEl.slideIn('l', {stopFx:true});
37578         },
37579
37580         hide : function(msgEl, f){
37581             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37582         }
37583     }
37584 };/*
37585  * Based on:
37586  * Ext JS Library 1.1.1
37587  * Copyright(c) 2006-2007, Ext JS, LLC.
37588  *
37589  * Originally Released Under LGPL - original licence link has changed is not relivant.
37590  *
37591  * Fork - LGPL
37592  * <script type="text/javascript">
37593  */
37594  
37595
37596 /**
37597  * @class Roo.form.TextField
37598  * @extends Roo.form.Field
37599  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37600  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37601  * @constructor
37602  * Creates a new TextField
37603  * @param {Object} config Configuration options
37604  */
37605 Roo.form.TextField = function(config){
37606     Roo.form.TextField.superclass.constructor.call(this, config);
37607     this.addEvents({
37608         /**
37609          * @event autosize
37610          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37611          * according to the default logic, but this event provides a hook for the developer to apply additional
37612          * logic at runtime to resize the field if needed.
37613              * @param {Roo.form.Field} this This text field
37614              * @param {Number} width The new field width
37615              */
37616         autosize : true
37617     });
37618 };
37619
37620 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37621     /**
37622      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37623      */
37624     grow : false,
37625     /**
37626      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37627      */
37628     growMin : 30,
37629     /**
37630      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37631      */
37632     growMax : 800,
37633     /**
37634      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37635      */
37636     vtype : null,
37637     /**
37638      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37639      */
37640     maskRe : null,
37641     /**
37642      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37643      */
37644     disableKeyFilter : false,
37645     /**
37646      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37647      */
37648     allowBlank : true,
37649     /**
37650      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37651      */
37652     minLength : 0,
37653     /**
37654      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37655      */
37656     maxLength : Number.MAX_VALUE,
37657     /**
37658      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37659      */
37660     minLengthText : "The minimum length for this field is {0}",
37661     /**
37662      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37663      */
37664     maxLengthText : "The maximum length for this field is {0}",
37665     /**
37666      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37667      */
37668     selectOnFocus : false,
37669     /**
37670      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37671      */
37672     blankText : "This field is required",
37673     /**
37674      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37675      * If available, this function will be called only after the basic validators all return true, and will be passed the
37676      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37677      */
37678     validator : null,
37679     /**
37680      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37681      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37682      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37683      */
37684     regex : null,
37685     /**
37686      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37687      */
37688     regexText : "",
37689     /**
37690      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37691      */
37692     emptyText : null,
37693    
37694
37695     // private
37696     initEvents : function()
37697     {
37698         if (this.emptyText) {
37699             this.el.attr('placeholder', this.emptyText);
37700         }
37701         
37702         Roo.form.TextField.superclass.initEvents.call(this);
37703         if(this.validationEvent == 'keyup'){
37704             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37705             this.el.on('keyup', this.filterValidation, this);
37706         }
37707         else if(this.validationEvent !== false){
37708             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37709         }
37710         
37711         if(this.selectOnFocus){
37712             this.on("focus", this.preFocus, this);
37713             
37714         }
37715         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37716             this.el.on("keypress", this.filterKeys, this);
37717         }
37718         if(this.grow){
37719             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37720             this.el.on("click", this.autoSize,  this);
37721         }
37722         if(this.el.is('input[type=password]') && Roo.isSafari){
37723             this.el.on('keydown', this.SafariOnKeyDown, this);
37724         }
37725     },
37726
37727     processValue : function(value){
37728         if(this.stripCharsRe){
37729             var newValue = value.replace(this.stripCharsRe, '');
37730             if(newValue !== value){
37731                 this.setRawValue(newValue);
37732                 return newValue;
37733             }
37734         }
37735         return value;
37736     },
37737
37738     filterValidation : function(e){
37739         if(!e.isNavKeyPress()){
37740             this.validationTask.delay(this.validationDelay);
37741         }
37742     },
37743
37744     // private
37745     onKeyUp : function(e){
37746         if(!e.isNavKeyPress()){
37747             this.autoSize();
37748         }
37749     },
37750
37751     /**
37752      * Resets the current field value to the originally-loaded value and clears any validation messages.
37753      *  
37754      */
37755     reset : function(){
37756         Roo.form.TextField.superclass.reset.call(this);
37757        
37758     },
37759
37760     
37761     // private
37762     preFocus : function(){
37763         
37764         if(this.selectOnFocus){
37765             this.el.dom.select();
37766         }
37767     },
37768
37769     
37770     // private
37771     filterKeys : function(e){
37772         var k = e.getKey();
37773         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37774             return;
37775         }
37776         var c = e.getCharCode(), cc = String.fromCharCode(c);
37777         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37778             return;
37779         }
37780         if(!this.maskRe.test(cc)){
37781             e.stopEvent();
37782         }
37783     },
37784
37785     setValue : function(v){
37786         
37787         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37788         
37789         this.autoSize();
37790     },
37791
37792     /**
37793      * Validates a value according to the field's validation rules and marks the field as invalid
37794      * if the validation fails
37795      * @param {Mixed} value The value to validate
37796      * @return {Boolean} True if the value is valid, else false
37797      */
37798     validateValue : function(value){
37799         if(value.length < 1)  { // if it's blank
37800              if(this.allowBlank){
37801                 this.clearInvalid();
37802                 return true;
37803              }else{
37804                 this.markInvalid(this.blankText);
37805                 return false;
37806              }
37807         }
37808         if(value.length < this.minLength){
37809             this.markInvalid(String.format(this.minLengthText, this.minLength));
37810             return false;
37811         }
37812         if(value.length > this.maxLength){
37813             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37814             return false;
37815         }
37816         if(this.vtype){
37817             var vt = Roo.form.VTypes;
37818             if(!vt[this.vtype](value, this)){
37819                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37820                 return false;
37821             }
37822         }
37823         if(typeof this.validator == "function"){
37824             var msg = this.validator(value);
37825             if(msg !== true){
37826                 this.markInvalid(msg);
37827                 return false;
37828             }
37829         }
37830         if(this.regex && !this.regex.test(value)){
37831             this.markInvalid(this.regexText);
37832             return false;
37833         }
37834         return true;
37835     },
37836
37837     /**
37838      * Selects text in this field
37839      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37840      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37841      */
37842     selectText : function(start, end){
37843         var v = this.getRawValue();
37844         if(v.length > 0){
37845             start = start === undefined ? 0 : start;
37846             end = end === undefined ? v.length : end;
37847             var d = this.el.dom;
37848             if(d.setSelectionRange){
37849                 d.setSelectionRange(start, end);
37850             }else if(d.createTextRange){
37851                 var range = d.createTextRange();
37852                 range.moveStart("character", start);
37853                 range.moveEnd("character", v.length-end);
37854                 range.select();
37855             }
37856         }
37857     },
37858
37859     /**
37860      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37861      * This only takes effect if grow = true, and fires the autosize event.
37862      */
37863     autoSize : function(){
37864         if(!this.grow || !this.rendered){
37865             return;
37866         }
37867         if(!this.metrics){
37868             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37869         }
37870         var el = this.el;
37871         var v = el.dom.value;
37872         var d = document.createElement('div');
37873         d.appendChild(document.createTextNode(v));
37874         v = d.innerHTML;
37875         d = null;
37876         v += "&#160;";
37877         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37878         this.el.setWidth(w);
37879         this.fireEvent("autosize", this, w);
37880     },
37881     
37882     // private
37883     SafariOnKeyDown : function(event)
37884     {
37885         // this is a workaround for a password hang bug on chrome/ webkit.
37886         
37887         var isSelectAll = false;
37888         
37889         if(this.el.dom.selectionEnd > 0){
37890             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37891         }
37892         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37893             event.preventDefault();
37894             this.setValue('');
37895             return;
37896         }
37897         
37898         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37899             
37900             event.preventDefault();
37901             // this is very hacky as keydown always get's upper case.
37902             
37903             var cc = String.fromCharCode(event.getCharCode());
37904             
37905             
37906             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37907             
37908         }
37909         
37910         
37911     }
37912 });/*
37913  * Based on:
37914  * Ext JS Library 1.1.1
37915  * Copyright(c) 2006-2007, Ext JS, LLC.
37916  *
37917  * Originally Released Under LGPL - original licence link has changed is not relivant.
37918  *
37919  * Fork - LGPL
37920  * <script type="text/javascript">
37921  */
37922  
37923 /**
37924  * @class Roo.form.Hidden
37925  * @extends Roo.form.TextField
37926  * Simple Hidden element used on forms 
37927  * 
37928  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37929  * 
37930  * @constructor
37931  * Creates a new Hidden form element.
37932  * @param {Object} config Configuration options
37933  */
37934
37935
37936
37937 // easy hidden field...
37938 Roo.form.Hidden = function(config){
37939     Roo.form.Hidden.superclass.constructor.call(this, config);
37940 };
37941   
37942 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37943     fieldLabel:      '',
37944     inputType:      'hidden',
37945     width:          50,
37946     allowBlank:     true,
37947     labelSeparator: '',
37948     hidden:         true,
37949     itemCls :       'x-form-item-display-none'
37950
37951
37952 });
37953
37954
37955 /*
37956  * Based on:
37957  * Ext JS Library 1.1.1
37958  * Copyright(c) 2006-2007, Ext JS, LLC.
37959  *
37960  * Originally Released Under LGPL - original licence link has changed is not relivant.
37961  *
37962  * Fork - LGPL
37963  * <script type="text/javascript">
37964  */
37965  
37966 /**
37967  * @class Roo.form.TriggerField
37968  * @extends Roo.form.TextField
37969  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37970  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37971  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37972  * for which you can provide a custom implementation.  For example:
37973  * <pre><code>
37974 var trigger = new Roo.form.TriggerField();
37975 trigger.onTriggerClick = myTriggerFn;
37976 trigger.applyTo('my-field');
37977 </code></pre>
37978  *
37979  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37980  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37981  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37982  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37983  * @constructor
37984  * Create a new TriggerField.
37985  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37986  * to the base TextField)
37987  */
37988 Roo.form.TriggerField = function(config){
37989     this.mimicing = false;
37990     Roo.form.TriggerField.superclass.constructor.call(this, config);
37991 };
37992
37993 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37994     /**
37995      * @cfg {String} triggerClass A CSS class to apply to the trigger
37996      */
37997     /**
37998      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37999      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
38000      */
38001     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38002     /**
38003      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38004      */
38005     hideTrigger:false,
38006
38007     /** @cfg {Boolean} grow @hide */
38008     /** @cfg {Number} growMin @hide */
38009     /** @cfg {Number} growMax @hide */
38010
38011     /**
38012      * @hide 
38013      * @method
38014      */
38015     autoSize: Roo.emptyFn,
38016     // private
38017     monitorTab : true,
38018     // private
38019     deferHeight : true,
38020
38021     
38022     actionMode : 'wrap',
38023     // private
38024     onResize : function(w, h){
38025         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38026         if(typeof w == 'number'){
38027             var x = w - this.trigger.getWidth();
38028             this.el.setWidth(this.adjustWidth('input', x));
38029             this.trigger.setStyle('left', x+'px');
38030         }
38031     },
38032
38033     // private
38034     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38035
38036     // private
38037     getResizeEl : function(){
38038         return this.wrap;
38039     },
38040
38041     // private
38042     getPositionEl : function(){
38043         return this.wrap;
38044     },
38045
38046     // private
38047     alignErrorIcon : function(){
38048         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38049     },
38050
38051     // private
38052     onRender : function(ct, position){
38053         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38054         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38055         this.trigger = this.wrap.createChild(this.triggerConfig ||
38056                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38057         if(this.hideTrigger){
38058             this.trigger.setDisplayed(false);
38059         }
38060         this.initTrigger();
38061         if(!this.width){
38062             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38063         }
38064     },
38065
38066     // private
38067     initTrigger : function(){
38068         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38069         this.trigger.addClassOnOver('x-form-trigger-over');
38070         this.trigger.addClassOnClick('x-form-trigger-click');
38071     },
38072
38073     // private
38074     onDestroy : function(){
38075         if(this.trigger){
38076             this.trigger.removeAllListeners();
38077             this.trigger.remove();
38078         }
38079         if(this.wrap){
38080             this.wrap.remove();
38081         }
38082         Roo.form.TriggerField.superclass.onDestroy.call(this);
38083     },
38084
38085     // private
38086     onFocus : function(){
38087         Roo.form.TriggerField.superclass.onFocus.call(this);
38088         if(!this.mimicing){
38089             this.wrap.addClass('x-trigger-wrap-focus');
38090             this.mimicing = true;
38091             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38092             if(this.monitorTab){
38093                 this.el.on("keydown", this.checkTab, this);
38094             }
38095         }
38096     },
38097
38098     // private
38099     checkTab : function(e){
38100         if(e.getKey() == e.TAB){
38101             this.triggerBlur();
38102         }
38103     },
38104
38105     // private
38106     onBlur : function(){
38107         // do nothing
38108     },
38109
38110     // private
38111     mimicBlur : function(e, t){
38112         if(!this.wrap.contains(t) && this.validateBlur()){
38113             this.triggerBlur();
38114         }
38115     },
38116
38117     // private
38118     triggerBlur : function(){
38119         this.mimicing = false;
38120         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38121         if(this.monitorTab){
38122             this.el.un("keydown", this.checkTab, this);
38123         }
38124         this.wrap.removeClass('x-trigger-wrap-focus');
38125         Roo.form.TriggerField.superclass.onBlur.call(this);
38126     },
38127
38128     // private
38129     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38130     validateBlur : function(e, t){
38131         return true;
38132     },
38133
38134     // private
38135     onDisable : function(){
38136         Roo.form.TriggerField.superclass.onDisable.call(this);
38137         if(this.wrap){
38138             this.wrap.addClass('x-item-disabled');
38139         }
38140     },
38141
38142     // private
38143     onEnable : function(){
38144         Roo.form.TriggerField.superclass.onEnable.call(this);
38145         if(this.wrap){
38146             this.wrap.removeClass('x-item-disabled');
38147         }
38148     },
38149
38150     // private
38151     onShow : function(){
38152         var ae = this.getActionEl();
38153         
38154         if(ae){
38155             ae.dom.style.display = '';
38156             ae.dom.style.visibility = 'visible';
38157         }
38158     },
38159
38160     // private
38161     
38162     onHide : function(){
38163         var ae = this.getActionEl();
38164         ae.dom.style.display = 'none';
38165     },
38166
38167     /**
38168      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38169      * by an implementing function.
38170      * @method
38171      * @param {EventObject} e
38172      */
38173     onTriggerClick : Roo.emptyFn
38174 });
38175
38176 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38177 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38178 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38179 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38180     initComponent : function(){
38181         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38182
38183         this.triggerConfig = {
38184             tag:'span', cls:'x-form-twin-triggers', cn:[
38185             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38186             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38187         ]};
38188     },
38189
38190     getTrigger : function(index){
38191         return this.triggers[index];
38192     },
38193
38194     initTrigger : function(){
38195         var ts = this.trigger.select('.x-form-trigger', true);
38196         this.wrap.setStyle('overflow', 'hidden');
38197         var triggerField = this;
38198         ts.each(function(t, all, index){
38199             t.hide = function(){
38200                 var w = triggerField.wrap.getWidth();
38201                 this.dom.style.display = 'none';
38202                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38203             };
38204             t.show = function(){
38205                 var w = triggerField.wrap.getWidth();
38206                 this.dom.style.display = '';
38207                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38208             };
38209             var triggerIndex = 'Trigger'+(index+1);
38210
38211             if(this['hide'+triggerIndex]){
38212                 t.dom.style.display = 'none';
38213             }
38214             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38215             t.addClassOnOver('x-form-trigger-over');
38216             t.addClassOnClick('x-form-trigger-click');
38217         }, this);
38218         this.triggers = ts.elements;
38219     },
38220
38221     onTrigger1Click : Roo.emptyFn,
38222     onTrigger2Click : Roo.emptyFn
38223 });/*
38224  * Based on:
38225  * Ext JS Library 1.1.1
38226  * Copyright(c) 2006-2007, Ext JS, LLC.
38227  *
38228  * Originally Released Under LGPL - original licence link has changed is not relivant.
38229  *
38230  * Fork - LGPL
38231  * <script type="text/javascript">
38232  */
38233  
38234 /**
38235  * @class Roo.form.TextArea
38236  * @extends Roo.form.TextField
38237  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38238  * support for auto-sizing.
38239  * @constructor
38240  * Creates a new TextArea
38241  * @param {Object} config Configuration options
38242  */
38243 Roo.form.TextArea = function(config){
38244     Roo.form.TextArea.superclass.constructor.call(this, config);
38245     // these are provided exchanges for backwards compat
38246     // minHeight/maxHeight were replaced by growMin/growMax to be
38247     // compatible with TextField growing config values
38248     if(this.minHeight !== undefined){
38249         this.growMin = this.minHeight;
38250     }
38251     if(this.maxHeight !== undefined){
38252         this.growMax = this.maxHeight;
38253     }
38254 };
38255
38256 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38257     /**
38258      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38259      */
38260     growMin : 60,
38261     /**
38262      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38263      */
38264     growMax: 1000,
38265     /**
38266      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38267      * in the field (equivalent to setting overflow: hidden, defaults to false)
38268      */
38269     preventScrollbars: false,
38270     /**
38271      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38272      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38273      */
38274
38275     // private
38276     onRender : function(ct, position){
38277         if(!this.el){
38278             this.defaultAutoCreate = {
38279                 tag: "textarea",
38280                 style:"width:300px;height:60px;",
38281                 autocomplete: "new-password"
38282             };
38283         }
38284         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38285         if(this.grow){
38286             this.textSizeEl = Roo.DomHelper.append(document.body, {
38287                 tag: "pre", cls: "x-form-grow-sizer"
38288             });
38289             if(this.preventScrollbars){
38290                 this.el.setStyle("overflow", "hidden");
38291             }
38292             this.el.setHeight(this.growMin);
38293         }
38294     },
38295
38296     onDestroy : function(){
38297         if(this.textSizeEl){
38298             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38299         }
38300         Roo.form.TextArea.superclass.onDestroy.call(this);
38301     },
38302
38303     // private
38304     onKeyUp : function(e){
38305         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38306             this.autoSize();
38307         }
38308     },
38309
38310     /**
38311      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38312      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38313      */
38314     autoSize : function(){
38315         if(!this.grow || !this.textSizeEl){
38316             return;
38317         }
38318         var el = this.el;
38319         var v = el.dom.value;
38320         var ts = this.textSizeEl;
38321
38322         ts.innerHTML = '';
38323         ts.appendChild(document.createTextNode(v));
38324         v = ts.innerHTML;
38325
38326         Roo.fly(ts).setWidth(this.el.getWidth());
38327         if(v.length < 1){
38328             v = "&#160;&#160;";
38329         }else{
38330             if(Roo.isIE){
38331                 v = v.replace(/\n/g, '<p>&#160;</p>');
38332             }
38333             v += "&#160;\n&#160;";
38334         }
38335         ts.innerHTML = v;
38336         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38337         if(h != this.lastHeight){
38338             this.lastHeight = h;
38339             this.el.setHeight(h);
38340             this.fireEvent("autosize", this, h);
38341         }
38342     }
38343 });/*
38344  * Based on:
38345  * Ext JS Library 1.1.1
38346  * Copyright(c) 2006-2007, Ext JS, LLC.
38347  *
38348  * Originally Released Under LGPL - original licence link has changed is not relivant.
38349  *
38350  * Fork - LGPL
38351  * <script type="text/javascript">
38352  */
38353  
38354
38355 /**
38356  * @class Roo.form.NumberField
38357  * @extends Roo.form.TextField
38358  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38359  * @constructor
38360  * Creates a new NumberField
38361  * @param {Object} config Configuration options
38362  */
38363 Roo.form.NumberField = function(config){
38364     Roo.form.NumberField.superclass.constructor.call(this, config);
38365 };
38366
38367 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38368     /**
38369      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38370      */
38371     fieldClass: "x-form-field x-form-num-field",
38372     /**
38373      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38374      */
38375     allowDecimals : true,
38376     /**
38377      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38378      */
38379     decimalSeparator : ".",
38380     /**
38381      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38382      */
38383     decimalPrecision : 2,
38384     /**
38385      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38386      */
38387     allowNegative : true,
38388     /**
38389      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38390      */
38391     minValue : Number.NEGATIVE_INFINITY,
38392     /**
38393      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38394      */
38395     maxValue : Number.MAX_VALUE,
38396     /**
38397      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38398      */
38399     minText : "The minimum value for this field is {0}",
38400     /**
38401      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38402      */
38403     maxText : "The maximum value for this field is {0}",
38404     /**
38405      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38406      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38407      */
38408     nanText : "{0} is not a valid number",
38409
38410     // private
38411     initEvents : function(){
38412         Roo.form.NumberField.superclass.initEvents.call(this);
38413         var allowed = "0123456789";
38414         if(this.allowDecimals){
38415             allowed += this.decimalSeparator;
38416         }
38417         if(this.allowNegative){
38418             allowed += "-";
38419         }
38420         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38421         var keyPress = function(e){
38422             var k = e.getKey();
38423             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38424                 return;
38425             }
38426             var c = e.getCharCode();
38427             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38428                 e.stopEvent();
38429             }
38430         };
38431         this.el.on("keypress", keyPress, this);
38432     },
38433
38434     // private
38435     validateValue : function(value){
38436         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38437             return false;
38438         }
38439         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38440              return true;
38441         }
38442         var num = this.parseValue(value);
38443         if(isNaN(num)){
38444             this.markInvalid(String.format(this.nanText, value));
38445             return false;
38446         }
38447         if(num < this.minValue){
38448             this.markInvalid(String.format(this.minText, this.minValue));
38449             return false;
38450         }
38451         if(num > this.maxValue){
38452             this.markInvalid(String.format(this.maxText, this.maxValue));
38453             return false;
38454         }
38455         return true;
38456     },
38457
38458     getValue : function(){
38459         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38460     },
38461
38462     // private
38463     parseValue : function(value){
38464         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38465         return isNaN(value) ? '' : value;
38466     },
38467
38468     // private
38469     fixPrecision : function(value){
38470         var nan = isNaN(value);
38471         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38472             return nan ? '' : value;
38473         }
38474         return parseFloat(value).toFixed(this.decimalPrecision);
38475     },
38476
38477     setValue : function(v){
38478         v = this.fixPrecision(v);
38479         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38480     },
38481
38482     // private
38483     decimalPrecisionFcn : function(v){
38484         return Math.floor(v);
38485     },
38486
38487     beforeBlur : function(){
38488         var v = this.parseValue(this.getRawValue());
38489         if(v){
38490             this.setValue(v);
38491         }
38492     }
38493 });/*
38494  * Based on:
38495  * Ext JS Library 1.1.1
38496  * Copyright(c) 2006-2007, Ext JS, LLC.
38497  *
38498  * Originally Released Under LGPL - original licence link has changed is not relivant.
38499  *
38500  * Fork - LGPL
38501  * <script type="text/javascript">
38502  */
38503  
38504 /**
38505  * @class Roo.form.DateField
38506  * @extends Roo.form.TriggerField
38507  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38508 * @constructor
38509 * Create a new DateField
38510 * @param {Object} config
38511  */
38512 Roo.form.DateField = function(config){
38513     Roo.form.DateField.superclass.constructor.call(this, config);
38514     
38515       this.addEvents({
38516          
38517         /**
38518          * @event select
38519          * Fires when a date is selected
38520              * @param {Roo.form.DateField} combo This combo box
38521              * @param {Date} date The date selected
38522              */
38523         'select' : true
38524          
38525     });
38526     
38527     
38528     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38529     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38530     this.ddMatch = null;
38531     if(this.disabledDates){
38532         var dd = this.disabledDates;
38533         var re = "(?:";
38534         for(var i = 0; i < dd.length; i++){
38535             re += dd[i];
38536             if(i != dd.length-1) re += "|";
38537         }
38538         this.ddMatch = new RegExp(re + ")");
38539     }
38540 };
38541
38542 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38543     /**
38544      * @cfg {String} format
38545      * The default date format string which can be overriden for localization support.  The format must be
38546      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38547      */
38548     format : "m/d/y",
38549     /**
38550      * @cfg {String} altFormats
38551      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38552      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38553      */
38554     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38555     /**
38556      * @cfg {Array} disabledDays
38557      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38558      */
38559     disabledDays : null,
38560     /**
38561      * @cfg {String} disabledDaysText
38562      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38563      */
38564     disabledDaysText : "Disabled",
38565     /**
38566      * @cfg {Array} disabledDates
38567      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38568      * expression so they are very powerful. Some examples:
38569      * <ul>
38570      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38571      * <li>["03/08", "09/16"] would disable those days for every year</li>
38572      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38573      * <li>["03/../2006"] would disable every day in March 2006</li>
38574      * <li>["^03"] would disable every day in every March</li>
38575      * </ul>
38576      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38577      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38578      */
38579     disabledDates : null,
38580     /**
38581      * @cfg {String} disabledDatesText
38582      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38583      */
38584     disabledDatesText : "Disabled",
38585     /**
38586      * @cfg {Date/String} minValue
38587      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38588      * valid format (defaults to null).
38589      */
38590     minValue : null,
38591     /**
38592      * @cfg {Date/String} maxValue
38593      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38594      * valid format (defaults to null).
38595      */
38596     maxValue : null,
38597     /**
38598      * @cfg {String} minText
38599      * The error text to display when the date in the cell is before minValue (defaults to
38600      * 'The date in this field must be after {minValue}').
38601      */
38602     minText : "The date in this field must be equal to or after {0}",
38603     /**
38604      * @cfg {String} maxText
38605      * The error text to display when the date in the cell is after maxValue (defaults to
38606      * 'The date in this field must be before {maxValue}').
38607      */
38608     maxText : "The date in this field must be equal to or before {0}",
38609     /**
38610      * @cfg {String} invalidText
38611      * The error text to display when the date in the field is invalid (defaults to
38612      * '{value} is not a valid date - it must be in the format {format}').
38613      */
38614     invalidText : "{0} is not a valid date - it must be in the format {1}",
38615     /**
38616      * @cfg {String} triggerClass
38617      * An additional CSS class used to style the trigger button.  The trigger will always get the
38618      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38619      * which displays a calendar icon).
38620      */
38621     triggerClass : 'x-form-date-trigger',
38622     
38623
38624     /**
38625      * @cfg {Boolean} useIso
38626      * if enabled, then the date field will use a hidden field to store the 
38627      * real value as iso formated date. default (false)
38628      */ 
38629     useIso : false,
38630     /**
38631      * @cfg {String/Object} autoCreate
38632      * A DomHelper element spec, or true for a default element spec (defaults to
38633      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38634      */ 
38635     // private
38636     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38637     
38638     // private
38639     hiddenField: false,
38640     
38641     onRender : function(ct, position)
38642     {
38643         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38644         if (this.useIso) {
38645             //this.el.dom.removeAttribute('name'); 
38646             Roo.log("Changing name?");
38647             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38648             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38649                     'before', true);
38650             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38651             // prevent input submission
38652             this.hiddenName = this.name;
38653         }
38654             
38655             
38656     },
38657     
38658     // private
38659     validateValue : function(value)
38660     {
38661         value = this.formatDate(value);
38662         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38663             Roo.log('super failed');
38664             return false;
38665         }
38666         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38667              return true;
38668         }
38669         var svalue = value;
38670         value = this.parseDate(value);
38671         if(!value){
38672             Roo.log('parse date failed' + svalue);
38673             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38674             return false;
38675         }
38676         var time = value.getTime();
38677         if(this.minValue && time < this.minValue.getTime()){
38678             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38679             return false;
38680         }
38681         if(this.maxValue && time > this.maxValue.getTime()){
38682             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38683             return false;
38684         }
38685         if(this.disabledDays){
38686             var day = value.getDay();
38687             for(var i = 0; i < this.disabledDays.length; i++) {
38688                 if(day === this.disabledDays[i]){
38689                     this.markInvalid(this.disabledDaysText);
38690                     return false;
38691                 }
38692             }
38693         }
38694         var fvalue = this.formatDate(value);
38695         if(this.ddMatch && this.ddMatch.test(fvalue)){
38696             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38697             return false;
38698         }
38699         return true;
38700     },
38701
38702     // private
38703     // Provides logic to override the default TriggerField.validateBlur which just returns true
38704     validateBlur : function(){
38705         return !this.menu || !this.menu.isVisible();
38706     },
38707     
38708     getName: function()
38709     {
38710         // returns hidden if it's set..
38711         if (!this.rendered) {return ''};
38712         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38713         
38714     },
38715
38716     /**
38717      * Returns the current date value of the date field.
38718      * @return {Date} The date value
38719      */
38720     getValue : function(){
38721         
38722         return  this.hiddenField ?
38723                 this.hiddenField.value :
38724                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38725     },
38726
38727     /**
38728      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38729      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38730      * (the default format used is "m/d/y").
38731      * <br />Usage:
38732      * <pre><code>
38733 //All of these calls set the same date value (May 4, 2006)
38734
38735 //Pass a date object:
38736 var dt = new Date('5/4/06');
38737 dateField.setValue(dt);
38738
38739 //Pass a date string (default format):
38740 dateField.setValue('5/4/06');
38741
38742 //Pass a date string (custom format):
38743 dateField.format = 'Y-m-d';
38744 dateField.setValue('2006-5-4');
38745 </code></pre>
38746      * @param {String/Date} date The date or valid date string
38747      */
38748     setValue : function(date){
38749         if (this.hiddenField) {
38750             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38751         }
38752         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38753         // make sure the value field is always stored as a date..
38754         this.value = this.parseDate(date);
38755         
38756         
38757     },
38758
38759     // private
38760     parseDate : function(value){
38761         if(!value || value instanceof Date){
38762             return value;
38763         }
38764         var v = Date.parseDate(value, this.format);
38765          if (!v && this.useIso) {
38766             v = Date.parseDate(value, 'Y-m-d');
38767         }
38768         if(!v && this.altFormats){
38769             if(!this.altFormatsArray){
38770                 this.altFormatsArray = this.altFormats.split("|");
38771             }
38772             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38773                 v = Date.parseDate(value, this.altFormatsArray[i]);
38774             }
38775         }
38776         return v;
38777     },
38778
38779     // private
38780     formatDate : function(date, fmt){
38781         return (!date || !(date instanceof Date)) ?
38782                date : date.dateFormat(fmt || this.format);
38783     },
38784
38785     // private
38786     menuListeners : {
38787         select: function(m, d){
38788             
38789             this.setValue(d);
38790             this.fireEvent('select', this, d);
38791         },
38792         show : function(){ // retain focus styling
38793             this.onFocus();
38794         },
38795         hide : function(){
38796             this.focus.defer(10, this);
38797             var ml = this.menuListeners;
38798             this.menu.un("select", ml.select,  this);
38799             this.menu.un("show", ml.show,  this);
38800             this.menu.un("hide", ml.hide,  this);
38801         }
38802     },
38803
38804     // private
38805     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38806     onTriggerClick : function(){
38807         if(this.disabled){
38808             return;
38809         }
38810         if(this.menu == null){
38811             this.menu = new Roo.menu.DateMenu();
38812         }
38813         Roo.apply(this.menu.picker,  {
38814             showClear: this.allowBlank,
38815             minDate : this.minValue,
38816             maxDate : this.maxValue,
38817             disabledDatesRE : this.ddMatch,
38818             disabledDatesText : this.disabledDatesText,
38819             disabledDays : this.disabledDays,
38820             disabledDaysText : this.disabledDaysText,
38821             format : this.useIso ? 'Y-m-d' : this.format,
38822             minText : String.format(this.minText, this.formatDate(this.minValue)),
38823             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38824         });
38825         this.menu.on(Roo.apply({}, this.menuListeners, {
38826             scope:this
38827         }));
38828         this.menu.picker.setValue(this.getValue() || new Date());
38829         this.menu.show(this.el, "tl-bl?");
38830     },
38831
38832     beforeBlur : function(){
38833         var v = this.parseDate(this.getRawValue());
38834         if(v){
38835             this.setValue(v);
38836         }
38837     },
38838
38839     /*@
38840      * overide
38841      * 
38842      */
38843     isDirty : function() {
38844         if(this.disabled) {
38845             return false;
38846         }
38847         
38848         if(typeof(this.startValue) === 'undefined'){
38849             return false;
38850         }
38851         
38852         return String(this.getValue()) !== String(this.startValue);
38853         
38854     }
38855 });/*
38856  * Based on:
38857  * Ext JS Library 1.1.1
38858  * Copyright(c) 2006-2007, Ext JS, LLC.
38859  *
38860  * Originally Released Under LGPL - original licence link has changed is not relivant.
38861  *
38862  * Fork - LGPL
38863  * <script type="text/javascript">
38864  */
38865  
38866 /**
38867  * @class Roo.form.MonthField
38868  * @extends Roo.form.TriggerField
38869  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38870 * @constructor
38871 * Create a new MonthField
38872 * @param {Object} config
38873  */
38874 Roo.form.MonthField = function(config){
38875     
38876     Roo.form.MonthField.superclass.constructor.call(this, config);
38877     
38878       this.addEvents({
38879          
38880         /**
38881          * @event select
38882          * Fires when a date is selected
38883              * @param {Roo.form.MonthFieeld} combo This combo box
38884              * @param {Date} date The date selected
38885              */
38886         'select' : true
38887          
38888     });
38889     
38890     
38891     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38892     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38893     this.ddMatch = null;
38894     if(this.disabledDates){
38895         var dd = this.disabledDates;
38896         var re = "(?:";
38897         for(var i = 0; i < dd.length; i++){
38898             re += dd[i];
38899             if(i != dd.length-1) re += "|";
38900         }
38901         this.ddMatch = new RegExp(re + ")");
38902     }
38903 };
38904
38905 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38906     /**
38907      * @cfg {String} format
38908      * The default date format string which can be overriden for localization support.  The format must be
38909      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38910      */
38911     format : "M Y",
38912     /**
38913      * @cfg {String} altFormats
38914      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38915      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38916      */
38917     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38918     /**
38919      * @cfg {Array} disabledDays
38920      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38921      */
38922     disabledDays : [0,1,2,3,4,5,6],
38923     /**
38924      * @cfg {String} disabledDaysText
38925      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38926      */
38927     disabledDaysText : "Disabled",
38928     /**
38929      * @cfg {Array} disabledDates
38930      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38931      * expression so they are very powerful. Some examples:
38932      * <ul>
38933      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38934      * <li>["03/08", "09/16"] would disable those days for every year</li>
38935      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38936      * <li>["03/../2006"] would disable every day in March 2006</li>
38937      * <li>["^03"] would disable every day in every March</li>
38938      * </ul>
38939      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38940      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38941      */
38942     disabledDates : null,
38943     /**
38944      * @cfg {String} disabledDatesText
38945      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38946      */
38947     disabledDatesText : "Disabled",
38948     /**
38949      * @cfg {Date/String} minValue
38950      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38951      * valid format (defaults to null).
38952      */
38953     minValue : null,
38954     /**
38955      * @cfg {Date/String} maxValue
38956      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38957      * valid format (defaults to null).
38958      */
38959     maxValue : null,
38960     /**
38961      * @cfg {String} minText
38962      * The error text to display when the date in the cell is before minValue (defaults to
38963      * 'The date in this field must be after {minValue}').
38964      */
38965     minText : "The date in this field must be equal to or after {0}",
38966     /**
38967      * @cfg {String} maxTextf
38968      * The error text to display when the date in the cell is after maxValue (defaults to
38969      * 'The date in this field must be before {maxValue}').
38970      */
38971     maxText : "The date in this field must be equal to or before {0}",
38972     /**
38973      * @cfg {String} invalidText
38974      * The error text to display when the date in the field is invalid (defaults to
38975      * '{value} is not a valid date - it must be in the format {format}').
38976      */
38977     invalidText : "{0} is not a valid date - it must be in the format {1}",
38978     /**
38979      * @cfg {String} triggerClass
38980      * An additional CSS class used to style the trigger button.  The trigger will always get the
38981      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38982      * which displays a calendar icon).
38983      */
38984     triggerClass : 'x-form-date-trigger',
38985     
38986
38987     /**
38988      * @cfg {Boolean} useIso
38989      * if enabled, then the date field will use a hidden field to store the 
38990      * real value as iso formated date. default (true)
38991      */ 
38992     useIso : true,
38993     /**
38994      * @cfg {String/Object} autoCreate
38995      * A DomHelper element spec, or true for a default element spec (defaults to
38996      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38997      */ 
38998     // private
38999     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
39000     
39001     // private
39002     hiddenField: false,
39003     
39004     hideMonthPicker : false,
39005     
39006     onRender : function(ct, position)
39007     {
39008         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39009         if (this.useIso) {
39010             this.el.dom.removeAttribute('name'); 
39011             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39012                     'before', true);
39013             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39014             // prevent input submission
39015             this.hiddenName = this.name;
39016         }
39017             
39018             
39019     },
39020     
39021     // private
39022     validateValue : function(value)
39023     {
39024         value = this.formatDate(value);
39025         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39026             return false;
39027         }
39028         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39029              return true;
39030         }
39031         var svalue = value;
39032         value = this.parseDate(value);
39033         if(!value){
39034             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39035             return false;
39036         }
39037         var time = value.getTime();
39038         if(this.minValue && time < this.minValue.getTime()){
39039             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39040             return false;
39041         }
39042         if(this.maxValue && time > this.maxValue.getTime()){
39043             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39044             return false;
39045         }
39046         /*if(this.disabledDays){
39047             var day = value.getDay();
39048             for(var i = 0; i < this.disabledDays.length; i++) {
39049                 if(day === this.disabledDays[i]){
39050                     this.markInvalid(this.disabledDaysText);
39051                     return false;
39052                 }
39053             }
39054         }
39055         */
39056         var fvalue = this.formatDate(value);
39057         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39058             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39059             return false;
39060         }
39061         */
39062         return true;
39063     },
39064
39065     // private
39066     // Provides logic to override the default TriggerField.validateBlur which just returns true
39067     validateBlur : function(){
39068         return !this.menu || !this.menu.isVisible();
39069     },
39070
39071     /**
39072      * Returns the current date value of the date field.
39073      * @return {Date} The date value
39074      */
39075     getValue : function(){
39076         
39077         
39078         
39079         return  this.hiddenField ?
39080                 this.hiddenField.value :
39081                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39082     },
39083
39084     /**
39085      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39086      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39087      * (the default format used is "m/d/y").
39088      * <br />Usage:
39089      * <pre><code>
39090 //All of these calls set the same date value (May 4, 2006)
39091
39092 //Pass a date object:
39093 var dt = new Date('5/4/06');
39094 monthField.setValue(dt);
39095
39096 //Pass a date string (default format):
39097 monthField.setValue('5/4/06');
39098
39099 //Pass a date string (custom format):
39100 monthField.format = 'Y-m-d';
39101 monthField.setValue('2006-5-4');
39102 </code></pre>
39103      * @param {String/Date} date The date or valid date string
39104      */
39105     setValue : function(date){
39106         Roo.log('month setValue' + date);
39107         // can only be first of month..
39108         
39109         var val = this.parseDate(date);
39110         
39111         if (this.hiddenField) {
39112             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39113         }
39114         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39115         this.value = this.parseDate(date);
39116     },
39117
39118     // private
39119     parseDate : function(value){
39120         if(!value || value instanceof Date){
39121             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39122             return value;
39123         }
39124         var v = Date.parseDate(value, this.format);
39125         if (!v && this.useIso) {
39126             v = Date.parseDate(value, 'Y-m-d');
39127         }
39128         if (v) {
39129             // 
39130             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39131         }
39132         
39133         
39134         if(!v && this.altFormats){
39135             if(!this.altFormatsArray){
39136                 this.altFormatsArray = this.altFormats.split("|");
39137             }
39138             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39139                 v = Date.parseDate(value, this.altFormatsArray[i]);
39140             }
39141         }
39142         return v;
39143     },
39144
39145     // private
39146     formatDate : function(date, fmt){
39147         return (!date || !(date instanceof Date)) ?
39148                date : date.dateFormat(fmt || this.format);
39149     },
39150
39151     // private
39152     menuListeners : {
39153         select: function(m, d){
39154             this.setValue(d);
39155             this.fireEvent('select', this, d);
39156         },
39157         show : function(){ // retain focus styling
39158             this.onFocus();
39159         },
39160         hide : function(){
39161             this.focus.defer(10, this);
39162             var ml = this.menuListeners;
39163             this.menu.un("select", ml.select,  this);
39164             this.menu.un("show", ml.show,  this);
39165             this.menu.un("hide", ml.hide,  this);
39166         }
39167     },
39168     // private
39169     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39170     onTriggerClick : function(){
39171         if(this.disabled){
39172             return;
39173         }
39174         if(this.menu == null){
39175             this.menu = new Roo.menu.DateMenu();
39176            
39177         }
39178         
39179         Roo.apply(this.menu.picker,  {
39180             
39181             showClear: this.allowBlank,
39182             minDate : this.minValue,
39183             maxDate : this.maxValue,
39184             disabledDatesRE : this.ddMatch,
39185             disabledDatesText : this.disabledDatesText,
39186             
39187             format : this.useIso ? 'Y-m-d' : this.format,
39188             minText : String.format(this.minText, this.formatDate(this.minValue)),
39189             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39190             
39191         });
39192          this.menu.on(Roo.apply({}, this.menuListeners, {
39193             scope:this
39194         }));
39195        
39196         
39197         var m = this.menu;
39198         var p = m.picker;
39199         
39200         // hide month picker get's called when we called by 'before hide';
39201         
39202         var ignorehide = true;
39203         p.hideMonthPicker  = function(disableAnim){
39204             if (ignorehide) {
39205                 return;
39206             }
39207              if(this.monthPicker){
39208                 Roo.log("hideMonthPicker called");
39209                 if(disableAnim === true){
39210                     this.monthPicker.hide();
39211                 }else{
39212                     this.monthPicker.slideOut('t', {duration:.2});
39213                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39214                     p.fireEvent("select", this, this.value);
39215                     m.hide();
39216                 }
39217             }
39218         }
39219         
39220         Roo.log('picker set value');
39221         Roo.log(this.getValue());
39222         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39223         m.show(this.el, 'tl-bl?');
39224         ignorehide  = false;
39225         // this will trigger hideMonthPicker..
39226         
39227         
39228         // hidden the day picker
39229         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39230         
39231         
39232         
39233       
39234         
39235         p.showMonthPicker.defer(100, p);
39236     
39237         
39238        
39239     },
39240
39241     beforeBlur : function(){
39242         var v = this.parseDate(this.getRawValue());
39243         if(v){
39244             this.setValue(v);
39245         }
39246     }
39247
39248     /** @cfg {Boolean} grow @hide */
39249     /** @cfg {Number} growMin @hide */
39250     /** @cfg {Number} growMax @hide */
39251     /**
39252      * @hide
39253      * @method autoSize
39254      */
39255 });/*
39256  * Based on:
39257  * Ext JS Library 1.1.1
39258  * Copyright(c) 2006-2007, Ext JS, LLC.
39259  *
39260  * Originally Released Under LGPL - original licence link has changed is not relivant.
39261  *
39262  * Fork - LGPL
39263  * <script type="text/javascript">
39264  */
39265  
39266
39267 /**
39268  * @class Roo.form.ComboBox
39269  * @extends Roo.form.TriggerField
39270  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39271  * @constructor
39272  * Create a new ComboBox.
39273  * @param {Object} config Configuration options
39274  */
39275 Roo.form.ComboBox = function(config){
39276     Roo.form.ComboBox.superclass.constructor.call(this, config);
39277     this.addEvents({
39278         /**
39279          * @event expand
39280          * Fires when the dropdown list is expanded
39281              * @param {Roo.form.ComboBox} combo This combo box
39282              */
39283         'expand' : true,
39284         /**
39285          * @event collapse
39286          * Fires when the dropdown list is collapsed
39287              * @param {Roo.form.ComboBox} combo This combo box
39288              */
39289         'collapse' : true,
39290         /**
39291          * @event beforeselect
39292          * Fires before a list item is selected. Return false to cancel the selection.
39293              * @param {Roo.form.ComboBox} combo This combo box
39294              * @param {Roo.data.Record} record The data record returned from the underlying store
39295              * @param {Number} index The index of the selected item in the dropdown list
39296              */
39297         'beforeselect' : true,
39298         /**
39299          * @event select
39300          * Fires when a list item is selected
39301              * @param {Roo.form.ComboBox} combo This combo box
39302              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39303              * @param {Number} index The index of the selected item in the dropdown list
39304              */
39305         'select' : true,
39306         /**
39307          * @event beforequery
39308          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39309          * The event object passed has these properties:
39310              * @param {Roo.form.ComboBox} combo This combo box
39311              * @param {String} query The query
39312              * @param {Boolean} forceAll true to force "all" query
39313              * @param {Boolean} cancel true to cancel the query
39314              * @param {Object} e The query event object
39315              */
39316         'beforequery': true,
39317          /**
39318          * @event add
39319          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39320              * @param {Roo.form.ComboBox} combo This combo box
39321              */
39322         'add' : true,
39323         /**
39324          * @event edit
39325          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39326              * @param {Roo.form.ComboBox} combo This combo box
39327              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39328              */
39329         'edit' : true
39330         
39331         
39332     });
39333     if(this.transform){
39334         this.allowDomMove = false;
39335         var s = Roo.getDom(this.transform);
39336         if(!this.hiddenName){
39337             this.hiddenName = s.name;
39338         }
39339         if(!this.store){
39340             this.mode = 'local';
39341             var d = [], opts = s.options;
39342             for(var i = 0, len = opts.length;i < len; i++){
39343                 var o = opts[i];
39344                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39345                 if(o.selected) {
39346                     this.value = value;
39347                 }
39348                 d.push([value, o.text]);
39349             }
39350             this.store = new Roo.data.SimpleStore({
39351                 'id': 0,
39352                 fields: ['value', 'text'],
39353                 data : d
39354             });
39355             this.valueField = 'value';
39356             this.displayField = 'text';
39357         }
39358         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39359         if(!this.lazyRender){
39360             this.target = true;
39361             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39362             s.parentNode.removeChild(s); // remove it
39363             this.render(this.el.parentNode);
39364         }else{
39365             s.parentNode.removeChild(s); // remove it
39366         }
39367
39368     }
39369     if (this.store) {
39370         this.store = Roo.factory(this.store, Roo.data);
39371     }
39372     
39373     this.selectedIndex = -1;
39374     if(this.mode == 'local'){
39375         if(config.queryDelay === undefined){
39376             this.queryDelay = 10;
39377         }
39378         if(config.minChars === undefined){
39379             this.minChars = 0;
39380         }
39381     }
39382 };
39383
39384 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39385     /**
39386      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39387      */
39388     /**
39389      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39390      * rendering into an Roo.Editor, defaults to false)
39391      */
39392     /**
39393      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39394      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39395      */
39396     /**
39397      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39398      */
39399     /**
39400      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39401      * the dropdown list (defaults to undefined, with no header element)
39402      */
39403
39404      /**
39405      * @cfg {String/Roo.Template} tpl The template to use to render the output
39406      */
39407      
39408     // private
39409     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39410     /**
39411      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39412      */
39413     listWidth: undefined,
39414     /**
39415      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39416      * mode = 'remote' or 'text' if mode = 'local')
39417      */
39418     displayField: undefined,
39419     /**
39420      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39421      * mode = 'remote' or 'value' if mode = 'local'). 
39422      * Note: use of a valueField requires the user make a selection
39423      * in order for a value to be mapped.
39424      */
39425     valueField: undefined,
39426     
39427     
39428     /**
39429      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39430      * field's data value (defaults to the underlying DOM element's name)
39431      */
39432     hiddenName: undefined,
39433     /**
39434      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39435      */
39436     listClass: '',
39437     /**
39438      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39439      */
39440     selectedClass: 'x-combo-selected',
39441     /**
39442      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39443      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39444      * which displays a downward arrow icon).
39445      */
39446     triggerClass : 'x-form-arrow-trigger',
39447     /**
39448      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39449      */
39450     shadow:'sides',
39451     /**
39452      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39453      * anchor positions (defaults to 'tl-bl')
39454      */
39455     listAlign: 'tl-bl?',
39456     /**
39457      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39458      */
39459     maxHeight: 300,
39460     /**
39461      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39462      * query specified by the allQuery config option (defaults to 'query')
39463      */
39464     triggerAction: 'query',
39465     /**
39466      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39467      * (defaults to 4, does not apply if editable = false)
39468      */
39469     minChars : 4,
39470     /**
39471      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39472      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39473      */
39474     typeAhead: false,
39475     /**
39476      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39477      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39478      */
39479     queryDelay: 500,
39480     /**
39481      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39482      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39483      */
39484     pageSize: 0,
39485     /**
39486      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39487      * when editable = true (defaults to false)
39488      */
39489     selectOnFocus:false,
39490     /**
39491      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39492      */
39493     queryParam: 'query',
39494     /**
39495      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39496      * when mode = 'remote' (defaults to 'Loading...')
39497      */
39498     loadingText: 'Loading...',
39499     /**
39500      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39501      */
39502     resizable: false,
39503     /**
39504      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39505      */
39506     handleHeight : 8,
39507     /**
39508      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39509      * traditional select (defaults to true)
39510      */
39511     editable: true,
39512     /**
39513      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39514      */
39515     allQuery: '',
39516     /**
39517      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39518      */
39519     mode: 'remote',
39520     /**
39521      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39522      * listWidth has a higher value)
39523      */
39524     minListWidth : 70,
39525     /**
39526      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39527      * allow the user to set arbitrary text into the field (defaults to false)
39528      */
39529     forceSelection:false,
39530     /**
39531      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39532      * if typeAhead = true (defaults to 250)
39533      */
39534     typeAheadDelay : 250,
39535     /**
39536      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39537      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39538      */
39539     valueNotFoundText : undefined,
39540     /**
39541      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39542      */
39543     blockFocus : false,
39544     
39545     /**
39546      * @cfg {Boolean} disableClear Disable showing of clear button.
39547      */
39548     disableClear : false,
39549     /**
39550      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39551      */
39552     alwaysQuery : false,
39553     
39554     //private
39555     addicon : false,
39556     editicon: false,
39557     
39558     // element that contains real text value.. (when hidden is used..)
39559      
39560     // private
39561     onRender : function(ct, position){
39562         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39563         if(this.hiddenName){
39564             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39565                     'before', true);
39566             this.hiddenField.value =
39567                 this.hiddenValue !== undefined ? this.hiddenValue :
39568                 this.value !== undefined ? this.value : '';
39569
39570             // prevent input submission
39571             this.el.dom.removeAttribute('name');
39572              
39573              
39574         }
39575         if(Roo.isGecko){
39576             this.el.dom.setAttribute('autocomplete', 'off');
39577         }
39578
39579         var cls = 'x-combo-list';
39580
39581         this.list = new Roo.Layer({
39582             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39583         });
39584
39585         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39586         this.list.setWidth(lw);
39587         this.list.swallowEvent('mousewheel');
39588         this.assetHeight = 0;
39589
39590         if(this.title){
39591             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39592             this.assetHeight += this.header.getHeight();
39593         }
39594
39595         this.innerList = this.list.createChild({cls:cls+'-inner'});
39596         this.innerList.on('mouseover', this.onViewOver, this);
39597         this.innerList.on('mousemove', this.onViewMove, this);
39598         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39599         
39600         if(this.allowBlank && !this.pageSize && !this.disableClear){
39601             this.footer = this.list.createChild({cls:cls+'-ft'});
39602             this.pageTb = new Roo.Toolbar(this.footer);
39603            
39604         }
39605         if(this.pageSize){
39606             this.footer = this.list.createChild({cls:cls+'-ft'});
39607             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39608                     {pageSize: this.pageSize});
39609             
39610         }
39611         
39612         if (this.pageTb && this.allowBlank && !this.disableClear) {
39613             var _this = this;
39614             this.pageTb.add(new Roo.Toolbar.Fill(), {
39615                 cls: 'x-btn-icon x-btn-clear',
39616                 text: '&#160;',
39617                 handler: function()
39618                 {
39619                     _this.collapse();
39620                     _this.clearValue();
39621                     _this.onSelect(false, -1);
39622                 }
39623             });
39624         }
39625         if (this.footer) {
39626             this.assetHeight += this.footer.getHeight();
39627         }
39628         
39629
39630         if(!this.tpl){
39631             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39632         }
39633
39634         this.view = new Roo.View(this.innerList, this.tpl, {
39635             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39636         });
39637
39638         this.view.on('click', this.onViewClick, this);
39639
39640         this.store.on('beforeload', this.onBeforeLoad, this);
39641         this.store.on('load', this.onLoad, this);
39642         this.store.on('loadexception', this.onLoadException, this);
39643
39644         if(this.resizable){
39645             this.resizer = new Roo.Resizable(this.list,  {
39646                pinned:true, handles:'se'
39647             });
39648             this.resizer.on('resize', function(r, w, h){
39649                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39650                 this.listWidth = w;
39651                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39652                 this.restrictHeight();
39653             }, this);
39654             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39655         }
39656         if(!this.editable){
39657             this.editable = true;
39658             this.setEditable(false);
39659         }  
39660         
39661         
39662         if (typeof(this.events.add.listeners) != 'undefined') {
39663             
39664             this.addicon = this.wrap.createChild(
39665                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39666        
39667             this.addicon.on('click', function(e) {
39668                 this.fireEvent('add', this);
39669             }, this);
39670         }
39671         if (typeof(this.events.edit.listeners) != 'undefined') {
39672             
39673             this.editicon = this.wrap.createChild(
39674                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39675             if (this.addicon) {
39676                 this.editicon.setStyle('margin-left', '40px');
39677             }
39678             this.editicon.on('click', function(e) {
39679                 
39680                 // we fire even  if inothing is selected..
39681                 this.fireEvent('edit', this, this.lastData );
39682                 
39683             }, this);
39684         }
39685         
39686         
39687         
39688     },
39689
39690     // private
39691     initEvents : function(){
39692         Roo.form.ComboBox.superclass.initEvents.call(this);
39693
39694         this.keyNav = new Roo.KeyNav(this.el, {
39695             "up" : function(e){
39696                 this.inKeyMode = true;
39697                 this.selectPrev();
39698             },
39699
39700             "down" : function(e){
39701                 if(!this.isExpanded()){
39702                     this.onTriggerClick();
39703                 }else{
39704                     this.inKeyMode = true;
39705                     this.selectNext();
39706                 }
39707             },
39708
39709             "enter" : function(e){
39710                 this.onViewClick();
39711                 //return true;
39712             },
39713
39714             "esc" : function(e){
39715                 this.collapse();
39716             },
39717
39718             "tab" : function(e){
39719                 this.onViewClick(false);
39720                 this.fireEvent("specialkey", this, e);
39721                 return true;
39722             },
39723
39724             scope : this,
39725
39726             doRelay : function(foo, bar, hname){
39727                 if(hname == 'down' || this.scope.isExpanded()){
39728                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39729                 }
39730                 return true;
39731             },
39732
39733             forceKeyDown: true
39734         });
39735         this.queryDelay = Math.max(this.queryDelay || 10,
39736                 this.mode == 'local' ? 10 : 250);
39737         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39738         if(this.typeAhead){
39739             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39740         }
39741         if(this.editable !== false){
39742             this.el.on("keyup", this.onKeyUp, this);
39743         }
39744         if(this.forceSelection){
39745             this.on('blur', this.doForce, this);
39746         }
39747     },
39748
39749     onDestroy : function(){
39750         if(this.view){
39751             this.view.setStore(null);
39752             this.view.el.removeAllListeners();
39753             this.view.el.remove();
39754             this.view.purgeListeners();
39755         }
39756         if(this.list){
39757             this.list.destroy();
39758         }
39759         if(this.store){
39760             this.store.un('beforeload', this.onBeforeLoad, this);
39761             this.store.un('load', this.onLoad, this);
39762             this.store.un('loadexception', this.onLoadException, this);
39763         }
39764         Roo.form.ComboBox.superclass.onDestroy.call(this);
39765     },
39766
39767     // private
39768     fireKey : function(e){
39769         if(e.isNavKeyPress() && !this.list.isVisible()){
39770             this.fireEvent("specialkey", this, e);
39771         }
39772     },
39773
39774     // private
39775     onResize: function(w, h){
39776         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39777         
39778         if(typeof w != 'number'){
39779             // we do not handle it!?!?
39780             return;
39781         }
39782         var tw = this.trigger.getWidth();
39783         tw += this.addicon ? this.addicon.getWidth() : 0;
39784         tw += this.editicon ? this.editicon.getWidth() : 0;
39785         var x = w - tw;
39786         this.el.setWidth( this.adjustWidth('input', x));
39787             
39788         this.trigger.setStyle('left', x+'px');
39789         
39790         if(this.list && this.listWidth === undefined){
39791             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39792             this.list.setWidth(lw);
39793             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39794         }
39795         
39796     
39797         
39798     },
39799
39800     /**
39801      * Allow or prevent the user from directly editing the field text.  If false is passed,
39802      * the user will only be able to select from the items defined in the dropdown list.  This method
39803      * is the runtime equivalent of setting the 'editable' config option at config time.
39804      * @param {Boolean} value True to allow the user to directly edit the field text
39805      */
39806     setEditable : function(value){
39807         if(value == this.editable){
39808             return;
39809         }
39810         this.editable = value;
39811         if(!value){
39812             this.el.dom.setAttribute('readOnly', true);
39813             this.el.on('mousedown', this.onTriggerClick,  this);
39814             this.el.addClass('x-combo-noedit');
39815         }else{
39816             this.el.dom.setAttribute('readOnly', false);
39817             this.el.un('mousedown', this.onTriggerClick,  this);
39818             this.el.removeClass('x-combo-noedit');
39819         }
39820     },
39821
39822     // private
39823     onBeforeLoad : function(){
39824         if(!this.hasFocus){
39825             return;
39826         }
39827         this.innerList.update(this.loadingText ?
39828                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39829         this.restrictHeight();
39830         this.selectedIndex = -1;
39831     },
39832
39833     // private
39834     onLoad : function(){
39835         if(!this.hasFocus){
39836             return;
39837         }
39838         if(this.store.getCount() > 0){
39839             this.expand();
39840             this.restrictHeight();
39841             if(this.lastQuery == this.allQuery){
39842                 if(this.editable){
39843                     this.el.dom.select();
39844                 }
39845                 if(!this.selectByValue(this.value, true)){
39846                     this.select(0, true);
39847                 }
39848             }else{
39849                 this.selectNext();
39850                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39851                     this.taTask.delay(this.typeAheadDelay);
39852                 }
39853             }
39854         }else{
39855             this.onEmptyResults();
39856         }
39857         //this.el.focus();
39858     },
39859     // private
39860     onLoadException : function()
39861     {
39862         this.collapse();
39863         Roo.log(this.store.reader.jsonData);
39864         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39865             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39866         }
39867         
39868         
39869     },
39870     // private
39871     onTypeAhead : function(){
39872         if(this.store.getCount() > 0){
39873             var r = this.store.getAt(0);
39874             var newValue = r.data[this.displayField];
39875             var len = newValue.length;
39876             var selStart = this.getRawValue().length;
39877             if(selStart != len){
39878                 this.setRawValue(newValue);
39879                 this.selectText(selStart, newValue.length);
39880             }
39881         }
39882     },
39883
39884     // private
39885     onSelect : function(record, index){
39886         if(this.fireEvent('beforeselect', this, record, index) !== false){
39887             this.setFromData(index > -1 ? record.data : false);
39888             this.collapse();
39889             this.fireEvent('select', this, record, index);
39890         }
39891     },
39892
39893     /**
39894      * Returns the currently selected field value or empty string if no value is set.
39895      * @return {String} value The selected value
39896      */
39897     getValue : function(){
39898         if(this.valueField){
39899             return typeof this.value != 'undefined' ? this.value : '';
39900         }
39901         return Roo.form.ComboBox.superclass.getValue.call(this);
39902     },
39903
39904     /**
39905      * Clears any text/value currently set in the field
39906      */
39907     clearValue : function(){
39908         if(this.hiddenField){
39909             this.hiddenField.value = '';
39910         }
39911         this.value = '';
39912         this.setRawValue('');
39913         this.lastSelectionText = '';
39914         
39915     },
39916
39917     /**
39918      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39919      * will be displayed in the field.  If the value does not match the data value of an existing item,
39920      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39921      * Otherwise the field will be blank (although the value will still be set).
39922      * @param {String} value The value to match
39923      */
39924     setValue : function(v){
39925         var text = v;
39926         if(this.valueField){
39927             var r = this.findRecord(this.valueField, v);
39928             if(r){
39929                 text = r.data[this.displayField];
39930             }else if(this.valueNotFoundText !== undefined){
39931                 text = this.valueNotFoundText;
39932             }
39933         }
39934         this.lastSelectionText = text;
39935         if(this.hiddenField){
39936             this.hiddenField.value = v;
39937         }
39938         Roo.form.ComboBox.superclass.setValue.call(this, text);
39939         this.value = v;
39940     },
39941     /**
39942      * @property {Object} the last set data for the element
39943      */
39944     
39945     lastData : false,
39946     /**
39947      * Sets the value of the field based on a object which is related to the record format for the store.
39948      * @param {Object} value the value to set as. or false on reset?
39949      */
39950     setFromData : function(o){
39951         var dv = ''; // display value
39952         var vv = ''; // value value..
39953         this.lastData = o;
39954         if (this.displayField) {
39955             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39956         } else {
39957             // this is an error condition!!!
39958             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39959         }
39960         
39961         if(this.valueField){
39962             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39963         }
39964         if(this.hiddenField){
39965             this.hiddenField.value = vv;
39966             
39967             this.lastSelectionText = dv;
39968             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39969             this.value = vv;
39970             return;
39971         }
39972         // no hidden field.. - we store the value in 'value', but still display
39973         // display field!!!!
39974         this.lastSelectionText = dv;
39975         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39976         this.value = vv;
39977         
39978         
39979     },
39980     // private
39981     reset : function(){
39982         // overridden so that last data is reset..
39983         this.setValue(this.resetValue);
39984         this.clearInvalid();
39985         this.lastData = false;
39986         if (this.view) {
39987             this.view.clearSelections();
39988         }
39989     },
39990     // private
39991     findRecord : function(prop, value){
39992         var record;
39993         if(this.store.getCount() > 0){
39994             this.store.each(function(r){
39995                 if(r.data[prop] == value){
39996                     record = r;
39997                     return false;
39998                 }
39999                 return true;
40000             });
40001         }
40002         return record;
40003     },
40004     
40005     getName: function()
40006     {
40007         // returns hidden if it's set..
40008         if (!this.rendered) {return ''};
40009         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40010         
40011     },
40012     // private
40013     onViewMove : function(e, t){
40014         this.inKeyMode = false;
40015     },
40016
40017     // private
40018     onViewOver : function(e, t){
40019         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40020             return;
40021         }
40022         var item = this.view.findItemFromChild(t);
40023         if(item){
40024             var index = this.view.indexOf(item);
40025             this.select(index, false);
40026         }
40027     },
40028
40029     // private
40030     onViewClick : function(doFocus)
40031     {
40032         var index = this.view.getSelectedIndexes()[0];
40033         var r = this.store.getAt(index);
40034         if(r){
40035             this.onSelect(r, index);
40036         }
40037         if(doFocus !== false && !this.blockFocus){
40038             this.el.focus();
40039         }
40040     },
40041
40042     // private
40043     restrictHeight : function(){
40044         this.innerList.dom.style.height = '';
40045         var inner = this.innerList.dom;
40046         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40047         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40048         this.list.beginUpdate();
40049         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40050         this.list.alignTo(this.el, this.listAlign);
40051         this.list.endUpdate();
40052     },
40053
40054     // private
40055     onEmptyResults : function(){
40056         this.collapse();
40057     },
40058
40059     /**
40060      * Returns true if the dropdown list is expanded, else false.
40061      */
40062     isExpanded : function(){
40063         return this.list.isVisible();
40064     },
40065
40066     /**
40067      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40068      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40069      * @param {String} value The data value of the item to select
40070      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40071      * selected item if it is not currently in view (defaults to true)
40072      * @return {Boolean} True if the value matched an item in the list, else false
40073      */
40074     selectByValue : function(v, scrollIntoView){
40075         if(v !== undefined && v !== null){
40076             var r = this.findRecord(this.valueField || this.displayField, v);
40077             if(r){
40078                 this.select(this.store.indexOf(r), scrollIntoView);
40079                 return true;
40080             }
40081         }
40082         return false;
40083     },
40084
40085     /**
40086      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40087      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40088      * @param {Number} index The zero-based index of the list item to select
40089      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40090      * selected item if it is not currently in view (defaults to true)
40091      */
40092     select : function(index, scrollIntoView){
40093         this.selectedIndex = index;
40094         this.view.select(index);
40095         if(scrollIntoView !== false){
40096             var el = this.view.getNode(index);
40097             if(el){
40098                 this.innerList.scrollChildIntoView(el, false);
40099             }
40100         }
40101     },
40102
40103     // private
40104     selectNext : function(){
40105         var ct = this.store.getCount();
40106         if(ct > 0){
40107             if(this.selectedIndex == -1){
40108                 this.select(0);
40109             }else if(this.selectedIndex < ct-1){
40110                 this.select(this.selectedIndex+1);
40111             }
40112         }
40113     },
40114
40115     // private
40116     selectPrev : function(){
40117         var ct = this.store.getCount();
40118         if(ct > 0){
40119             if(this.selectedIndex == -1){
40120                 this.select(0);
40121             }else if(this.selectedIndex != 0){
40122                 this.select(this.selectedIndex-1);
40123             }
40124         }
40125     },
40126
40127     // private
40128     onKeyUp : function(e){
40129         if(this.editable !== false && !e.isSpecialKey()){
40130             this.lastKey = e.getKey();
40131             this.dqTask.delay(this.queryDelay);
40132         }
40133     },
40134
40135     // private
40136     validateBlur : function(){
40137         return !this.list || !this.list.isVisible();   
40138     },
40139
40140     // private
40141     initQuery : function(){
40142         this.doQuery(this.getRawValue());
40143     },
40144
40145     // private
40146     doForce : function(){
40147         if(this.el.dom.value.length > 0){
40148             this.el.dom.value =
40149                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40150              
40151         }
40152     },
40153
40154     /**
40155      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40156      * query allowing the query action to be canceled if needed.
40157      * @param {String} query The SQL query to execute
40158      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40159      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40160      * saved in the current store (defaults to false)
40161      */
40162     doQuery : function(q, forceAll){
40163         if(q === undefined || q === null){
40164             q = '';
40165         }
40166         var qe = {
40167             query: q,
40168             forceAll: forceAll,
40169             combo: this,
40170             cancel:false
40171         };
40172         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40173             return false;
40174         }
40175         q = qe.query;
40176         forceAll = qe.forceAll;
40177         if(forceAll === true || (q.length >= this.minChars)){
40178             if(this.lastQuery != q || this.alwaysQuery){
40179                 this.lastQuery = q;
40180                 if(this.mode == 'local'){
40181                     this.selectedIndex = -1;
40182                     if(forceAll){
40183                         this.store.clearFilter();
40184                     }else{
40185                         this.store.filter(this.displayField, q);
40186                     }
40187                     this.onLoad();
40188                 }else{
40189                     this.store.baseParams[this.queryParam] = q;
40190                     this.store.load({
40191                         params: this.getParams(q)
40192                     });
40193                     this.expand();
40194                 }
40195             }else{
40196                 this.selectedIndex = -1;
40197                 this.onLoad();   
40198             }
40199         }
40200     },
40201
40202     // private
40203     getParams : function(q){
40204         var p = {};
40205         //p[this.queryParam] = q;
40206         if(this.pageSize){
40207             p.start = 0;
40208             p.limit = this.pageSize;
40209         }
40210         return p;
40211     },
40212
40213     /**
40214      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40215      */
40216     collapse : function(){
40217         if(!this.isExpanded()){
40218             return;
40219         }
40220         this.list.hide();
40221         Roo.get(document).un('mousedown', this.collapseIf, this);
40222         Roo.get(document).un('mousewheel', this.collapseIf, this);
40223         if (!this.editable) {
40224             Roo.get(document).un('keydown', this.listKeyPress, this);
40225         }
40226         this.fireEvent('collapse', this);
40227     },
40228
40229     // private
40230     collapseIf : function(e){
40231         if(!e.within(this.wrap) && !e.within(this.list)){
40232             this.collapse();
40233         }
40234     },
40235
40236     /**
40237      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40238      */
40239     expand : function(){
40240         if(this.isExpanded() || !this.hasFocus){
40241             return;
40242         }
40243         this.list.alignTo(this.el, this.listAlign);
40244         this.list.show();
40245         Roo.get(document).on('mousedown', this.collapseIf, this);
40246         Roo.get(document).on('mousewheel', this.collapseIf, this);
40247         if (!this.editable) {
40248             Roo.get(document).on('keydown', this.listKeyPress, this);
40249         }
40250         
40251         this.fireEvent('expand', this);
40252     },
40253
40254     // private
40255     // Implements the default empty TriggerField.onTriggerClick function
40256     onTriggerClick : function(){
40257         if(this.disabled){
40258             return;
40259         }
40260         if(this.isExpanded()){
40261             this.collapse();
40262             if (!this.blockFocus) {
40263                 this.el.focus();
40264             }
40265             
40266         }else {
40267             this.hasFocus = true;
40268             if(this.triggerAction == 'all') {
40269                 this.doQuery(this.allQuery, true);
40270             } else {
40271                 this.doQuery(this.getRawValue());
40272             }
40273             if (!this.blockFocus) {
40274                 this.el.focus();
40275             }
40276         }
40277     },
40278     listKeyPress : function(e)
40279     {
40280         //Roo.log('listkeypress');
40281         // scroll to first matching element based on key pres..
40282         if (e.isSpecialKey()) {
40283             return false;
40284         }
40285         var k = String.fromCharCode(e.getKey()).toUpperCase();
40286         //Roo.log(k);
40287         var match  = false;
40288         var csel = this.view.getSelectedNodes();
40289         var cselitem = false;
40290         if (csel.length) {
40291             var ix = this.view.indexOf(csel[0]);
40292             cselitem  = this.store.getAt(ix);
40293             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40294                 cselitem = false;
40295             }
40296             
40297         }
40298         
40299         this.store.each(function(v) { 
40300             if (cselitem) {
40301                 // start at existing selection.
40302                 if (cselitem.id == v.id) {
40303                     cselitem = false;
40304                 }
40305                 return;
40306             }
40307                 
40308             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40309                 match = this.store.indexOf(v);
40310                 return false;
40311             }
40312         }, this);
40313         
40314         if (match === false) {
40315             return true; // no more action?
40316         }
40317         // scroll to?
40318         this.view.select(match);
40319         var sn = Roo.get(this.view.getSelectedNodes()[0])
40320         sn.scrollIntoView(sn.dom.parentNode, false);
40321     }
40322
40323     /** 
40324     * @cfg {Boolean} grow 
40325     * @hide 
40326     */
40327     /** 
40328     * @cfg {Number} growMin 
40329     * @hide 
40330     */
40331     /** 
40332     * @cfg {Number} growMax 
40333     * @hide 
40334     */
40335     /**
40336      * @hide
40337      * @method autoSize
40338      */
40339 });/*
40340  * Copyright(c) 2010-2012, Roo J Solutions Limited
40341  *
40342  * Licence LGPL
40343  *
40344  */
40345
40346 /**
40347  * @class Roo.form.ComboBoxArray
40348  * @extends Roo.form.TextField
40349  * A facebook style adder... for lists of email / people / countries  etc...
40350  * pick multiple items from a combo box, and shows each one.
40351  *
40352  *  Fred [x]  Brian [x]  [Pick another |v]
40353  *
40354  *
40355  *  For this to work: it needs various extra information
40356  *    - normal combo problay has
40357  *      name, hiddenName
40358  *    + displayField, valueField
40359  *
40360  *    For our purpose...
40361  *
40362  *
40363  *   If we change from 'extends' to wrapping...
40364  *   
40365  *  
40366  *
40367  
40368  
40369  * @constructor
40370  * Create a new ComboBoxArray.
40371  * @param {Object} config Configuration options
40372  */
40373  
40374
40375 Roo.form.ComboBoxArray = function(config)
40376 {
40377     this.addEvents({
40378         /**
40379          * @event beforeremove
40380          * Fires before remove the value from the list
40381              * @param {Roo.form.ComboBoxArray} _self This combo box array
40382              * @param {Roo.form.ComboBoxArray.Item} item removed item
40383              */
40384         'beforeremove' : true,
40385         /**
40386          * @event remove
40387          * Fires when remove the value from the list
40388              * @param {Roo.form.ComboBoxArray} _self This combo box array
40389              * @param {Roo.form.ComboBoxArray.Item} item removed item
40390              */
40391         'remove' : true
40392         
40393         
40394     });
40395     
40396     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40397     
40398     this.items = new Roo.util.MixedCollection(false);
40399     
40400     // construct the child combo...
40401     
40402     
40403     
40404     
40405    
40406     
40407 }
40408
40409  
40410 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40411
40412     /**
40413      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40414      */
40415     
40416     lastData : false,
40417     
40418     // behavies liek a hiddne field
40419     inputType:      'hidden',
40420     /**
40421      * @cfg {Number} width The width of the box that displays the selected element
40422      */ 
40423     width:          300,
40424
40425     
40426     
40427     /**
40428      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40429      */
40430     name : false,
40431     /**
40432      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40433      */
40434     hiddenName : false,
40435     
40436     
40437     // private the array of items that are displayed..
40438     items  : false,
40439     // private - the hidden field el.
40440     hiddenEl : false,
40441     // private - the filed el..
40442     el : false,
40443     
40444     //validateValue : function() { return true; }, // all values are ok!
40445     //onAddClick: function() { },
40446     
40447     onRender : function(ct, position) 
40448     {
40449         
40450         // create the standard hidden element
40451         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40452         
40453         
40454         // give fake names to child combo;
40455         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40456         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40457         
40458         this.combo = Roo.factory(this.combo, Roo.form);
40459         this.combo.onRender(ct, position);
40460         if (typeof(this.combo.width) != 'undefined') {
40461             this.combo.onResize(this.combo.width,0);
40462         }
40463         
40464         this.combo.initEvents();
40465         
40466         // assigned so form know we need to do this..
40467         this.store          = this.combo.store;
40468         this.valueField     = this.combo.valueField;
40469         this.displayField   = this.combo.displayField ;
40470         
40471         
40472         this.combo.wrap.addClass('x-cbarray-grp');
40473         
40474         var cbwrap = this.combo.wrap.createChild(
40475             {tag: 'div', cls: 'x-cbarray-cb'},
40476             this.combo.el.dom
40477         );
40478         
40479              
40480         this.hiddenEl = this.combo.wrap.createChild({
40481             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40482         });
40483         this.el = this.combo.wrap.createChild({
40484             tag: 'input',  type:'hidden' , name: this.name, value : ''
40485         });
40486          //   this.el.dom.removeAttribute("name");
40487         
40488         
40489         this.outerWrap = this.combo.wrap;
40490         this.wrap = cbwrap;
40491         
40492         this.outerWrap.setWidth(this.width);
40493         this.outerWrap.dom.removeChild(this.el.dom);
40494         
40495         this.wrap.dom.appendChild(this.el.dom);
40496         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40497         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40498         
40499         this.combo.trigger.setStyle('position','relative');
40500         this.combo.trigger.setStyle('left', '0px');
40501         this.combo.trigger.setStyle('top', '2px');
40502         
40503         this.combo.el.setStyle('vertical-align', 'text-bottom');
40504         
40505         //this.trigger.setStyle('vertical-align', 'top');
40506         
40507         // this should use the code from combo really... on('add' ....)
40508         if (this.adder) {
40509             
40510         
40511             this.adder = this.outerWrap.createChild(
40512                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40513             var _t = this;
40514             this.adder.on('click', function(e) {
40515                 _t.fireEvent('adderclick', this, e);
40516             }, _t);
40517         }
40518         //var _t = this;
40519         //this.adder.on('click', this.onAddClick, _t);
40520         
40521         
40522         this.combo.on('select', function(cb, rec, ix) {
40523             this.addItem(rec.data);
40524             
40525             cb.setValue('');
40526             cb.el.dom.value = '';
40527             //cb.lastData = rec.data;
40528             // add to list
40529             
40530         }, this);
40531         
40532         
40533     },
40534     
40535     
40536     getName: function()
40537     {
40538         // returns hidden if it's set..
40539         if (!this.rendered) {return ''};
40540         return  this.hiddenName ? this.hiddenName : this.name;
40541         
40542     },
40543     
40544     
40545     onResize: function(w, h){
40546         
40547         return;
40548         // not sure if this is needed..
40549         //this.combo.onResize(w,h);
40550         
40551         if(typeof w != 'number'){
40552             // we do not handle it!?!?
40553             return;
40554         }
40555         var tw = this.combo.trigger.getWidth();
40556         tw += this.addicon ? this.addicon.getWidth() : 0;
40557         tw += this.editicon ? this.editicon.getWidth() : 0;
40558         var x = w - tw;
40559         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40560             
40561         this.combo.trigger.setStyle('left', '0px');
40562         
40563         if(this.list && this.listWidth === undefined){
40564             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40565             this.list.setWidth(lw);
40566             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40567         }
40568         
40569     
40570         
40571     },
40572     
40573     addItem: function(rec)
40574     {
40575         var valueField = this.combo.valueField;
40576         var displayField = this.combo.displayField;
40577         if (this.items.indexOfKey(rec[valueField]) > -1) {
40578             //console.log("GOT " + rec.data.id);
40579             return;
40580         }
40581         
40582         var x = new Roo.form.ComboBoxArray.Item({
40583             //id : rec[this.idField],
40584             data : rec,
40585             displayField : displayField ,
40586             tipField : displayField ,
40587             cb : this
40588         });
40589         // use the 
40590         this.items.add(rec[valueField],x);
40591         // add it before the element..
40592         this.updateHiddenEl();
40593         x.render(this.outerWrap, this.wrap.dom);
40594         // add the image handler..
40595     },
40596     
40597     updateHiddenEl : function()
40598     {
40599         this.validate();
40600         if (!this.hiddenEl) {
40601             return;
40602         }
40603         var ar = [];
40604         var idField = this.combo.valueField;
40605         
40606         this.items.each(function(f) {
40607             ar.push(f.data[idField]);
40608            
40609         });
40610         this.hiddenEl.dom.value = ar.join(',');
40611         this.validate();
40612     },
40613     
40614     reset : function()
40615     {
40616         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40617         this.items.each(function(f) {
40618            f.remove(); 
40619         });
40620         this.el.dom.value = '';
40621         if (this.hiddenEl) {
40622             this.hiddenEl.dom.value = '';
40623         }
40624         
40625     },
40626     getValue: function()
40627     {
40628         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40629     },
40630     setValue: function(v) // not a valid action - must use addItems..
40631     {
40632          
40633         this.reset();
40634         
40635         
40636         
40637         if (this.store.isLocal && (typeof(v) == 'string')) {
40638             // then we can use the store to find the values..
40639             // comma seperated at present.. this needs to allow JSON based encoding..
40640             this.hiddenEl.value  = v;
40641             var v_ar = [];
40642             Roo.each(v.split(','), function(k) {
40643                 Roo.log("CHECK " + this.valueField + ',' + k);
40644                 var li = this.store.query(this.valueField, k);
40645                 if (!li.length) {
40646                     return;
40647                 }
40648                 var add = {};
40649                 add[this.valueField] = k;
40650                 add[this.displayField] = li.item(0).data[this.displayField];
40651                 
40652                 this.addItem(add);
40653             }, this) 
40654              
40655         }
40656         if (typeof(v) == 'object' ) {
40657             // then let's assume it's an array of objects..
40658             Roo.each(v, function(l) {
40659                 this.addItem(l);
40660             }, this);
40661              
40662         }
40663         
40664         
40665     },
40666     setFromData: function(v)
40667     {
40668         // this recieves an object, if setValues is called.
40669         this.reset();
40670         this.el.dom.value = v[this.displayField];
40671         this.hiddenEl.dom.value = v[this.valueField];
40672         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40673             return;
40674         }
40675         var kv = v[this.valueField];
40676         var dv = v[this.displayField];
40677         kv = typeof(kv) != 'string' ? '' : kv;
40678         dv = typeof(dv) != 'string' ? '' : dv;
40679         
40680         
40681         var keys = kv.split(',');
40682         var display = dv.split(',');
40683         for (var i = 0 ; i < keys.length; i++) {
40684             
40685             add = {};
40686             add[this.valueField] = keys[i];
40687             add[this.displayField] = display[i];
40688             this.addItem(add);
40689         }
40690       
40691         
40692     },
40693     
40694     /**
40695      * Validates the combox array value
40696      * @return {Boolean} True if the value is valid, else false
40697      */
40698     validate : function(){
40699         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40700             this.clearInvalid();
40701             return true;
40702         }
40703         return false;
40704     },
40705     
40706     validateValue : function(value){
40707         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40708         
40709     },
40710     
40711     /*@
40712      * overide
40713      * 
40714      */
40715     isDirty : function() {
40716         if(this.disabled) {
40717             return false;
40718         }
40719         
40720         try {
40721             var d = Roo.decode(String(this.originalValue));
40722         } catch (e) {
40723             return String(this.getValue()) !== String(this.originalValue);
40724         }
40725         
40726         var originalValue = [];
40727         
40728         for (var i = 0; i < d.length; i++){
40729             originalValue.push(d[i][this.valueField]);
40730         }
40731         
40732         return String(this.getValue()) !== String(originalValue.join(','));
40733         
40734     }
40735     
40736 });
40737
40738
40739
40740 /**
40741  * @class Roo.form.ComboBoxArray.Item
40742  * @extends Roo.BoxComponent
40743  * A selected item in the list
40744  *  Fred [x]  Brian [x]  [Pick another |v]
40745  * 
40746  * @constructor
40747  * Create a new item.
40748  * @param {Object} config Configuration options
40749  */
40750  
40751 Roo.form.ComboBoxArray.Item = function(config) {
40752     config.id = Roo.id();
40753     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40754 }
40755
40756 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40757     data : {},
40758     cb: false,
40759     displayField : false,
40760     tipField : false,
40761     
40762     
40763     defaultAutoCreate : {
40764         tag: 'div',
40765         cls: 'x-cbarray-item',
40766         cn : [ 
40767             { tag: 'div' },
40768             {
40769                 tag: 'img',
40770                 width:16,
40771                 height : 16,
40772                 src : Roo.BLANK_IMAGE_URL ,
40773                 align: 'center'
40774             }
40775         ]
40776         
40777     },
40778     
40779  
40780     onRender : function(ct, position)
40781     {
40782         Roo.form.Field.superclass.onRender.call(this, ct, position);
40783         
40784         if(!this.el){
40785             var cfg = this.getAutoCreate();
40786             this.el = ct.createChild(cfg, position);
40787         }
40788         
40789         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40790         
40791         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40792             this.cb.renderer(this.data) :
40793             String.format('{0}',this.data[this.displayField]);
40794         
40795             
40796         this.el.child('div').dom.setAttribute('qtip',
40797                         String.format('{0}',this.data[this.tipField])
40798         );
40799         
40800         this.el.child('img').on('click', this.remove, this);
40801         
40802     },
40803    
40804     remove : function()
40805     {
40806         if(this.cb.disabled){
40807             return;
40808         }
40809         
40810         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40811             this.cb.items.remove(this);
40812             this.el.child('img').un('click', this.remove, this);
40813             this.el.remove();
40814             this.cb.updateHiddenEl();
40815
40816             this.cb.fireEvent('remove', this.cb, this);
40817         }
40818         
40819     }
40820 });/*
40821  * Based on:
40822  * Ext JS Library 1.1.1
40823  * Copyright(c) 2006-2007, Ext JS, LLC.
40824  *
40825  * Originally Released Under LGPL - original licence link has changed is not relivant.
40826  *
40827  * Fork - LGPL
40828  * <script type="text/javascript">
40829  */
40830 /**
40831  * @class Roo.form.Checkbox
40832  * @extends Roo.form.Field
40833  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40834  * @constructor
40835  * Creates a new Checkbox
40836  * @param {Object} config Configuration options
40837  */
40838 Roo.form.Checkbox = function(config){
40839     Roo.form.Checkbox.superclass.constructor.call(this, config);
40840     this.addEvents({
40841         /**
40842          * @event check
40843          * Fires when the checkbox is checked or unchecked.
40844              * @param {Roo.form.Checkbox} this This checkbox
40845              * @param {Boolean} checked The new checked value
40846              */
40847         check : true
40848     });
40849 };
40850
40851 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40852     /**
40853      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40854      */
40855     focusClass : undefined,
40856     /**
40857      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40858      */
40859     fieldClass: "x-form-field",
40860     /**
40861      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40862      */
40863     checked: false,
40864     /**
40865      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40866      * {tag: "input", type: "checkbox", autocomplete: "off"})
40867      */
40868     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40869     /**
40870      * @cfg {String} boxLabel The text that appears beside the checkbox
40871      */
40872     boxLabel : "",
40873     /**
40874      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40875      */  
40876     inputValue : '1',
40877     /**
40878      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40879      */
40880      valueOff: '0', // value when not checked..
40881
40882     actionMode : 'viewEl', 
40883     //
40884     // private
40885     itemCls : 'x-menu-check-item x-form-item',
40886     groupClass : 'x-menu-group-item',
40887     inputType : 'hidden',
40888     
40889     
40890     inSetChecked: false, // check that we are not calling self...
40891     
40892     inputElement: false, // real input element?
40893     basedOn: false, // ????
40894     
40895     isFormField: true, // not sure where this is needed!!!!
40896
40897     onResize : function(){
40898         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40899         if(!this.boxLabel){
40900             this.el.alignTo(this.wrap, 'c-c');
40901         }
40902     },
40903
40904     initEvents : function(){
40905         Roo.form.Checkbox.superclass.initEvents.call(this);
40906         this.el.on("click", this.onClick,  this);
40907         this.el.on("change", this.onClick,  this);
40908     },
40909
40910
40911     getResizeEl : function(){
40912         return this.wrap;
40913     },
40914
40915     getPositionEl : function(){
40916         return this.wrap;
40917     },
40918
40919     // private
40920     onRender : function(ct, position){
40921         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40922         /*
40923         if(this.inputValue !== undefined){
40924             this.el.dom.value = this.inputValue;
40925         }
40926         */
40927         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40928         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40929         var viewEl = this.wrap.createChild({ 
40930             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40931         this.viewEl = viewEl;   
40932         this.wrap.on('click', this.onClick,  this); 
40933         
40934         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40935         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40936         
40937         
40938         
40939         if(this.boxLabel){
40940             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40941         //    viewEl.on('click', this.onClick,  this); 
40942         }
40943         //if(this.checked){
40944             this.setChecked(this.checked);
40945         //}else{
40946             //this.checked = this.el.dom;
40947         //}
40948
40949     },
40950
40951     // private
40952     initValue : Roo.emptyFn,
40953
40954     /**
40955      * Returns the checked state of the checkbox.
40956      * @return {Boolean} True if checked, else false
40957      */
40958     getValue : function(){
40959         if(this.el){
40960             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40961         }
40962         return this.valueOff;
40963         
40964     },
40965
40966         // private
40967     onClick : function(){ 
40968         if (this.disabled) {
40969             return;
40970         }
40971         this.setChecked(!this.checked);
40972
40973         //if(this.el.dom.checked != this.checked){
40974         //    this.setValue(this.el.dom.checked);
40975        // }
40976     },
40977
40978     /**
40979      * Sets the checked state of the checkbox.
40980      * On is always based on a string comparison between inputValue and the param.
40981      * @param {Boolean/String} value - the value to set 
40982      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40983      */
40984     setValue : function(v,suppressEvent){
40985         
40986         
40987         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40988         //if(this.el && this.el.dom){
40989         //    this.el.dom.checked = this.checked;
40990         //    this.el.dom.defaultChecked = this.checked;
40991         //}
40992         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40993         //this.fireEvent("check", this, this.checked);
40994     },
40995     // private..
40996     setChecked : function(state,suppressEvent)
40997     {
40998         if (this.inSetChecked) {
40999             this.checked = state;
41000             return;
41001         }
41002         
41003     
41004         if(this.wrap){
41005             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41006         }
41007         this.checked = state;
41008         if(suppressEvent !== true){
41009             this.fireEvent('check', this, state);
41010         }
41011         this.inSetChecked = true;
41012         this.el.dom.value = state ? this.inputValue : this.valueOff;
41013         this.inSetChecked = false;
41014         
41015     },
41016     // handle setting of hidden value by some other method!!?!?
41017     setFromHidden: function()
41018     {
41019         if(!this.el){
41020             return;
41021         }
41022         //console.log("SET FROM HIDDEN");
41023         //alert('setFrom hidden');
41024         this.setValue(this.el.dom.value);
41025     },
41026     
41027     onDestroy : function()
41028     {
41029         if(this.viewEl){
41030             Roo.get(this.viewEl).remove();
41031         }
41032          
41033         Roo.form.Checkbox.superclass.onDestroy.call(this);
41034     }
41035
41036 });/*
41037  * Based on:
41038  * Ext JS Library 1.1.1
41039  * Copyright(c) 2006-2007, Ext JS, LLC.
41040  *
41041  * Originally Released Under LGPL - original licence link has changed is not relivant.
41042  *
41043  * Fork - LGPL
41044  * <script type="text/javascript">
41045  */
41046  
41047 /**
41048  * @class Roo.form.Radio
41049  * @extends Roo.form.Checkbox
41050  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41051  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41052  * @constructor
41053  * Creates a new Radio
41054  * @param {Object} config Configuration options
41055  */
41056 Roo.form.Radio = function(){
41057     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41058 };
41059 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41060     inputType: 'radio',
41061
41062     /**
41063      * If this radio is part of a group, it will return the selected value
41064      * @return {String}
41065      */
41066     getGroupValue : function(){
41067         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41068     },
41069     
41070     
41071     onRender : function(ct, position){
41072         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41073         
41074         if(this.inputValue !== undefined){
41075             this.el.dom.value = this.inputValue;
41076         }
41077          
41078         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41079         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41080         //var viewEl = this.wrap.createChild({ 
41081         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41082         //this.viewEl = viewEl;   
41083         //this.wrap.on('click', this.onClick,  this); 
41084         
41085         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41086         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41087         
41088         
41089         
41090         if(this.boxLabel){
41091             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41092         //    viewEl.on('click', this.onClick,  this); 
41093         }
41094          if(this.checked){
41095             this.el.dom.checked =   'checked' ;
41096         }
41097          
41098     } 
41099     
41100     
41101 });//<script type="text/javascript">
41102
41103 /*
41104  * Based  Ext JS Library 1.1.1
41105  * Copyright(c) 2006-2007, Ext JS, LLC.
41106  * LGPL
41107  *
41108  */
41109  
41110 /**
41111  * @class Roo.HtmlEditorCore
41112  * @extends Roo.Component
41113  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41114  *
41115  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41116  */
41117
41118 Roo.HtmlEditorCore = function(config){
41119     
41120     
41121     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41122     
41123     
41124     this.addEvents({
41125         /**
41126          * @event initialize
41127          * Fires when the editor is fully initialized (including the iframe)
41128          * @param {Roo.HtmlEditorCore} this
41129          */
41130         initialize: true,
41131         /**
41132          * @event activate
41133          * Fires when the editor is first receives the focus. Any insertion must wait
41134          * until after this event.
41135          * @param {Roo.HtmlEditorCore} this
41136          */
41137         activate: true,
41138          /**
41139          * @event beforesync
41140          * Fires before the textarea is updated with content from the editor iframe. Return false
41141          * to cancel the sync.
41142          * @param {Roo.HtmlEditorCore} this
41143          * @param {String} html
41144          */
41145         beforesync: true,
41146          /**
41147          * @event beforepush
41148          * Fires before the iframe editor is updated with content from the textarea. Return false
41149          * to cancel the push.
41150          * @param {Roo.HtmlEditorCore} this
41151          * @param {String} html
41152          */
41153         beforepush: true,
41154          /**
41155          * @event sync
41156          * Fires when the textarea is updated with content from the editor iframe.
41157          * @param {Roo.HtmlEditorCore} this
41158          * @param {String} html
41159          */
41160         sync: true,
41161          /**
41162          * @event push
41163          * Fires when the iframe editor is updated with content from the textarea.
41164          * @param {Roo.HtmlEditorCore} this
41165          * @param {String} html
41166          */
41167         push: true,
41168         
41169         /**
41170          * @event editorevent
41171          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41172          * @param {Roo.HtmlEditorCore} this
41173          */
41174         editorevent: true
41175         
41176     });
41177     
41178     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41179     
41180     // defaults : white / black...
41181     this.applyBlacklists();
41182     
41183     
41184     
41185 };
41186
41187
41188 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41189
41190
41191      /**
41192      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41193      */
41194     
41195     owner : false,
41196     
41197      /**
41198      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41199      *                        Roo.resizable.
41200      */
41201     resizable : false,
41202      /**
41203      * @cfg {Number} height (in pixels)
41204      */   
41205     height: 300,
41206    /**
41207      * @cfg {Number} width (in pixels)
41208      */   
41209     width: 500,
41210     
41211     /**
41212      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41213      * 
41214      */
41215     stylesheets: false,
41216     
41217     // id of frame..
41218     frameId: false,
41219     
41220     // private properties
41221     validationEvent : false,
41222     deferHeight: true,
41223     initialized : false,
41224     activated : false,
41225     sourceEditMode : false,
41226     onFocus : Roo.emptyFn,
41227     iframePad:3,
41228     hideMode:'offsets',
41229     
41230     clearUp: true,
41231     
41232     // blacklist + whitelisted elements..
41233     black: false,
41234     white: false,
41235      
41236     
41237
41238     /**
41239      * Protected method that will not generally be called directly. It
41240      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41241      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41242      */
41243     getDocMarkup : function(){
41244         // body styles..
41245         var st = '';
41246         
41247         // inherit styels from page...?? 
41248         if (this.stylesheets === false) {
41249             
41250             Roo.get(document.head).select('style').each(function(node) {
41251                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41252             });
41253             
41254             Roo.get(document.head).select('link').each(function(node) { 
41255                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41256             });
41257             
41258         } else if (!this.stylesheets.length) {
41259                 // simple..
41260                 st = '<style type="text/css">' +
41261                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41262                    '</style>';
41263         } else { 
41264             
41265         }
41266         
41267         st +=  '<style type="text/css">' +
41268             'IMG { cursor: pointer } ' +
41269         '</style>';
41270
41271         
41272         return '<html><head>' + st  +
41273             //<style type="text/css">' +
41274             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41275             //'</style>' +
41276             ' </head><body class="roo-htmleditor-body"></body></html>';
41277     },
41278
41279     // private
41280     onRender : function(ct, position)
41281     {
41282         var _t = this;
41283         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41284         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41285         
41286         
41287         this.el.dom.style.border = '0 none';
41288         this.el.dom.setAttribute('tabIndex', -1);
41289         this.el.addClass('x-hidden hide');
41290         
41291         
41292         
41293         if(Roo.isIE){ // fix IE 1px bogus margin
41294             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41295         }
41296        
41297         
41298         this.frameId = Roo.id();
41299         
41300          
41301         
41302         var iframe = this.owner.wrap.createChild({
41303             tag: 'iframe',
41304             cls: 'form-control', // bootstrap..
41305             id: this.frameId,
41306             name: this.frameId,
41307             frameBorder : 'no',
41308             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41309         }, this.el
41310         );
41311         
41312         
41313         this.iframe = iframe.dom;
41314
41315          this.assignDocWin();
41316         
41317         this.doc.designMode = 'on';
41318        
41319         this.doc.open();
41320         this.doc.write(this.getDocMarkup());
41321         this.doc.close();
41322
41323         
41324         var task = { // must defer to wait for browser to be ready
41325             run : function(){
41326                 //console.log("run task?" + this.doc.readyState);
41327                 this.assignDocWin();
41328                 if(this.doc.body || this.doc.readyState == 'complete'){
41329                     try {
41330                         this.doc.designMode="on";
41331                     } catch (e) {
41332                         return;
41333                     }
41334                     Roo.TaskMgr.stop(task);
41335                     this.initEditor.defer(10, this);
41336                 }
41337             },
41338             interval : 10,
41339             duration: 10000,
41340             scope: this
41341         };
41342         Roo.TaskMgr.start(task);
41343
41344     },
41345
41346     // private
41347     onResize : function(w, h)
41348     {
41349          Roo.log('resize: ' +w + ',' + h );
41350         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41351         if(!this.iframe){
41352             return;
41353         }
41354         if(typeof w == 'number'){
41355             
41356             this.iframe.style.width = w + 'px';
41357         }
41358         if(typeof h == 'number'){
41359             
41360             this.iframe.style.height = h + 'px';
41361             if(this.doc){
41362                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41363             }
41364         }
41365         
41366     },
41367
41368     /**
41369      * Toggles the editor between standard and source edit mode.
41370      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41371      */
41372     toggleSourceEdit : function(sourceEditMode){
41373         
41374         this.sourceEditMode = sourceEditMode === true;
41375         
41376         if(this.sourceEditMode){
41377  
41378             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41379             
41380         }else{
41381             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41382             //this.iframe.className = '';
41383             this.deferFocus();
41384         }
41385         //this.setSize(this.owner.wrap.getSize());
41386         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41387     },
41388
41389     
41390   
41391
41392     /**
41393      * Protected method that will not generally be called directly. If you need/want
41394      * custom HTML cleanup, this is the method you should override.
41395      * @param {String} html The HTML to be cleaned
41396      * return {String} The cleaned HTML
41397      */
41398     cleanHtml : function(html){
41399         html = String(html);
41400         if(html.length > 5){
41401             if(Roo.isSafari){ // strip safari nonsense
41402                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41403             }
41404         }
41405         if(html == '&nbsp;'){
41406             html = '';
41407         }
41408         return html;
41409     },
41410
41411     /**
41412      * HTML Editor -> Textarea
41413      * Protected method that will not generally be called directly. Syncs the contents
41414      * of the editor iframe with the textarea.
41415      */
41416     syncValue : function(){
41417         if(this.initialized){
41418             var bd = (this.doc.body || this.doc.documentElement);
41419             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41420             var html = bd.innerHTML;
41421             if(Roo.isSafari){
41422                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41423                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41424                 if(m && m[1]){
41425                     html = '<div style="'+m[0]+'">' + html + '</div>';
41426                 }
41427             }
41428             html = this.cleanHtml(html);
41429             // fix up the special chars.. normaly like back quotes in word...
41430             // however we do not want to do this with chinese..
41431             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41432                 var cc = b.charCodeAt();
41433                 if (
41434                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41435                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41436                     (cc >= 0xf900 && cc < 0xfb00 )
41437                 ) {
41438                         return b;
41439                 }
41440                 return "&#"+cc+";" 
41441             });
41442             if(this.owner.fireEvent('beforesync', this, html) !== false){
41443                 this.el.dom.value = html;
41444                 this.owner.fireEvent('sync', this, html);
41445             }
41446         }
41447     },
41448
41449     /**
41450      * Protected method that will not generally be called directly. Pushes the value of the textarea
41451      * into the iframe editor.
41452      */
41453     pushValue : function(){
41454         if(this.initialized){
41455             var v = this.el.dom.value.trim();
41456             
41457 //            if(v.length < 1){
41458 //                v = '&#160;';
41459 //            }
41460             
41461             if(this.owner.fireEvent('beforepush', this, v) !== false){
41462                 var d = (this.doc.body || this.doc.documentElement);
41463                 d.innerHTML = v;
41464                 this.cleanUpPaste();
41465                 this.el.dom.value = d.innerHTML;
41466                 this.owner.fireEvent('push', this, v);
41467             }
41468         }
41469     },
41470
41471     // private
41472     deferFocus : function(){
41473         this.focus.defer(10, this);
41474     },
41475
41476     // doc'ed in Field
41477     focus : function(){
41478         if(this.win && !this.sourceEditMode){
41479             this.win.focus();
41480         }else{
41481             this.el.focus();
41482         }
41483     },
41484     
41485     assignDocWin: function()
41486     {
41487         var iframe = this.iframe;
41488         
41489          if(Roo.isIE){
41490             this.doc = iframe.contentWindow.document;
41491             this.win = iframe.contentWindow;
41492         } else {
41493 //            if (!Roo.get(this.frameId)) {
41494 //                return;
41495 //            }
41496 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41497 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41498             
41499             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41500                 return;
41501             }
41502             
41503             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41504             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41505         }
41506     },
41507     
41508     // private
41509     initEditor : function(){
41510         //console.log("INIT EDITOR");
41511         this.assignDocWin();
41512         
41513         
41514         
41515         this.doc.designMode="on";
41516         this.doc.open();
41517         this.doc.write(this.getDocMarkup());
41518         this.doc.close();
41519         
41520         var dbody = (this.doc.body || this.doc.documentElement);
41521         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41522         // this copies styles from the containing element into thsi one..
41523         // not sure why we need all of this..
41524         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41525         
41526         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41527         //ss['background-attachment'] = 'fixed'; // w3c
41528         dbody.bgProperties = 'fixed'; // ie
41529         //Roo.DomHelper.applyStyles(dbody, ss);
41530         Roo.EventManager.on(this.doc, {
41531             //'mousedown': this.onEditorEvent,
41532             'mouseup': this.onEditorEvent,
41533             'dblclick': this.onEditorEvent,
41534             'click': this.onEditorEvent,
41535             'keyup': this.onEditorEvent,
41536             buffer:100,
41537             scope: this
41538         });
41539         if(Roo.isGecko){
41540             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41541         }
41542         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41543             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41544         }
41545         this.initialized = true;
41546
41547         this.owner.fireEvent('initialize', this);
41548         this.pushValue();
41549     },
41550
41551     // private
41552     onDestroy : function(){
41553         
41554         
41555         
41556         if(this.rendered){
41557             
41558             //for (var i =0; i < this.toolbars.length;i++) {
41559             //    // fixme - ask toolbars for heights?
41560             //    this.toolbars[i].onDestroy();
41561            // }
41562             
41563             //this.wrap.dom.innerHTML = '';
41564             //this.wrap.remove();
41565         }
41566     },
41567
41568     // private
41569     onFirstFocus : function(){
41570         
41571         this.assignDocWin();
41572         
41573         
41574         this.activated = true;
41575          
41576     
41577         if(Roo.isGecko){ // prevent silly gecko errors
41578             this.win.focus();
41579             var s = this.win.getSelection();
41580             if(!s.focusNode || s.focusNode.nodeType != 3){
41581                 var r = s.getRangeAt(0);
41582                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41583                 r.collapse(true);
41584                 this.deferFocus();
41585             }
41586             try{
41587                 this.execCmd('useCSS', true);
41588                 this.execCmd('styleWithCSS', false);
41589             }catch(e){}
41590         }
41591         this.owner.fireEvent('activate', this);
41592     },
41593
41594     // private
41595     adjustFont: function(btn){
41596         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41597         //if(Roo.isSafari){ // safari
41598         //    adjust *= 2;
41599        // }
41600         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41601         if(Roo.isSafari){ // safari
41602             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41603             v =  (v < 10) ? 10 : v;
41604             v =  (v > 48) ? 48 : v;
41605             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41606             
41607         }
41608         
41609         
41610         v = Math.max(1, v+adjust);
41611         
41612         this.execCmd('FontSize', v  );
41613     },
41614
41615     onEditorEvent : function(e)
41616     {
41617         this.owner.fireEvent('editorevent', this, e);
41618       //  this.updateToolbar();
41619         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41620     },
41621
41622     insertTag : function(tg)
41623     {
41624         // could be a bit smarter... -> wrap the current selected tRoo..
41625         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41626             
41627             range = this.createRange(this.getSelection());
41628             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41629             wrappingNode.appendChild(range.extractContents());
41630             range.insertNode(wrappingNode);
41631
41632             return;
41633             
41634             
41635             
41636         }
41637         this.execCmd("formatblock",   tg);
41638         
41639     },
41640     
41641     insertText : function(txt)
41642     {
41643         
41644         
41645         var range = this.createRange();
41646         range.deleteContents();
41647                //alert(Sender.getAttribute('label'));
41648                
41649         range.insertNode(this.doc.createTextNode(txt));
41650     } ,
41651     
41652      
41653
41654     /**
41655      * Executes a Midas editor command on the editor document and performs necessary focus and
41656      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41657      * @param {String} cmd The Midas command
41658      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41659      */
41660     relayCmd : function(cmd, value){
41661         this.win.focus();
41662         this.execCmd(cmd, value);
41663         this.owner.fireEvent('editorevent', this);
41664         //this.updateToolbar();
41665         this.owner.deferFocus();
41666     },
41667
41668     /**
41669      * Executes a Midas editor command directly on the editor document.
41670      * For visual commands, you should use {@link #relayCmd} instead.
41671      * <b>This should only be called after the editor is initialized.</b>
41672      * @param {String} cmd The Midas command
41673      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41674      */
41675     execCmd : function(cmd, value){
41676         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41677         this.syncValue();
41678     },
41679  
41680  
41681    
41682     /**
41683      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41684      * to insert tRoo.
41685      * @param {String} text | dom node.. 
41686      */
41687     insertAtCursor : function(text)
41688     {
41689         
41690         
41691         
41692         if(!this.activated){
41693             return;
41694         }
41695         /*
41696         if(Roo.isIE){
41697             this.win.focus();
41698             var r = this.doc.selection.createRange();
41699             if(r){
41700                 r.collapse(true);
41701                 r.pasteHTML(text);
41702                 this.syncValue();
41703                 this.deferFocus();
41704             
41705             }
41706             return;
41707         }
41708         */
41709         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41710             this.win.focus();
41711             
41712             
41713             // from jquery ui (MIT licenced)
41714             var range, node;
41715             var win = this.win;
41716             
41717             if (win.getSelection && win.getSelection().getRangeAt) {
41718                 range = win.getSelection().getRangeAt(0);
41719                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41720                 range.insertNode(node);
41721             } else if (win.document.selection && win.document.selection.createRange) {
41722                 // no firefox support
41723                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41724                 win.document.selection.createRange().pasteHTML(txt);
41725             } else {
41726                 // no firefox support
41727                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41728                 this.execCmd('InsertHTML', txt);
41729             } 
41730             
41731             this.syncValue();
41732             
41733             this.deferFocus();
41734         }
41735     },
41736  // private
41737     mozKeyPress : function(e){
41738         if(e.ctrlKey){
41739             var c = e.getCharCode(), cmd;
41740           
41741             if(c > 0){
41742                 c = String.fromCharCode(c).toLowerCase();
41743                 switch(c){
41744                     case 'b':
41745                         cmd = 'bold';
41746                         break;
41747                     case 'i':
41748                         cmd = 'italic';
41749                         break;
41750                     
41751                     case 'u':
41752                         cmd = 'underline';
41753                         break;
41754                     
41755                     case 'v':
41756                         this.cleanUpPaste.defer(100, this);
41757                         return;
41758                         
41759                 }
41760                 if(cmd){
41761                     this.win.focus();
41762                     this.execCmd(cmd);
41763                     this.deferFocus();
41764                     e.preventDefault();
41765                 }
41766                 
41767             }
41768         }
41769     },
41770
41771     // private
41772     fixKeys : function(){ // load time branching for fastest keydown performance
41773         if(Roo.isIE){
41774             return function(e){
41775                 var k = e.getKey(), r;
41776                 if(k == e.TAB){
41777                     e.stopEvent();
41778                     r = this.doc.selection.createRange();
41779                     if(r){
41780                         r.collapse(true);
41781                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41782                         this.deferFocus();
41783                     }
41784                     return;
41785                 }
41786                 
41787                 if(k == e.ENTER){
41788                     r = this.doc.selection.createRange();
41789                     if(r){
41790                         var target = r.parentElement();
41791                         if(!target || target.tagName.toLowerCase() != 'li'){
41792                             e.stopEvent();
41793                             r.pasteHTML('<br />');
41794                             r.collapse(false);
41795                             r.select();
41796                         }
41797                     }
41798                 }
41799                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41800                     this.cleanUpPaste.defer(100, this);
41801                     return;
41802                 }
41803                 
41804                 
41805             };
41806         }else if(Roo.isOpera){
41807             return function(e){
41808                 var k = e.getKey();
41809                 if(k == e.TAB){
41810                     e.stopEvent();
41811                     this.win.focus();
41812                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41813                     this.deferFocus();
41814                 }
41815                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41816                     this.cleanUpPaste.defer(100, this);
41817                     return;
41818                 }
41819                 
41820             };
41821         }else if(Roo.isSafari){
41822             return function(e){
41823                 var k = e.getKey();
41824                 
41825                 if(k == e.TAB){
41826                     e.stopEvent();
41827                     this.execCmd('InsertText','\t');
41828                     this.deferFocus();
41829                     return;
41830                 }
41831                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41832                     this.cleanUpPaste.defer(100, this);
41833                     return;
41834                 }
41835                 
41836              };
41837         }
41838     }(),
41839     
41840     getAllAncestors: function()
41841     {
41842         var p = this.getSelectedNode();
41843         var a = [];
41844         if (!p) {
41845             a.push(p); // push blank onto stack..
41846             p = this.getParentElement();
41847         }
41848         
41849         
41850         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41851             a.push(p);
41852             p = p.parentNode;
41853         }
41854         a.push(this.doc.body);
41855         return a;
41856     },
41857     lastSel : false,
41858     lastSelNode : false,
41859     
41860     
41861     getSelection : function() 
41862     {
41863         this.assignDocWin();
41864         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41865     },
41866     
41867     getSelectedNode: function() 
41868     {
41869         // this may only work on Gecko!!!
41870         
41871         // should we cache this!!!!
41872         
41873         
41874         
41875          
41876         var range = this.createRange(this.getSelection()).cloneRange();
41877         
41878         if (Roo.isIE) {
41879             var parent = range.parentElement();
41880             while (true) {
41881                 var testRange = range.duplicate();
41882                 testRange.moveToElementText(parent);
41883                 if (testRange.inRange(range)) {
41884                     break;
41885                 }
41886                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41887                     break;
41888                 }
41889                 parent = parent.parentElement;
41890             }
41891             return parent;
41892         }
41893         
41894         // is ancestor a text element.
41895         var ac =  range.commonAncestorContainer;
41896         if (ac.nodeType == 3) {
41897             ac = ac.parentNode;
41898         }
41899         
41900         var ar = ac.childNodes;
41901          
41902         var nodes = [];
41903         var other_nodes = [];
41904         var has_other_nodes = false;
41905         for (var i=0;i<ar.length;i++) {
41906             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41907                 continue;
41908             }
41909             // fullly contained node.
41910             
41911             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41912                 nodes.push(ar[i]);
41913                 continue;
41914             }
41915             
41916             // probably selected..
41917             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41918                 other_nodes.push(ar[i]);
41919                 continue;
41920             }
41921             // outer..
41922             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41923                 continue;
41924             }
41925             
41926             
41927             has_other_nodes = true;
41928         }
41929         if (!nodes.length && other_nodes.length) {
41930             nodes= other_nodes;
41931         }
41932         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41933             return false;
41934         }
41935         
41936         return nodes[0];
41937     },
41938     createRange: function(sel)
41939     {
41940         // this has strange effects when using with 
41941         // top toolbar - not sure if it's a great idea.
41942         //this.editor.contentWindow.focus();
41943         if (typeof sel != "undefined") {
41944             try {
41945                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41946             } catch(e) {
41947                 return this.doc.createRange();
41948             }
41949         } else {
41950             return this.doc.createRange();
41951         }
41952     },
41953     getParentElement: function()
41954     {
41955         
41956         this.assignDocWin();
41957         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41958         
41959         var range = this.createRange(sel);
41960          
41961         try {
41962             var p = range.commonAncestorContainer;
41963             while (p.nodeType == 3) { // text node
41964                 p = p.parentNode;
41965             }
41966             return p;
41967         } catch (e) {
41968             return null;
41969         }
41970     
41971     },
41972     /***
41973      *
41974      * Range intersection.. the hard stuff...
41975      *  '-1' = before
41976      *  '0' = hits..
41977      *  '1' = after.
41978      *         [ -- selected range --- ]
41979      *   [fail]                        [fail]
41980      *
41981      *    basically..
41982      *      if end is before start or  hits it. fail.
41983      *      if start is after end or hits it fail.
41984      *
41985      *   if either hits (but other is outside. - then it's not 
41986      *   
41987      *    
41988      **/
41989     
41990     
41991     // @see http://www.thismuchiknow.co.uk/?p=64.
41992     rangeIntersectsNode : function(range, node)
41993     {
41994         var nodeRange = node.ownerDocument.createRange();
41995         try {
41996             nodeRange.selectNode(node);
41997         } catch (e) {
41998             nodeRange.selectNodeContents(node);
41999         }
42000     
42001         var rangeStartRange = range.cloneRange();
42002         rangeStartRange.collapse(true);
42003     
42004         var rangeEndRange = range.cloneRange();
42005         rangeEndRange.collapse(false);
42006     
42007         var nodeStartRange = nodeRange.cloneRange();
42008         nodeStartRange.collapse(true);
42009     
42010         var nodeEndRange = nodeRange.cloneRange();
42011         nodeEndRange.collapse(false);
42012     
42013         return rangeStartRange.compareBoundaryPoints(
42014                  Range.START_TO_START, nodeEndRange) == -1 &&
42015                rangeEndRange.compareBoundaryPoints(
42016                  Range.START_TO_START, nodeStartRange) == 1;
42017         
42018          
42019     },
42020     rangeCompareNode : function(range, node)
42021     {
42022         var nodeRange = node.ownerDocument.createRange();
42023         try {
42024             nodeRange.selectNode(node);
42025         } catch (e) {
42026             nodeRange.selectNodeContents(node);
42027         }
42028         
42029         
42030         range.collapse(true);
42031     
42032         nodeRange.collapse(true);
42033      
42034         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42035         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42036          
42037         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42038         
42039         var nodeIsBefore   =  ss == 1;
42040         var nodeIsAfter    = ee == -1;
42041         
42042         if (nodeIsBefore && nodeIsAfter)
42043             return 0; // outer
42044         if (!nodeIsBefore && nodeIsAfter)
42045             return 1; //right trailed.
42046         
42047         if (nodeIsBefore && !nodeIsAfter)
42048             return 2;  // left trailed.
42049         // fully contined.
42050         return 3;
42051     },
42052
42053     // private? - in a new class?
42054     cleanUpPaste :  function()
42055     {
42056         // cleans up the whole document..
42057         Roo.log('cleanuppaste');
42058         
42059         this.cleanUpChildren(this.doc.body);
42060         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42061         if (clean != this.doc.body.innerHTML) {
42062             this.doc.body.innerHTML = clean;
42063         }
42064         
42065     },
42066     
42067     cleanWordChars : function(input) {// change the chars to hex code
42068         var he = Roo.HtmlEditorCore;
42069         
42070         var output = input;
42071         Roo.each(he.swapCodes, function(sw) { 
42072             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42073             
42074             output = output.replace(swapper, sw[1]);
42075         });
42076         
42077         return output;
42078     },
42079     
42080     
42081     cleanUpChildren : function (n)
42082     {
42083         if (!n.childNodes.length) {
42084             return;
42085         }
42086         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42087            this.cleanUpChild(n.childNodes[i]);
42088         }
42089     },
42090     
42091     
42092         
42093     
42094     cleanUpChild : function (node)
42095     {
42096         var ed = this;
42097         //console.log(node);
42098         if (node.nodeName == "#text") {
42099             // clean up silly Windows -- stuff?
42100             return; 
42101         }
42102         if (node.nodeName == "#comment") {
42103             node.parentNode.removeChild(node);
42104             // clean up silly Windows -- stuff?
42105             return; 
42106         }
42107         var lcname = node.tagName.toLowerCase();
42108         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42109         // whitelist of tags..
42110         
42111         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42112             // remove node.
42113             node.parentNode.removeChild(node);
42114             return;
42115             
42116         }
42117         
42118         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42119         
42120         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42121         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42122         
42123         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42124         //    remove_keep_children = true;
42125         //}
42126         
42127         if (remove_keep_children) {
42128             this.cleanUpChildren(node);
42129             // inserts everything just before this node...
42130             while (node.childNodes.length) {
42131                 var cn = node.childNodes[0];
42132                 node.removeChild(cn);
42133                 node.parentNode.insertBefore(cn, node);
42134             }
42135             node.parentNode.removeChild(node);
42136             return;
42137         }
42138         
42139         if (!node.attributes || !node.attributes.length) {
42140             this.cleanUpChildren(node);
42141             return;
42142         }
42143         
42144         function cleanAttr(n,v)
42145         {
42146             
42147             if (v.match(/^\./) || v.match(/^\//)) {
42148                 return;
42149             }
42150             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42151                 return;
42152             }
42153             if (v.match(/^#/)) {
42154                 return;
42155             }
42156 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42157             node.removeAttribute(n);
42158             
42159         }
42160         
42161         var cwhite = this.cwhite;
42162         var cblack = this.cblack;
42163             
42164         function cleanStyle(n,v)
42165         {
42166             if (v.match(/expression/)) { //XSS?? should we even bother..
42167                 node.removeAttribute(n);
42168                 return;
42169             }
42170             
42171             var parts = v.split(/;/);
42172             var clean = [];
42173             
42174             Roo.each(parts, function(p) {
42175                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42176                 if (!p.length) {
42177                     return true;
42178                 }
42179                 var l = p.split(':').shift().replace(/\s+/g,'');
42180                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42181                 
42182                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42183 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42184                     //node.removeAttribute(n);
42185                     return true;
42186                 }
42187                 //Roo.log()
42188                 // only allow 'c whitelisted system attributes'
42189                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42190 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42191                     //node.removeAttribute(n);
42192                     return true;
42193                 }
42194                 
42195                 
42196                  
42197                 
42198                 clean.push(p);
42199                 return true;
42200             });
42201             if (clean.length) { 
42202                 node.setAttribute(n, clean.join(';'));
42203             } else {
42204                 node.removeAttribute(n);
42205             }
42206             
42207         }
42208         
42209         
42210         for (var i = node.attributes.length-1; i > -1 ; i--) {
42211             var a = node.attributes[i];
42212             //console.log(a);
42213             
42214             if (a.name.toLowerCase().substr(0,2)=='on')  {
42215                 node.removeAttribute(a.name);
42216                 continue;
42217             }
42218             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42219                 node.removeAttribute(a.name);
42220                 continue;
42221             }
42222             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42223                 cleanAttr(a.name,a.value); // fixme..
42224                 continue;
42225             }
42226             if (a.name == 'style') {
42227                 cleanStyle(a.name,a.value);
42228                 continue;
42229             }
42230             /// clean up MS crap..
42231             // tecnically this should be a list of valid class'es..
42232             
42233             
42234             if (a.name == 'class') {
42235                 if (a.value.match(/^Mso/)) {
42236                     node.className = '';
42237                 }
42238                 
42239                 if (a.value.match(/body/)) {
42240                     node.className = '';
42241                 }
42242                 continue;
42243             }
42244             
42245             // style cleanup!?
42246             // class cleanup?
42247             
42248         }
42249         
42250         
42251         this.cleanUpChildren(node);
42252         
42253         
42254     },
42255     
42256     /**
42257      * Clean up MS wordisms...
42258      */
42259     cleanWord : function(node)
42260     {
42261         
42262         
42263         if (!node) {
42264             this.cleanWord(this.doc.body);
42265             return;
42266         }
42267         if (node.nodeName == "#text") {
42268             // clean up silly Windows -- stuff?
42269             return; 
42270         }
42271         if (node.nodeName == "#comment") {
42272             node.parentNode.removeChild(node);
42273             // clean up silly Windows -- stuff?
42274             return; 
42275         }
42276         
42277         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42278             node.parentNode.removeChild(node);
42279             return;
42280         }
42281         
42282         // remove - but keep children..
42283         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42284             while (node.childNodes.length) {
42285                 var cn = node.childNodes[0];
42286                 node.removeChild(cn);
42287                 node.parentNode.insertBefore(cn, node);
42288             }
42289             node.parentNode.removeChild(node);
42290             this.iterateChildren(node, this.cleanWord);
42291             return;
42292         }
42293         // clean styles
42294         if (node.className.length) {
42295             
42296             var cn = node.className.split(/\W+/);
42297             var cna = [];
42298             Roo.each(cn, function(cls) {
42299                 if (cls.match(/Mso[a-zA-Z]+/)) {
42300                     return;
42301                 }
42302                 cna.push(cls);
42303             });
42304             node.className = cna.length ? cna.join(' ') : '';
42305             if (!cna.length) {
42306                 node.removeAttribute("class");
42307             }
42308         }
42309         
42310         if (node.hasAttribute("lang")) {
42311             node.removeAttribute("lang");
42312         }
42313         
42314         if (node.hasAttribute("style")) {
42315             
42316             var styles = node.getAttribute("style").split(";");
42317             var nstyle = [];
42318             Roo.each(styles, function(s) {
42319                 if (!s.match(/:/)) {
42320                     return;
42321                 }
42322                 var kv = s.split(":");
42323                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42324                     return;
42325                 }
42326                 // what ever is left... we allow.
42327                 nstyle.push(s);
42328             });
42329             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42330             if (!nstyle.length) {
42331                 node.removeAttribute('style');
42332             }
42333         }
42334         this.iterateChildren(node, this.cleanWord);
42335         
42336         
42337         
42338     },
42339     /**
42340      * iterateChildren of a Node, calling fn each time, using this as the scole..
42341      * @param {DomNode} node node to iterate children of.
42342      * @param {Function} fn method of this class to call on each item.
42343      */
42344     iterateChildren : function(node, fn)
42345     {
42346         if (!node.childNodes.length) {
42347                 return;
42348         }
42349         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42350            fn.call(this, node.childNodes[i])
42351         }
42352     },
42353     
42354     
42355     /**
42356      * cleanTableWidths.
42357      *
42358      * Quite often pasting from word etc.. results in tables with column and widths.
42359      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42360      *
42361      */
42362     cleanTableWidths : function(node)
42363     {
42364          
42365          
42366         if (!node) {
42367             this.cleanTableWidths(this.doc.body);
42368             return;
42369         }
42370         
42371         // ignore list...
42372         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42373             return; 
42374         }
42375         Roo.log(node.tagName);
42376         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42377             this.iterateChildren(node, this.cleanTableWidths);
42378             return;
42379         }
42380         if (node.hasAttribute('width')) {
42381             node.removeAttribute('width');
42382         }
42383         
42384          
42385         if (node.hasAttribute("style")) {
42386             // pretty basic...
42387             
42388             var styles = node.getAttribute("style").split(";");
42389             var nstyle = [];
42390             Roo.each(styles, function(s) {
42391                 if (!s.match(/:/)) {
42392                     return;
42393                 }
42394                 var kv = s.split(":");
42395                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42396                     return;
42397                 }
42398                 // what ever is left... we allow.
42399                 nstyle.push(s);
42400             });
42401             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42402             if (!nstyle.length) {
42403                 node.removeAttribute('style');
42404             }
42405         }
42406         
42407         this.iterateChildren(node, this.cleanTableWidths);
42408         
42409         
42410     },
42411     
42412     
42413     
42414     
42415     domToHTML : function(currentElement, depth, nopadtext) {
42416         
42417         depth = depth || 0;
42418         nopadtext = nopadtext || false;
42419     
42420         if (!currentElement) {
42421             return this.domToHTML(this.doc.body);
42422         }
42423         
42424         //Roo.log(currentElement);
42425         var j;
42426         var allText = false;
42427         var nodeName = currentElement.nodeName;
42428         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42429         
42430         if  (nodeName == '#text') {
42431             
42432             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42433         }
42434         
42435         
42436         var ret = '';
42437         if (nodeName != 'BODY') {
42438              
42439             var i = 0;
42440             // Prints the node tagName, such as <A>, <IMG>, etc
42441             if (tagName) {
42442                 var attr = [];
42443                 for(i = 0; i < currentElement.attributes.length;i++) {
42444                     // quoting?
42445                     var aname = currentElement.attributes.item(i).name;
42446                     if (!currentElement.attributes.item(i).value.length) {
42447                         continue;
42448                     }
42449                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42450                 }
42451                 
42452                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42453             } 
42454             else {
42455                 
42456                 // eack
42457             }
42458         } else {
42459             tagName = false;
42460         }
42461         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42462             return ret;
42463         }
42464         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42465             nopadtext = true;
42466         }
42467         
42468         
42469         // Traverse the tree
42470         i = 0;
42471         var currentElementChild = currentElement.childNodes.item(i);
42472         var allText = true;
42473         var innerHTML  = '';
42474         lastnode = '';
42475         while (currentElementChild) {
42476             // Formatting code (indent the tree so it looks nice on the screen)
42477             var nopad = nopadtext;
42478             if (lastnode == 'SPAN') {
42479                 nopad  = true;
42480             }
42481             // text
42482             if  (currentElementChild.nodeName == '#text') {
42483                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42484                 toadd = nopadtext ? toadd : toadd.trim();
42485                 if (!nopad && toadd.length > 80) {
42486                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42487                 }
42488                 innerHTML  += toadd;
42489                 
42490                 i++;
42491                 currentElementChild = currentElement.childNodes.item(i);
42492                 lastNode = '';
42493                 continue;
42494             }
42495             allText = false;
42496             
42497             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42498                 
42499             // Recursively traverse the tree structure of the child node
42500             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42501             lastnode = currentElementChild.nodeName;
42502             i++;
42503             currentElementChild=currentElement.childNodes.item(i);
42504         }
42505         
42506         ret += innerHTML;
42507         
42508         if (!allText) {
42509                 // The remaining code is mostly for formatting the tree
42510             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42511         }
42512         
42513         
42514         if (tagName) {
42515             ret+= "</"+tagName+">";
42516         }
42517         return ret;
42518         
42519     },
42520         
42521     applyBlacklists : function()
42522     {
42523         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42524         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42525         
42526         this.white = [];
42527         this.black = [];
42528         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42529             if (b.indexOf(tag) > -1) {
42530                 return;
42531             }
42532             this.white.push(tag);
42533             
42534         }, this);
42535         
42536         Roo.each(w, function(tag) {
42537             if (b.indexOf(tag) > -1) {
42538                 return;
42539             }
42540             if (this.white.indexOf(tag) > -1) {
42541                 return;
42542             }
42543             this.white.push(tag);
42544             
42545         }, this);
42546         
42547         
42548         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42549             if (w.indexOf(tag) > -1) {
42550                 return;
42551             }
42552             this.black.push(tag);
42553             
42554         }, this);
42555         
42556         Roo.each(b, function(tag) {
42557             if (w.indexOf(tag) > -1) {
42558                 return;
42559             }
42560             if (this.black.indexOf(tag) > -1) {
42561                 return;
42562             }
42563             this.black.push(tag);
42564             
42565         }, this);
42566         
42567         
42568         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42569         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42570         
42571         this.cwhite = [];
42572         this.cblack = [];
42573         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42574             if (b.indexOf(tag) > -1) {
42575                 return;
42576             }
42577             this.cwhite.push(tag);
42578             
42579         }, this);
42580         
42581         Roo.each(w, function(tag) {
42582             if (b.indexOf(tag) > -1) {
42583                 return;
42584             }
42585             if (this.cwhite.indexOf(tag) > -1) {
42586                 return;
42587             }
42588             this.cwhite.push(tag);
42589             
42590         }, this);
42591         
42592         
42593         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42594             if (w.indexOf(tag) > -1) {
42595                 return;
42596             }
42597             this.cblack.push(tag);
42598             
42599         }, this);
42600         
42601         Roo.each(b, function(tag) {
42602             if (w.indexOf(tag) > -1) {
42603                 return;
42604             }
42605             if (this.cblack.indexOf(tag) > -1) {
42606                 return;
42607             }
42608             this.cblack.push(tag);
42609             
42610         }, this);
42611     },
42612     
42613     setStylesheets : function(stylesheets)
42614     {
42615         if(typeof(stylesheets) == 'string'){
42616             Roo.get(this.iframe.contentDocument.head).createChild({
42617                 tag : 'link',
42618                 rel : 'stylesheet',
42619                 type : 'text/css',
42620                 href : stylesheets
42621             });
42622             
42623             return;
42624         }
42625         var _this = this;
42626      
42627         Roo.each(stylesheets, function(s) {
42628             if(!s.length){
42629                 return;
42630             }
42631             
42632             Roo.get(_this.iframe.contentDocument.head).createChild({
42633                 tag : 'link',
42634                 rel : 'stylesheet',
42635                 type : 'text/css',
42636                 href : s
42637             });
42638         });
42639
42640         
42641     },
42642     
42643     removeStylesheets : function()
42644     {
42645         var _this = this;
42646         
42647         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42648             s.remove();
42649         });
42650     }
42651     
42652     // hide stuff that is not compatible
42653     /**
42654      * @event blur
42655      * @hide
42656      */
42657     /**
42658      * @event change
42659      * @hide
42660      */
42661     /**
42662      * @event focus
42663      * @hide
42664      */
42665     /**
42666      * @event specialkey
42667      * @hide
42668      */
42669     /**
42670      * @cfg {String} fieldClass @hide
42671      */
42672     /**
42673      * @cfg {String} focusClass @hide
42674      */
42675     /**
42676      * @cfg {String} autoCreate @hide
42677      */
42678     /**
42679      * @cfg {String} inputType @hide
42680      */
42681     /**
42682      * @cfg {String} invalidClass @hide
42683      */
42684     /**
42685      * @cfg {String} invalidText @hide
42686      */
42687     /**
42688      * @cfg {String} msgFx @hide
42689      */
42690     /**
42691      * @cfg {String} validateOnBlur @hide
42692      */
42693 });
42694
42695 Roo.HtmlEditorCore.white = [
42696         'area', 'br', 'img', 'input', 'hr', 'wbr',
42697         
42698        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42699        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42700        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42701        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42702        'table',   'ul',         'xmp', 
42703        
42704        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42705       'thead',   'tr', 
42706      
42707       'dir', 'menu', 'ol', 'ul', 'dl',
42708        
42709       'embed',  'object'
42710 ];
42711
42712
42713 Roo.HtmlEditorCore.black = [
42714     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42715         'applet', // 
42716         'base',   'basefont', 'bgsound', 'blink',  'body', 
42717         'frame',  'frameset', 'head',    'html',   'ilayer', 
42718         'iframe', 'layer',  'link',     'meta',    'object',   
42719         'script', 'style' ,'title',  'xml' // clean later..
42720 ];
42721 Roo.HtmlEditorCore.clean = [
42722     'script', 'style', 'title', 'xml'
42723 ];
42724 Roo.HtmlEditorCore.remove = [
42725     'font'
42726 ];
42727 // attributes..
42728
42729 Roo.HtmlEditorCore.ablack = [
42730     'on'
42731 ];
42732     
42733 Roo.HtmlEditorCore.aclean = [ 
42734     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42735 ];
42736
42737 // protocols..
42738 Roo.HtmlEditorCore.pwhite= [
42739         'http',  'https',  'mailto'
42740 ];
42741
42742 // white listed style attributes.
42743 Roo.HtmlEditorCore.cwhite= [
42744       //  'text-align', /// default is to allow most things..
42745       
42746          
42747 //        'font-size'//??
42748 ];
42749
42750 // black listed style attributes.
42751 Roo.HtmlEditorCore.cblack= [
42752       //  'font-size' -- this can be set by the project 
42753 ];
42754
42755
42756 Roo.HtmlEditorCore.swapCodes   =[ 
42757     [    8211, "--" ], 
42758     [    8212, "--" ], 
42759     [    8216,  "'" ],  
42760     [    8217, "'" ],  
42761     [    8220, '"' ],  
42762     [    8221, '"' ],  
42763     [    8226, "*" ],  
42764     [    8230, "..." ]
42765 ]; 
42766
42767     //<script type="text/javascript">
42768
42769 /*
42770  * Ext JS Library 1.1.1
42771  * Copyright(c) 2006-2007, Ext JS, LLC.
42772  * Licence LGPL
42773  * 
42774  */
42775  
42776  
42777 Roo.form.HtmlEditor = function(config){
42778     
42779     
42780     
42781     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42782     
42783     if (!this.toolbars) {
42784         this.toolbars = [];
42785     }
42786     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42787     
42788     
42789 };
42790
42791 /**
42792  * @class Roo.form.HtmlEditor
42793  * @extends Roo.form.Field
42794  * Provides a lightweight HTML Editor component.
42795  *
42796  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42797  * 
42798  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42799  * supported by this editor.</b><br/><br/>
42800  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42801  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42802  */
42803 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42804     /**
42805      * @cfg {Boolean} clearUp
42806      */
42807     clearUp : true,
42808       /**
42809      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42810      */
42811     toolbars : false,
42812    
42813      /**
42814      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42815      *                        Roo.resizable.
42816      */
42817     resizable : false,
42818      /**
42819      * @cfg {Number} height (in pixels)
42820      */   
42821     height: 300,
42822    /**
42823      * @cfg {Number} width (in pixels)
42824      */   
42825     width: 500,
42826     
42827     /**
42828      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42829      * 
42830      */
42831     stylesheets: false,
42832     
42833     
42834      /**
42835      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42836      * 
42837      */
42838     cblack: false,
42839     /**
42840      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42841      * 
42842      */
42843     cwhite: false,
42844     
42845      /**
42846      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42847      * 
42848      */
42849     black: false,
42850     /**
42851      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42852      * 
42853      */
42854     white: false,
42855     
42856     // id of frame..
42857     frameId: false,
42858     
42859     // private properties
42860     validationEvent : false,
42861     deferHeight: true,
42862     initialized : false,
42863     activated : false,
42864     
42865     onFocus : Roo.emptyFn,
42866     iframePad:3,
42867     hideMode:'offsets',
42868     
42869     actionMode : 'container', // defaults to hiding it...
42870     
42871     defaultAutoCreate : { // modified by initCompnoent..
42872         tag: "textarea",
42873         style:"width:500px;height:300px;",
42874         autocomplete: "new-password"
42875     },
42876
42877     // private
42878     initComponent : function(){
42879         this.addEvents({
42880             /**
42881              * @event initialize
42882              * Fires when the editor is fully initialized (including the iframe)
42883              * @param {HtmlEditor} this
42884              */
42885             initialize: true,
42886             /**
42887              * @event activate
42888              * Fires when the editor is first receives the focus. Any insertion must wait
42889              * until after this event.
42890              * @param {HtmlEditor} this
42891              */
42892             activate: true,
42893              /**
42894              * @event beforesync
42895              * Fires before the textarea is updated with content from the editor iframe. Return false
42896              * to cancel the sync.
42897              * @param {HtmlEditor} this
42898              * @param {String} html
42899              */
42900             beforesync: true,
42901              /**
42902              * @event beforepush
42903              * Fires before the iframe editor is updated with content from the textarea. Return false
42904              * to cancel the push.
42905              * @param {HtmlEditor} this
42906              * @param {String} html
42907              */
42908             beforepush: true,
42909              /**
42910              * @event sync
42911              * Fires when the textarea is updated with content from the editor iframe.
42912              * @param {HtmlEditor} this
42913              * @param {String} html
42914              */
42915             sync: true,
42916              /**
42917              * @event push
42918              * Fires when the iframe editor is updated with content from the textarea.
42919              * @param {HtmlEditor} this
42920              * @param {String} html
42921              */
42922             push: true,
42923              /**
42924              * @event editmodechange
42925              * Fires when the editor switches edit modes
42926              * @param {HtmlEditor} this
42927              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42928              */
42929             editmodechange: true,
42930             /**
42931              * @event editorevent
42932              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42933              * @param {HtmlEditor} this
42934              */
42935             editorevent: true,
42936             /**
42937              * @event firstfocus
42938              * Fires when on first focus - needed by toolbars..
42939              * @param {HtmlEditor} this
42940              */
42941             firstfocus: true,
42942             /**
42943              * @event autosave
42944              * Auto save the htmlEditor value as a file into Events
42945              * @param {HtmlEditor} this
42946              */
42947             autosave: true,
42948             /**
42949              * @event savedpreview
42950              * preview the saved version of htmlEditor
42951              * @param {HtmlEditor} this
42952              */
42953             savedpreview: true,
42954             
42955             /**
42956             * @event stylesheetsclick
42957             * Fires when press the Sytlesheets button
42958             * @param {Roo.HtmlEditorCore} this
42959             */
42960             stylesheetsclick: true
42961         });
42962         this.defaultAutoCreate =  {
42963             tag: "textarea",
42964             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42965             autocomplete: "new-password"
42966         };
42967     },
42968
42969     /**
42970      * Protected method that will not generally be called directly. It
42971      * is called when the editor creates its toolbar. Override this method if you need to
42972      * add custom toolbar buttons.
42973      * @param {HtmlEditor} editor
42974      */
42975     createToolbar : function(editor){
42976         Roo.log("create toolbars");
42977         if (!editor.toolbars || !editor.toolbars.length) {
42978             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42979         }
42980         
42981         for (var i =0 ; i < editor.toolbars.length;i++) {
42982             editor.toolbars[i] = Roo.factory(
42983                     typeof(editor.toolbars[i]) == 'string' ?
42984                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42985                 Roo.form.HtmlEditor);
42986             editor.toolbars[i].init(editor);
42987         }
42988          
42989         
42990     },
42991
42992      
42993     // private
42994     onRender : function(ct, position)
42995     {
42996         var _t = this;
42997         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42998         
42999         this.wrap = this.el.wrap({
43000             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
43001         });
43002         
43003         this.editorcore.onRender(ct, position);
43004          
43005         if (this.resizable) {
43006             this.resizeEl = new Roo.Resizable(this.wrap, {
43007                 pinned : true,
43008                 wrap: true,
43009                 dynamic : true,
43010                 minHeight : this.height,
43011                 height: this.height,
43012                 handles : this.resizable,
43013                 width: this.width,
43014                 listeners : {
43015                     resize : function(r, w, h) {
43016                         _t.onResize(w,h); // -something
43017                     }
43018                 }
43019             });
43020             
43021         }
43022         this.createToolbar(this);
43023        
43024         
43025         if(!this.width){
43026             this.setSize(this.wrap.getSize());
43027         }
43028         if (this.resizeEl) {
43029             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43030             // should trigger onReize..
43031         }
43032         
43033         this.keyNav = new Roo.KeyNav(this.el, {
43034             
43035             "tab" : function(e){
43036                 e.preventDefault();
43037                 
43038                 var value = this.getValue();
43039                 
43040                 var start = this.el.dom.selectionStart;
43041                 var end = this.el.dom.selectionEnd;
43042                 
43043                 if(!e.shiftKey){
43044                     
43045                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43046                     this.el.dom.setSelectionRange(end + 1, end + 1);
43047                     return;
43048                 }
43049                 
43050                 var f = value.substring(0, start).split("\t");
43051                 
43052                 if(f.pop().length != 0){
43053                     return;
43054                 }
43055                 
43056                 this.setValue(f.join("\t") + value.substring(end));
43057                 this.el.dom.setSelectionRange(start - 1, start - 1);
43058                 
43059             },
43060             
43061             "home" : function(e){
43062                 e.preventDefault();
43063                 
43064                 var curr = this.el.dom.selectionStart;
43065                 var lines = this.getValue().split("\n");
43066                 
43067                 if(!lines.length){
43068                     return;
43069                 }
43070                 
43071                 if(e.ctrlKey){
43072                     this.el.dom.setSelectionRange(0, 0);
43073                     return;
43074                 }
43075                 
43076                 var pos = 0;
43077                 
43078                 for (var i = 0; i < lines.length;i++) {
43079                     pos += lines[i].length;
43080                     
43081                     if(i != 0){
43082                         pos += 1;
43083                     }
43084                     
43085                     if(pos < curr){
43086                         continue;
43087                     }
43088                     
43089                     pos -= lines[i].length;
43090                     
43091                     break;
43092                 }
43093                 
43094                 if(!e.shiftKey){
43095                     this.el.dom.setSelectionRange(pos, pos);
43096                     return;
43097                 }
43098                 
43099                 this.el.dom.selectionStart = pos;
43100                 this.el.dom.selectionEnd = curr;
43101             },
43102             
43103             "end" : function(e){
43104                 e.preventDefault();
43105                 
43106                 var curr = this.el.dom.selectionStart;
43107                 var lines = this.getValue().split("\n");
43108                 
43109                 if(!lines.length){
43110                     return;
43111                 }
43112                 
43113                 if(e.ctrlKey){
43114                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43115                     return;
43116                 }
43117                 
43118                 var pos = 0;
43119                 
43120                 for (var i = 0; i < lines.length;i++) {
43121                     
43122                     pos += lines[i].length;
43123                     
43124                     if(i != 0){
43125                         pos += 1;
43126                     }
43127                     
43128                     if(pos < curr){
43129                         continue;
43130                     }
43131                     
43132                     break;
43133                 }
43134                 
43135                 if(!e.shiftKey){
43136                     this.el.dom.setSelectionRange(pos, pos);
43137                     return;
43138                 }
43139                 
43140                 this.el.dom.selectionStart = curr;
43141                 this.el.dom.selectionEnd = pos;
43142             },
43143
43144             scope : this,
43145
43146             doRelay : function(foo, bar, hname){
43147                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43148             },
43149
43150             forceKeyDown: true
43151         });
43152         
43153 //        if(this.autosave && this.w){
43154 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43155 //        }
43156     },
43157
43158     // private
43159     onResize : function(w, h)
43160     {
43161         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43162         var ew = false;
43163         var eh = false;
43164         
43165         if(this.el ){
43166             if(typeof w == 'number'){
43167                 var aw = w - this.wrap.getFrameWidth('lr');
43168                 this.el.setWidth(this.adjustWidth('textarea', aw));
43169                 ew = aw;
43170             }
43171             if(typeof h == 'number'){
43172                 var tbh = 0;
43173                 for (var i =0; i < this.toolbars.length;i++) {
43174                     // fixme - ask toolbars for heights?
43175                     tbh += this.toolbars[i].tb.el.getHeight();
43176                     if (this.toolbars[i].footer) {
43177                         tbh += this.toolbars[i].footer.el.getHeight();
43178                     }
43179                 }
43180                 
43181                 
43182                 
43183                 
43184                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43185                 ah -= 5; // knock a few pixes off for look..
43186 //                Roo.log(ah);
43187                 this.el.setHeight(this.adjustWidth('textarea', ah));
43188                 var eh = ah;
43189             }
43190         }
43191         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43192         this.editorcore.onResize(ew,eh);
43193         
43194     },
43195
43196     /**
43197      * Toggles the editor between standard and source edit mode.
43198      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43199      */
43200     toggleSourceEdit : function(sourceEditMode)
43201     {
43202         this.editorcore.toggleSourceEdit(sourceEditMode);
43203         
43204         if(this.editorcore.sourceEditMode){
43205             Roo.log('editor - showing textarea');
43206             
43207 //            Roo.log('in');
43208 //            Roo.log(this.syncValue());
43209             this.editorcore.syncValue();
43210             this.el.removeClass('x-hidden');
43211             this.el.dom.removeAttribute('tabIndex');
43212             this.el.focus();
43213             
43214             for (var i = 0; i < this.toolbars.length; i++) {
43215                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43216                     this.toolbars[i].tb.hide();
43217                     this.toolbars[i].footer.hide();
43218                 }
43219             }
43220             
43221         }else{
43222             Roo.log('editor - hiding textarea');
43223 //            Roo.log('out')
43224 //            Roo.log(this.pushValue()); 
43225             this.editorcore.pushValue();
43226             
43227             this.el.addClass('x-hidden');
43228             this.el.dom.setAttribute('tabIndex', -1);
43229             
43230             for (var i = 0; i < this.toolbars.length; i++) {
43231                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43232                     this.toolbars[i].tb.show();
43233                     this.toolbars[i].footer.show();
43234                 }
43235             }
43236             
43237             //this.deferFocus();
43238         }
43239         
43240         this.setSize(this.wrap.getSize());
43241         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43242         
43243         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43244     },
43245  
43246     // private (for BoxComponent)
43247     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43248
43249     // private (for BoxComponent)
43250     getResizeEl : function(){
43251         return this.wrap;
43252     },
43253
43254     // private (for BoxComponent)
43255     getPositionEl : function(){
43256         return this.wrap;
43257     },
43258
43259     // private
43260     initEvents : function(){
43261         this.originalValue = this.getValue();
43262     },
43263
43264     /**
43265      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43266      * @method
43267      */
43268     markInvalid : Roo.emptyFn,
43269     /**
43270      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43271      * @method
43272      */
43273     clearInvalid : Roo.emptyFn,
43274
43275     setValue : function(v){
43276         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43277         this.editorcore.pushValue();
43278     },
43279
43280      
43281     // private
43282     deferFocus : function(){
43283         this.focus.defer(10, this);
43284     },
43285
43286     // doc'ed in Field
43287     focus : function(){
43288         this.editorcore.focus();
43289         
43290     },
43291       
43292
43293     // private
43294     onDestroy : function(){
43295         
43296         
43297         
43298         if(this.rendered){
43299             
43300             for (var i =0; i < this.toolbars.length;i++) {
43301                 // fixme - ask toolbars for heights?
43302                 this.toolbars[i].onDestroy();
43303             }
43304             
43305             this.wrap.dom.innerHTML = '';
43306             this.wrap.remove();
43307         }
43308     },
43309
43310     // private
43311     onFirstFocus : function(){
43312         //Roo.log("onFirstFocus");
43313         this.editorcore.onFirstFocus();
43314          for (var i =0; i < this.toolbars.length;i++) {
43315             this.toolbars[i].onFirstFocus();
43316         }
43317         
43318     },
43319     
43320     // private
43321     syncValue : function()
43322     {
43323         this.editorcore.syncValue();
43324     },
43325     
43326     pushValue : function()
43327     {
43328         this.editorcore.pushValue();
43329     },
43330     
43331     setStylesheets : function(stylesheets)
43332     {
43333         this.editorcore.setStylesheets(stylesheets);
43334     },
43335     
43336     removeStylesheets : function()
43337     {
43338         this.editorcore.removeStylesheets();
43339     }
43340      
43341     
43342     // hide stuff that is not compatible
43343     /**
43344      * @event blur
43345      * @hide
43346      */
43347     /**
43348      * @event change
43349      * @hide
43350      */
43351     /**
43352      * @event focus
43353      * @hide
43354      */
43355     /**
43356      * @event specialkey
43357      * @hide
43358      */
43359     /**
43360      * @cfg {String} fieldClass @hide
43361      */
43362     /**
43363      * @cfg {String} focusClass @hide
43364      */
43365     /**
43366      * @cfg {String} autoCreate @hide
43367      */
43368     /**
43369      * @cfg {String} inputType @hide
43370      */
43371     /**
43372      * @cfg {String} invalidClass @hide
43373      */
43374     /**
43375      * @cfg {String} invalidText @hide
43376      */
43377     /**
43378      * @cfg {String} msgFx @hide
43379      */
43380     /**
43381      * @cfg {String} validateOnBlur @hide
43382      */
43383 });
43384  
43385     // <script type="text/javascript">
43386 /*
43387  * Based on
43388  * Ext JS Library 1.1.1
43389  * Copyright(c) 2006-2007, Ext JS, LLC.
43390  *  
43391  
43392  */
43393
43394 /**
43395  * @class Roo.form.HtmlEditorToolbar1
43396  * Basic Toolbar
43397  * 
43398  * Usage:
43399  *
43400  new Roo.form.HtmlEditor({
43401     ....
43402     toolbars : [
43403         new Roo.form.HtmlEditorToolbar1({
43404             disable : { fonts: 1 , format: 1, ..., ... , ...],
43405             btns : [ .... ]
43406         })
43407     }
43408      
43409  * 
43410  * @cfg {Object} disable List of elements to disable..
43411  * @cfg {Array} btns List of additional buttons.
43412  * 
43413  * 
43414  * NEEDS Extra CSS? 
43415  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43416  */
43417  
43418 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43419 {
43420     
43421     Roo.apply(this, config);
43422     
43423     // default disabled, based on 'good practice'..
43424     this.disable = this.disable || {};
43425     Roo.applyIf(this.disable, {
43426         fontSize : true,
43427         colors : true,
43428         specialElements : true
43429     });
43430     
43431     
43432     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43433     // dont call parent... till later.
43434 }
43435
43436 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43437     
43438     tb: false,
43439     
43440     rendered: false,
43441     
43442     editor : false,
43443     editorcore : false,
43444     /**
43445      * @cfg {Object} disable  List of toolbar elements to disable
43446          
43447      */
43448     disable : false,
43449     
43450     
43451      /**
43452      * @cfg {String} createLinkText The default text for the create link prompt
43453      */
43454     createLinkText : 'Please enter the URL for the link:',
43455     /**
43456      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43457      */
43458     defaultLinkValue : 'http:/'+'/',
43459    
43460     
43461       /**
43462      * @cfg {Array} fontFamilies An array of available font families
43463      */
43464     fontFamilies : [
43465         'Arial',
43466         'Courier New',
43467         'Tahoma',
43468         'Times New Roman',
43469         'Verdana'
43470     ],
43471     
43472     specialChars : [
43473            "&#169;",
43474           "&#174;",     
43475           "&#8482;",    
43476           "&#163;" ,    
43477          // "&#8212;",    
43478           "&#8230;",    
43479           "&#247;" ,    
43480         //  "&#225;" ,     ?? a acute?
43481            "&#8364;"    , //Euro
43482        //   "&#8220;"    ,
43483         //  "&#8221;"    ,
43484         //  "&#8226;"    ,
43485           "&#176;"  //   , // degrees
43486
43487          // "&#233;"     , // e ecute
43488          // "&#250;"     , // u ecute?
43489     ],
43490     
43491     specialElements : [
43492         {
43493             text: "Insert Table",
43494             xtype: 'MenuItem',
43495             xns : Roo.Menu,
43496             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43497                 
43498         },
43499         {    
43500             text: "Insert Image",
43501             xtype: 'MenuItem',
43502             xns : Roo.Menu,
43503             ihtml : '<img src="about:blank"/>'
43504             
43505         }
43506         
43507          
43508     ],
43509     
43510     
43511     inputElements : [ 
43512             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43513             "input:submit", "input:button", "select", "textarea", "label" ],
43514     formats : [
43515         ["p"] ,  
43516         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43517         ["pre"],[ "code"], 
43518         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43519         ['div'],['span']
43520     ],
43521     
43522     cleanStyles : [
43523         "font-size"
43524     ],
43525      /**
43526      * @cfg {String} defaultFont default font to use.
43527      */
43528     defaultFont: 'tahoma',
43529    
43530     fontSelect : false,
43531     
43532     
43533     formatCombo : false,
43534     
43535     init : function(editor)
43536     {
43537         this.editor = editor;
43538         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43539         var editorcore = this.editorcore;
43540         
43541         var _t = this;
43542         
43543         var fid = editorcore.frameId;
43544         var etb = this;
43545         function btn(id, toggle, handler){
43546             var xid = fid + '-'+ id ;
43547             return {
43548                 id : xid,
43549                 cmd : id,
43550                 cls : 'x-btn-icon x-edit-'+id,
43551                 enableToggle:toggle !== false,
43552                 scope: _t, // was editor...
43553                 handler:handler||_t.relayBtnCmd,
43554                 clickEvent:'mousedown',
43555                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43556                 tabIndex:-1
43557             };
43558         }
43559         
43560         
43561         
43562         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43563         this.tb = tb;
43564          // stop form submits
43565         tb.el.on('click', function(e){
43566             e.preventDefault(); // what does this do?
43567         });
43568
43569         if(!this.disable.font) { // && !Roo.isSafari){
43570             /* why no safari for fonts 
43571             editor.fontSelect = tb.el.createChild({
43572                 tag:'select',
43573                 tabIndex: -1,
43574                 cls:'x-font-select',
43575                 html: this.createFontOptions()
43576             });
43577             
43578             editor.fontSelect.on('change', function(){
43579                 var font = editor.fontSelect.dom.value;
43580                 editor.relayCmd('fontname', font);
43581                 editor.deferFocus();
43582             }, editor);
43583             
43584             tb.add(
43585                 editor.fontSelect.dom,
43586                 '-'
43587             );
43588             */
43589             
43590         };
43591         if(!this.disable.formats){
43592             this.formatCombo = new Roo.form.ComboBox({
43593                 store: new Roo.data.SimpleStore({
43594                     id : 'tag',
43595                     fields: ['tag'],
43596                     data : this.formats // from states.js
43597                 }),
43598                 blockFocus : true,
43599                 name : '',
43600                 //autoCreate : {tag: "div",  size: "20"},
43601                 displayField:'tag',
43602                 typeAhead: false,
43603                 mode: 'local',
43604                 editable : false,
43605                 triggerAction: 'all',
43606                 emptyText:'Add tag',
43607                 selectOnFocus:true,
43608                 width:135,
43609                 listeners : {
43610                     'select': function(c, r, i) {
43611                         editorcore.insertTag(r.get('tag'));
43612                         editor.focus();
43613                     }
43614                 }
43615
43616             });
43617             tb.addField(this.formatCombo);
43618             
43619         }
43620         
43621         if(!this.disable.format){
43622             tb.add(
43623                 btn('bold'),
43624                 btn('italic'),
43625                 btn('underline')
43626             );
43627         };
43628         if(!this.disable.fontSize){
43629             tb.add(
43630                 '-',
43631                 
43632                 
43633                 btn('increasefontsize', false, editorcore.adjustFont),
43634                 btn('decreasefontsize', false, editorcore.adjustFont)
43635             );
43636         };
43637         
43638         
43639         if(!this.disable.colors){
43640             tb.add(
43641                 '-', {
43642                     id:editorcore.frameId +'-forecolor',
43643                     cls:'x-btn-icon x-edit-forecolor',
43644                     clickEvent:'mousedown',
43645                     tooltip: this.buttonTips['forecolor'] || undefined,
43646                     tabIndex:-1,
43647                     menu : new Roo.menu.ColorMenu({
43648                         allowReselect: true,
43649                         focus: Roo.emptyFn,
43650                         value:'000000',
43651                         plain:true,
43652                         selectHandler: function(cp, color){
43653                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43654                             editor.deferFocus();
43655                         },
43656                         scope: editorcore,
43657                         clickEvent:'mousedown'
43658                     })
43659                 }, {
43660                     id:editorcore.frameId +'backcolor',
43661                     cls:'x-btn-icon x-edit-backcolor',
43662                     clickEvent:'mousedown',
43663                     tooltip: this.buttonTips['backcolor'] || undefined,
43664                     tabIndex:-1,
43665                     menu : new Roo.menu.ColorMenu({
43666                         focus: Roo.emptyFn,
43667                         value:'FFFFFF',
43668                         plain:true,
43669                         allowReselect: true,
43670                         selectHandler: function(cp, color){
43671                             if(Roo.isGecko){
43672                                 editorcore.execCmd('useCSS', false);
43673                                 editorcore.execCmd('hilitecolor', color);
43674                                 editorcore.execCmd('useCSS', true);
43675                                 editor.deferFocus();
43676                             }else{
43677                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43678                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43679                                 editor.deferFocus();
43680                             }
43681                         },
43682                         scope:editorcore,
43683                         clickEvent:'mousedown'
43684                     })
43685                 }
43686             );
43687         };
43688         // now add all the items...
43689         
43690
43691         if(!this.disable.alignments){
43692             tb.add(
43693                 '-',
43694                 btn('justifyleft'),
43695                 btn('justifycenter'),
43696                 btn('justifyright')
43697             );
43698         };
43699
43700         //if(!Roo.isSafari){
43701             if(!this.disable.links){
43702                 tb.add(
43703                     '-',
43704                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43705                 );
43706             };
43707
43708             if(!this.disable.lists){
43709                 tb.add(
43710                     '-',
43711                     btn('insertorderedlist'),
43712                     btn('insertunorderedlist')
43713                 );
43714             }
43715             if(!this.disable.sourceEdit){
43716                 tb.add(
43717                     '-',
43718                     btn('sourceedit', true, function(btn){
43719                         this.toggleSourceEdit(btn.pressed);
43720                     })
43721                 );
43722             }
43723         //}
43724         
43725         var smenu = { };
43726         // special menu.. - needs to be tidied up..
43727         if (!this.disable.special) {
43728             smenu = {
43729                 text: "&#169;",
43730                 cls: 'x-edit-none',
43731                 
43732                 menu : {
43733                     items : []
43734                 }
43735             };
43736             for (var i =0; i < this.specialChars.length; i++) {
43737                 smenu.menu.items.push({
43738                     
43739                     html: this.specialChars[i],
43740                     handler: function(a,b) {
43741                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43742                         //editor.insertAtCursor(a.html);
43743                         
43744                     },
43745                     tabIndex:-1
43746                 });
43747             }
43748             
43749             
43750             tb.add(smenu);
43751             
43752             
43753         }
43754         
43755         var cmenu = { };
43756         if (!this.disable.cleanStyles) {
43757             cmenu = {
43758                 cls: 'x-btn-icon x-btn-clear',
43759                 
43760                 menu : {
43761                     items : []
43762                 }
43763             };
43764             for (var i =0; i < this.cleanStyles.length; i++) {
43765                 cmenu.menu.items.push({
43766                     actiontype : this.cleanStyles[i],
43767                     html: 'Remove ' + this.cleanStyles[i],
43768                     handler: function(a,b) {
43769 //                        Roo.log(a);
43770 //                        Roo.log(b);
43771                         var c = Roo.get(editorcore.doc.body);
43772                         c.select('[style]').each(function(s) {
43773                             s.dom.style.removeProperty(a.actiontype);
43774                         });
43775                         editorcore.syncValue();
43776                     },
43777                     tabIndex:-1
43778                 });
43779             }
43780              cmenu.menu.items.push({
43781                 actiontype : 'tablewidths',
43782                 html: 'Remove Table Widths',
43783                 handler: function(a,b) {
43784                     editorcore.cleanTableWidths();
43785                     editorcore.syncValue();
43786                 },
43787                 tabIndex:-1
43788             });
43789             cmenu.menu.items.push({
43790                 actiontype : 'word',
43791                 html: 'Remove MS Word Formating',
43792                 handler: function(a,b) {
43793                     editorcore.cleanWord();
43794                     editorcore.syncValue();
43795                 },
43796                 tabIndex:-1
43797             });
43798             
43799             cmenu.menu.items.push({
43800                 actiontype : 'all',
43801                 html: 'Remove All Styles',
43802                 handler: function(a,b) {
43803                     
43804                     var c = Roo.get(editorcore.doc.body);
43805                     c.select('[style]').each(function(s) {
43806                         s.dom.removeAttribute('style');
43807                     });
43808                     editorcore.syncValue();
43809                 },
43810                 tabIndex:-1
43811             });
43812              cmenu.menu.items.push({
43813                 actiontype : 'tidy',
43814                 html: 'Tidy HTML Source',
43815                 handler: function(a,b) {
43816                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43817                     editorcore.syncValue();
43818                 },
43819                 tabIndex:-1
43820             });
43821             
43822             
43823             tb.add(cmenu);
43824         }
43825          
43826         if (!this.disable.specialElements) {
43827             var semenu = {
43828                 text: "Other;",
43829                 cls: 'x-edit-none',
43830                 menu : {
43831                     items : []
43832                 }
43833             };
43834             for (var i =0; i < this.specialElements.length; i++) {
43835                 semenu.menu.items.push(
43836                     Roo.apply({ 
43837                         handler: function(a,b) {
43838                             editor.insertAtCursor(this.ihtml);
43839                         }
43840                     }, this.specialElements[i])
43841                 );
43842                     
43843             }
43844             
43845             tb.add(semenu);
43846             
43847             
43848         }
43849          
43850         
43851         if (this.btns) {
43852             for(var i =0; i< this.btns.length;i++) {
43853                 var b = Roo.factory(this.btns[i],Roo.form);
43854                 b.cls =  'x-edit-none';
43855                 
43856                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43857                     b.cls += ' x-init-enable';
43858                 }
43859                 
43860                 b.scope = editorcore;
43861                 tb.add(b);
43862             }
43863         
43864         }
43865         
43866         
43867         
43868         // disable everything...
43869         
43870         this.tb.items.each(function(item){
43871             
43872            if(
43873                 item.id != editorcore.frameId+ '-sourceedit' && 
43874                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43875             ){
43876                 
43877                 item.disable();
43878             }
43879         });
43880         this.rendered = true;
43881         
43882         // the all the btns;
43883         editor.on('editorevent', this.updateToolbar, this);
43884         // other toolbars need to implement this..
43885         //editor.on('editmodechange', this.updateToolbar, this);
43886     },
43887     
43888     
43889     relayBtnCmd : function(btn) {
43890         this.editorcore.relayCmd(btn.cmd);
43891     },
43892     // private used internally
43893     createLink : function(){
43894         Roo.log("create link?");
43895         var url = prompt(this.createLinkText, this.defaultLinkValue);
43896         if(url && url != 'http:/'+'/'){
43897             this.editorcore.relayCmd('createlink', url);
43898         }
43899     },
43900
43901     
43902     /**
43903      * Protected method that will not generally be called directly. It triggers
43904      * a toolbar update by reading the markup state of the current selection in the editor.
43905      */
43906     updateToolbar: function(){
43907
43908         if(!this.editorcore.activated){
43909             this.editor.onFirstFocus();
43910             return;
43911         }
43912
43913         var btns = this.tb.items.map, 
43914             doc = this.editorcore.doc,
43915             frameId = this.editorcore.frameId;
43916
43917         if(!this.disable.font && !Roo.isSafari){
43918             /*
43919             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43920             if(name != this.fontSelect.dom.value){
43921                 this.fontSelect.dom.value = name;
43922             }
43923             */
43924         }
43925         if(!this.disable.format){
43926             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43927             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43928             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43929         }
43930         if(!this.disable.alignments){
43931             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43932             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43933             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43934         }
43935         if(!Roo.isSafari && !this.disable.lists){
43936             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43937             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43938         }
43939         
43940         var ans = this.editorcore.getAllAncestors();
43941         if (this.formatCombo) {
43942             
43943             
43944             var store = this.formatCombo.store;
43945             this.formatCombo.setValue("");
43946             for (var i =0; i < ans.length;i++) {
43947                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43948                     // select it..
43949                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43950                     break;
43951                 }
43952             }
43953         }
43954         
43955         
43956         
43957         // hides menus... - so this cant be on a menu...
43958         Roo.menu.MenuMgr.hideAll();
43959
43960         //this.editorsyncValue();
43961     },
43962    
43963     
43964     createFontOptions : function(){
43965         var buf = [], fs = this.fontFamilies, ff, lc;
43966         
43967         
43968         
43969         for(var i = 0, len = fs.length; i< len; i++){
43970             ff = fs[i];
43971             lc = ff.toLowerCase();
43972             buf.push(
43973                 '<option value="',lc,'" style="font-family:',ff,';"',
43974                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43975                     ff,
43976                 '</option>'
43977             );
43978         }
43979         return buf.join('');
43980     },
43981     
43982     toggleSourceEdit : function(sourceEditMode){
43983         
43984         Roo.log("toolbar toogle");
43985         if(sourceEditMode === undefined){
43986             sourceEditMode = !this.sourceEditMode;
43987         }
43988         this.sourceEditMode = sourceEditMode === true;
43989         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43990         // just toggle the button?
43991         if(btn.pressed !== this.sourceEditMode){
43992             btn.toggle(this.sourceEditMode);
43993             return;
43994         }
43995         
43996         if(sourceEditMode){
43997             Roo.log("disabling buttons");
43998             this.tb.items.each(function(item){
43999                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
44000                     item.disable();
44001                 }
44002             });
44003           
44004         }else{
44005             Roo.log("enabling buttons");
44006             if(this.editorcore.initialized){
44007                 this.tb.items.each(function(item){
44008                     item.enable();
44009                 });
44010             }
44011             
44012         }
44013         Roo.log("calling toggole on editor");
44014         // tell the editor that it's been pressed..
44015         this.editor.toggleSourceEdit(sourceEditMode);
44016        
44017     },
44018      /**
44019      * Object collection of toolbar tooltips for the buttons in the editor. The key
44020      * is the command id associated with that button and the value is a valid QuickTips object.
44021      * For example:
44022 <pre><code>
44023 {
44024     bold : {
44025         title: 'Bold (Ctrl+B)',
44026         text: 'Make the selected text bold.',
44027         cls: 'x-html-editor-tip'
44028     },
44029     italic : {
44030         title: 'Italic (Ctrl+I)',
44031         text: 'Make the selected text italic.',
44032         cls: 'x-html-editor-tip'
44033     },
44034     ...
44035 </code></pre>
44036     * @type Object
44037      */
44038     buttonTips : {
44039         bold : {
44040             title: 'Bold (Ctrl+B)',
44041             text: 'Make the selected text bold.',
44042             cls: 'x-html-editor-tip'
44043         },
44044         italic : {
44045             title: 'Italic (Ctrl+I)',
44046             text: 'Make the selected text italic.',
44047             cls: 'x-html-editor-tip'
44048         },
44049         underline : {
44050             title: 'Underline (Ctrl+U)',
44051             text: 'Underline the selected text.',
44052             cls: 'x-html-editor-tip'
44053         },
44054         increasefontsize : {
44055             title: 'Grow Text',
44056             text: 'Increase the font size.',
44057             cls: 'x-html-editor-tip'
44058         },
44059         decreasefontsize : {
44060             title: 'Shrink Text',
44061             text: 'Decrease the font size.',
44062             cls: 'x-html-editor-tip'
44063         },
44064         backcolor : {
44065             title: 'Text Highlight Color',
44066             text: 'Change the background color of the selected text.',
44067             cls: 'x-html-editor-tip'
44068         },
44069         forecolor : {
44070             title: 'Font Color',
44071             text: 'Change the color of the selected text.',
44072             cls: 'x-html-editor-tip'
44073         },
44074         justifyleft : {
44075             title: 'Align Text Left',
44076             text: 'Align text to the left.',
44077             cls: 'x-html-editor-tip'
44078         },
44079         justifycenter : {
44080             title: 'Center Text',
44081             text: 'Center text in the editor.',
44082             cls: 'x-html-editor-tip'
44083         },
44084         justifyright : {
44085             title: 'Align Text Right',
44086             text: 'Align text to the right.',
44087             cls: 'x-html-editor-tip'
44088         },
44089         insertunorderedlist : {
44090             title: 'Bullet List',
44091             text: 'Start a bulleted list.',
44092             cls: 'x-html-editor-tip'
44093         },
44094         insertorderedlist : {
44095             title: 'Numbered List',
44096             text: 'Start a numbered list.',
44097             cls: 'x-html-editor-tip'
44098         },
44099         createlink : {
44100             title: 'Hyperlink',
44101             text: 'Make the selected text a hyperlink.',
44102             cls: 'x-html-editor-tip'
44103         },
44104         sourceedit : {
44105             title: 'Source Edit',
44106             text: 'Switch to source editing mode.',
44107             cls: 'x-html-editor-tip'
44108         }
44109     },
44110     // private
44111     onDestroy : function(){
44112         if(this.rendered){
44113             
44114             this.tb.items.each(function(item){
44115                 if(item.menu){
44116                     item.menu.removeAll();
44117                     if(item.menu.el){
44118                         item.menu.el.destroy();
44119                     }
44120                 }
44121                 item.destroy();
44122             });
44123              
44124         }
44125     },
44126     onFirstFocus: function() {
44127         this.tb.items.each(function(item){
44128            item.enable();
44129         });
44130     }
44131 });
44132
44133
44134
44135
44136 // <script type="text/javascript">
44137 /*
44138  * Based on
44139  * Ext JS Library 1.1.1
44140  * Copyright(c) 2006-2007, Ext JS, LLC.
44141  *  
44142  
44143  */
44144
44145  
44146 /**
44147  * @class Roo.form.HtmlEditor.ToolbarContext
44148  * Context Toolbar
44149  * 
44150  * Usage:
44151  *
44152  new Roo.form.HtmlEditor({
44153     ....
44154     toolbars : [
44155         { xtype: 'ToolbarStandard', styles : {} }
44156         { xtype: 'ToolbarContext', disable : {} }
44157     ]
44158 })
44159
44160      
44161  * 
44162  * @config : {Object} disable List of elements to disable.. (not done yet.)
44163  * @config : {Object} styles  Map of styles available.
44164  * 
44165  */
44166
44167 Roo.form.HtmlEditor.ToolbarContext = function(config)
44168 {
44169     
44170     Roo.apply(this, config);
44171     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44172     // dont call parent... till later.
44173     this.styles = this.styles || {};
44174 }
44175
44176  
44177
44178 Roo.form.HtmlEditor.ToolbarContext.types = {
44179     'IMG' : {
44180         width : {
44181             title: "Width",
44182             width: 40
44183         },
44184         height:  {
44185             title: "Height",
44186             width: 40
44187         },
44188         align: {
44189             title: "Align",
44190             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44191             width : 80
44192             
44193         },
44194         border: {
44195             title: "Border",
44196             width: 40
44197         },
44198         alt: {
44199             title: "Alt",
44200             width: 120
44201         },
44202         src : {
44203             title: "Src",
44204             width: 220
44205         }
44206         
44207     },
44208     'A' : {
44209         name : {
44210             title: "Name",
44211             width: 50
44212         },
44213         target:  {
44214             title: "Target",
44215             width: 120
44216         },
44217         href:  {
44218             title: "Href",
44219             width: 220
44220         } // border?
44221         
44222     },
44223     'TABLE' : {
44224         rows : {
44225             title: "Rows",
44226             width: 20
44227         },
44228         cols : {
44229             title: "Cols",
44230             width: 20
44231         },
44232         width : {
44233             title: "Width",
44234             width: 40
44235         },
44236         height : {
44237             title: "Height",
44238             width: 40
44239         },
44240         border : {
44241             title: "Border",
44242             width: 20
44243         }
44244     },
44245     'TD' : {
44246         width : {
44247             title: "Width",
44248             width: 40
44249         },
44250         height : {
44251             title: "Height",
44252             width: 40
44253         },   
44254         align: {
44255             title: "Align",
44256             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44257             width: 80
44258         },
44259         valign: {
44260             title: "Valign",
44261             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44262             width: 80
44263         },
44264         colspan: {
44265             title: "Colspan",
44266             width: 20
44267             
44268         },
44269          'font-family'  : {
44270             title : "Font",
44271             style : 'fontFamily',
44272             displayField: 'display',
44273             optname : 'font-family',
44274             width: 140
44275         }
44276     },
44277     'INPUT' : {
44278         name : {
44279             title: "name",
44280             width: 120
44281         },
44282         value : {
44283             title: "Value",
44284             width: 120
44285         },
44286         width : {
44287             title: "Width",
44288             width: 40
44289         }
44290     },
44291     'LABEL' : {
44292         'for' : {
44293             title: "For",
44294             width: 120
44295         }
44296     },
44297     'TEXTAREA' : {
44298           name : {
44299             title: "name",
44300             width: 120
44301         },
44302         rows : {
44303             title: "Rows",
44304             width: 20
44305         },
44306         cols : {
44307             title: "Cols",
44308             width: 20
44309         }
44310     },
44311     'SELECT' : {
44312         name : {
44313             title: "name",
44314             width: 120
44315         },
44316         selectoptions : {
44317             title: "Options",
44318             width: 200
44319         }
44320     },
44321     
44322     // should we really allow this??
44323     // should this just be 
44324     'BODY' : {
44325         title : {
44326             title: "Title",
44327             width: 200,
44328             disabled : true
44329         }
44330     },
44331     'SPAN' : {
44332         'font-family'  : {
44333             title : "Font",
44334             style : 'fontFamily',
44335             displayField: 'display',
44336             optname : 'font-family',
44337             width: 140
44338         }
44339     },
44340     'DIV' : {
44341         'font-family'  : {
44342             title : "Font",
44343             style : 'fontFamily',
44344             displayField: 'display',
44345             optname : 'font-family',
44346             width: 140
44347         }
44348     },
44349      'P' : {
44350         'font-family'  : {
44351             title : "Font",
44352             style : 'fontFamily',
44353             displayField: 'display',
44354             optname : 'font-family',
44355             width: 140
44356         }
44357     },
44358     
44359     '*' : {
44360         // empty..
44361     }
44362
44363 };
44364
44365 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44366 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44367
44368 Roo.form.HtmlEditor.ToolbarContext.options = {
44369         'font-family'  : [ 
44370                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44371                 [ 'Courier New', 'Courier New'],
44372                 [ 'Tahoma', 'Tahoma'],
44373                 [ 'Times New Roman,serif', 'Times'],
44374                 [ 'Verdana','Verdana' ]
44375         ]
44376 };
44377
44378 // fixme - these need to be configurable..
44379  
44380
44381 Roo.form.HtmlEditor.ToolbarContext.types
44382
44383
44384 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44385     
44386     tb: false,
44387     
44388     rendered: false,
44389     
44390     editor : false,
44391     editorcore : false,
44392     /**
44393      * @cfg {Object} disable  List of toolbar elements to disable
44394          
44395      */
44396     disable : false,
44397     /**
44398      * @cfg {Object} styles List of styles 
44399      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44400      *
44401      * These must be defined in the page, so they get rendered correctly..
44402      * .headline { }
44403      * TD.underline { }
44404      * 
44405      */
44406     styles : false,
44407     
44408     options: false,
44409     
44410     toolbars : false,
44411     
44412     init : function(editor)
44413     {
44414         this.editor = editor;
44415         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44416         var editorcore = this.editorcore;
44417         
44418         var fid = editorcore.frameId;
44419         var etb = this;
44420         function btn(id, toggle, handler){
44421             var xid = fid + '-'+ id ;
44422             return {
44423                 id : xid,
44424                 cmd : id,
44425                 cls : 'x-btn-icon x-edit-'+id,
44426                 enableToggle:toggle !== false,
44427                 scope: editorcore, // was editor...
44428                 handler:handler||editorcore.relayBtnCmd,
44429                 clickEvent:'mousedown',
44430                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44431                 tabIndex:-1
44432             };
44433         }
44434         // create a new element.
44435         var wdiv = editor.wrap.createChild({
44436                 tag: 'div'
44437             }, editor.wrap.dom.firstChild.nextSibling, true);
44438         
44439         // can we do this more than once??
44440         
44441          // stop form submits
44442       
44443  
44444         // disable everything...
44445         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44446         this.toolbars = {};
44447            
44448         for (var i in  ty) {
44449           
44450             this.toolbars[i] = this.buildToolbar(ty[i],i);
44451         }
44452         this.tb = this.toolbars.BODY;
44453         this.tb.el.show();
44454         this.buildFooter();
44455         this.footer.show();
44456         editor.on('hide', function( ) { this.footer.hide() }, this);
44457         editor.on('show', function( ) { this.footer.show() }, this);
44458         
44459          
44460         this.rendered = true;
44461         
44462         // the all the btns;
44463         editor.on('editorevent', this.updateToolbar, this);
44464         // other toolbars need to implement this..
44465         //editor.on('editmodechange', this.updateToolbar, this);
44466     },
44467     
44468     
44469     
44470     /**
44471      * Protected method that will not generally be called directly. It triggers
44472      * a toolbar update by reading the markup state of the current selection in the editor.
44473      *
44474      * Note you can force an update by calling on('editorevent', scope, false)
44475      */
44476     updateToolbar: function(editor,ev,sel){
44477
44478         //Roo.log(ev);
44479         // capture mouse up - this is handy for selecting images..
44480         // perhaps should go somewhere else...
44481         if(!this.editorcore.activated){
44482              this.editor.onFirstFocus();
44483             return;
44484         }
44485         
44486         
44487         
44488         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44489         // selectNode - might want to handle IE?
44490         if (ev &&
44491             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44492             ev.target && ev.target.tagName == 'IMG') {
44493             // they have click on an image...
44494             // let's see if we can change the selection...
44495             sel = ev.target;
44496          
44497               var nodeRange = sel.ownerDocument.createRange();
44498             try {
44499                 nodeRange.selectNode(sel);
44500             } catch (e) {
44501                 nodeRange.selectNodeContents(sel);
44502             }
44503             //nodeRange.collapse(true);
44504             var s = this.editorcore.win.getSelection();
44505             s.removeAllRanges();
44506             s.addRange(nodeRange);
44507         }  
44508         
44509       
44510         var updateFooter = sel ? false : true;
44511         
44512         
44513         var ans = this.editorcore.getAllAncestors();
44514         
44515         // pick
44516         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44517         
44518         if (!sel) { 
44519             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44520             sel = sel ? sel : this.editorcore.doc.body;
44521             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44522             
44523         }
44524         // pick a menu that exists..
44525         var tn = sel.tagName.toUpperCase();
44526         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44527         
44528         tn = sel.tagName.toUpperCase();
44529         
44530         var lastSel = this.tb.selectedNode
44531         
44532         this.tb.selectedNode = sel;
44533         
44534         // if current menu does not match..
44535         
44536         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44537                 
44538             this.tb.el.hide();
44539             ///console.log("show: " + tn);
44540             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44541             this.tb.el.show();
44542             // update name
44543             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44544             
44545             
44546             // update attributes
44547             if (this.tb.fields) {
44548                 this.tb.fields.each(function(e) {
44549                     if (e.stylename) {
44550                         e.setValue(sel.style[e.stylename]);
44551                         return;
44552                     } 
44553                    e.setValue(sel.getAttribute(e.attrname));
44554                 });
44555             }
44556             
44557             var hasStyles = false;
44558             for(var i in this.styles) {
44559                 hasStyles = true;
44560                 break;
44561             }
44562             
44563             // update styles
44564             if (hasStyles) { 
44565                 var st = this.tb.fields.item(0);
44566                 
44567                 st.store.removeAll();
44568                
44569                 
44570                 var cn = sel.className.split(/\s+/);
44571                 
44572                 var avs = [];
44573                 if (this.styles['*']) {
44574                     
44575                     Roo.each(this.styles['*'], function(v) {
44576                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44577                     });
44578                 }
44579                 if (this.styles[tn]) { 
44580                     Roo.each(this.styles[tn], function(v) {
44581                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44582                     });
44583                 }
44584                 
44585                 st.store.loadData(avs);
44586                 st.collapse();
44587                 st.setValue(cn);
44588             }
44589             // flag our selected Node.
44590             this.tb.selectedNode = sel;
44591            
44592            
44593             Roo.menu.MenuMgr.hideAll();
44594
44595         }
44596         
44597         if (!updateFooter) {
44598             //this.footDisp.dom.innerHTML = ''; 
44599             return;
44600         }
44601         // update the footer
44602         //
44603         var html = '';
44604         
44605         this.footerEls = ans.reverse();
44606         Roo.each(this.footerEls, function(a,i) {
44607             if (!a) { return; }
44608             html += html.length ? ' &gt; '  :  '';
44609             
44610             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44611             
44612         });
44613        
44614         // 
44615         var sz = this.footDisp.up('td').getSize();
44616         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44617         this.footDisp.dom.style.marginLeft = '5px';
44618         
44619         this.footDisp.dom.style.overflow = 'hidden';
44620         
44621         this.footDisp.dom.innerHTML = html;
44622             
44623         //this.editorsyncValue();
44624     },
44625      
44626     
44627    
44628        
44629     // private
44630     onDestroy : function(){
44631         if(this.rendered){
44632             
44633             this.tb.items.each(function(item){
44634                 if(item.menu){
44635                     item.menu.removeAll();
44636                     if(item.menu.el){
44637                         item.menu.el.destroy();
44638                     }
44639                 }
44640                 item.destroy();
44641             });
44642              
44643         }
44644     },
44645     onFirstFocus: function() {
44646         // need to do this for all the toolbars..
44647         this.tb.items.each(function(item){
44648            item.enable();
44649         });
44650     },
44651     buildToolbar: function(tlist, nm)
44652     {
44653         var editor = this.editor;
44654         var editorcore = this.editorcore;
44655          // create a new element.
44656         var wdiv = editor.wrap.createChild({
44657                 tag: 'div'
44658             }, editor.wrap.dom.firstChild.nextSibling, true);
44659         
44660        
44661         var tb = new Roo.Toolbar(wdiv);
44662         // add the name..
44663         
44664         tb.add(nm+ ":&nbsp;");
44665         
44666         var styles = [];
44667         for(var i in this.styles) {
44668             styles.push(i);
44669         }
44670         
44671         // styles...
44672         if (styles && styles.length) {
44673             
44674             // this needs a multi-select checkbox...
44675             tb.addField( new Roo.form.ComboBox({
44676                 store: new Roo.data.SimpleStore({
44677                     id : 'val',
44678                     fields: ['val', 'selected'],
44679                     data : [] 
44680                 }),
44681                 name : '-roo-edit-className',
44682                 attrname : 'className',
44683                 displayField: 'val',
44684                 typeAhead: false,
44685                 mode: 'local',
44686                 editable : false,
44687                 triggerAction: 'all',
44688                 emptyText:'Select Style',
44689                 selectOnFocus:true,
44690                 width: 130,
44691                 listeners : {
44692                     'select': function(c, r, i) {
44693                         // initial support only for on class per el..
44694                         tb.selectedNode.className =  r ? r.get('val') : '';
44695                         editorcore.syncValue();
44696                     }
44697                 }
44698     
44699             }));
44700         }
44701         
44702         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44703         var tbops = tbc.options;
44704         
44705         for (var i in tlist) {
44706             
44707             var item = tlist[i];
44708             tb.add(item.title + ":&nbsp;");
44709             
44710             
44711             //optname == used so you can configure the options available..
44712             var opts = item.opts ? item.opts : false;
44713             if (item.optname) {
44714                 opts = tbops[item.optname];
44715            
44716             }
44717             
44718             if (opts) {
44719                 // opts == pulldown..
44720                 tb.addField( new Roo.form.ComboBox({
44721                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44722                         id : 'val',
44723                         fields: ['val', 'display'],
44724                         data : opts  
44725                     }),
44726                     name : '-roo-edit-' + i,
44727                     attrname : i,
44728                     stylename : item.style ? item.style : false,
44729                     displayField: item.displayField ? item.displayField : 'val',
44730                     valueField :  'val',
44731                     typeAhead: false,
44732                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44733                     editable : false,
44734                     triggerAction: 'all',
44735                     emptyText:'Select',
44736                     selectOnFocus:true,
44737                     width: item.width ? item.width  : 130,
44738                     listeners : {
44739                         'select': function(c, r, i) {
44740                             if (c.stylename) {
44741                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44742                                 return;
44743                             }
44744                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44745                         }
44746                     }
44747
44748                 }));
44749                 continue;
44750                     
44751                  
44752                 
44753                 tb.addField( new Roo.form.TextField({
44754                     name: i,
44755                     width: 100,
44756                     //allowBlank:false,
44757                     value: ''
44758                 }));
44759                 continue;
44760             }
44761             tb.addField( new Roo.form.TextField({
44762                 name: '-roo-edit-' + i,
44763                 attrname : i,
44764                 
44765                 width: item.width,
44766                 //allowBlank:true,
44767                 value: '',
44768                 listeners: {
44769                     'change' : function(f, nv, ov) {
44770                         tb.selectedNode.setAttribute(f.attrname, nv);
44771                     }
44772                 }
44773             }));
44774              
44775         }
44776         
44777         var _this = this;
44778         
44779         if(nm == 'BODY'){
44780             tb.addSeparator();
44781         
44782             tb.addButton( {
44783                 text: 'Stylesheets',
44784
44785                 listeners : {
44786                     click : function ()
44787                     {
44788                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44789                     }
44790                 }
44791             });
44792         }
44793         
44794         tb.addFill();
44795         tb.addButton( {
44796             text: 'Remove Tag',
44797     
44798             listeners : {
44799                 click : function ()
44800                 {
44801                     // remove
44802                     // undo does not work.
44803                      
44804                     var sn = tb.selectedNode;
44805                     
44806                     var pn = sn.parentNode;
44807                     
44808                     var stn =  sn.childNodes[0];
44809                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44810                     while (sn.childNodes.length) {
44811                         var node = sn.childNodes[0];
44812                         sn.removeChild(node);
44813                         //Roo.log(node);
44814                         pn.insertBefore(node, sn);
44815                         
44816                     }
44817                     pn.removeChild(sn);
44818                     var range = editorcore.createRange();
44819         
44820                     range.setStart(stn,0);
44821                     range.setEnd(en,0); //????
44822                     //range.selectNode(sel);
44823                     
44824                     
44825                     var selection = editorcore.getSelection();
44826                     selection.removeAllRanges();
44827                     selection.addRange(range);
44828                     
44829                     
44830                     
44831                     //_this.updateToolbar(null, null, pn);
44832                     _this.updateToolbar(null, null, null);
44833                     _this.footDisp.dom.innerHTML = ''; 
44834                 }
44835             }
44836             
44837                     
44838                 
44839             
44840         });
44841         
44842         
44843         tb.el.on('click', function(e){
44844             e.preventDefault(); // what does this do?
44845         });
44846         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44847         tb.el.hide();
44848         tb.name = nm;
44849         // dont need to disable them... as they will get hidden
44850         return tb;
44851          
44852         
44853     },
44854     buildFooter : function()
44855     {
44856         
44857         var fel = this.editor.wrap.createChild();
44858         this.footer = new Roo.Toolbar(fel);
44859         // toolbar has scrolly on left / right?
44860         var footDisp= new Roo.Toolbar.Fill();
44861         var _t = this;
44862         this.footer.add(
44863             {
44864                 text : '&lt;',
44865                 xtype: 'Button',
44866                 handler : function() {
44867                     _t.footDisp.scrollTo('left',0,true)
44868                 }
44869             }
44870         );
44871         this.footer.add( footDisp );
44872         this.footer.add( 
44873             {
44874                 text : '&gt;',
44875                 xtype: 'Button',
44876                 handler : function() {
44877                     // no animation..
44878                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44879                 }
44880             }
44881         );
44882         var fel = Roo.get(footDisp.el);
44883         fel.addClass('x-editor-context');
44884         this.footDispWrap = fel; 
44885         this.footDispWrap.overflow  = 'hidden';
44886         
44887         this.footDisp = fel.createChild();
44888         this.footDispWrap.on('click', this.onContextClick, this)
44889         
44890         
44891     },
44892     onContextClick : function (ev,dom)
44893     {
44894         ev.preventDefault();
44895         var  cn = dom.className;
44896         //Roo.log(cn);
44897         if (!cn.match(/x-ed-loc-/)) {
44898             return;
44899         }
44900         var n = cn.split('-').pop();
44901         var ans = this.footerEls;
44902         var sel = ans[n];
44903         
44904          // pick
44905         var range = this.editorcore.createRange();
44906         
44907         range.selectNodeContents(sel);
44908         //range.selectNode(sel);
44909         
44910         
44911         var selection = this.editorcore.getSelection();
44912         selection.removeAllRanges();
44913         selection.addRange(range);
44914         
44915         
44916         
44917         this.updateToolbar(null, null, sel);
44918         
44919         
44920     }
44921     
44922     
44923     
44924     
44925     
44926 });
44927
44928
44929
44930
44931
44932 /*
44933  * Based on:
44934  * Ext JS Library 1.1.1
44935  * Copyright(c) 2006-2007, Ext JS, LLC.
44936  *
44937  * Originally Released Under LGPL - original licence link has changed is not relivant.
44938  *
44939  * Fork - LGPL
44940  * <script type="text/javascript">
44941  */
44942  
44943 /**
44944  * @class Roo.form.BasicForm
44945  * @extends Roo.util.Observable
44946  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44947  * @constructor
44948  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44949  * @param {Object} config Configuration options
44950  */
44951 Roo.form.BasicForm = function(el, config){
44952     this.allItems = [];
44953     this.childForms = [];
44954     Roo.apply(this, config);
44955     /*
44956      * The Roo.form.Field items in this form.
44957      * @type MixedCollection
44958      */
44959      
44960      
44961     this.items = new Roo.util.MixedCollection(false, function(o){
44962         return o.id || (o.id = Roo.id());
44963     });
44964     this.addEvents({
44965         /**
44966          * @event beforeaction
44967          * Fires before any action is performed. Return false to cancel the action.
44968          * @param {Form} this
44969          * @param {Action} action The action to be performed
44970          */
44971         beforeaction: true,
44972         /**
44973          * @event actionfailed
44974          * Fires when an action fails.
44975          * @param {Form} this
44976          * @param {Action} action The action that failed
44977          */
44978         actionfailed : true,
44979         /**
44980          * @event actioncomplete
44981          * Fires when an action is completed.
44982          * @param {Form} this
44983          * @param {Action} action The action that completed
44984          */
44985         actioncomplete : true
44986     });
44987     if(el){
44988         this.initEl(el);
44989     }
44990     Roo.form.BasicForm.superclass.constructor.call(this);
44991 };
44992
44993 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44994     /**
44995      * @cfg {String} method
44996      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44997      */
44998     /**
44999      * @cfg {DataReader} reader
45000      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
45001      * This is optional as there is built-in support for processing JSON.
45002      */
45003     /**
45004      * @cfg {DataReader} errorReader
45005      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45006      * This is completely optional as there is built-in support for processing JSON.
45007      */
45008     /**
45009      * @cfg {String} url
45010      * The URL to use for form actions if one isn't supplied in the action options.
45011      */
45012     /**
45013      * @cfg {Boolean} fileUpload
45014      * Set to true if this form is a file upload.
45015      */
45016      
45017     /**
45018      * @cfg {Object} baseParams
45019      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45020      */
45021      /**
45022      
45023     /**
45024      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45025      */
45026     timeout: 30,
45027
45028     // private
45029     activeAction : null,
45030
45031     /**
45032      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45033      * or setValues() data instead of when the form was first created.
45034      */
45035     trackResetOnLoad : false,
45036     
45037     
45038     /**
45039      * childForms - used for multi-tab forms
45040      * @type {Array}
45041      */
45042     childForms : false,
45043     
45044     /**
45045      * allItems - full list of fields.
45046      * @type {Array}
45047      */
45048     allItems : false,
45049     
45050     /**
45051      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45052      * element by passing it or its id or mask the form itself by passing in true.
45053      * @type Mixed
45054      */
45055     waitMsgTarget : false,
45056
45057     // private
45058     initEl : function(el){
45059         this.el = Roo.get(el);
45060         this.id = this.el.id || Roo.id();
45061         this.el.on('submit', this.onSubmit, this);
45062         this.el.addClass('x-form');
45063     },
45064
45065     // private
45066     onSubmit : function(e){
45067         e.stopEvent();
45068     },
45069
45070     /**
45071      * Returns true if client-side validation on the form is successful.
45072      * @return Boolean
45073      */
45074     isValid : function(){
45075         var valid = true;
45076         this.items.each(function(f){
45077            if(!f.validate()){
45078                valid = false;
45079            }
45080         });
45081         return valid;
45082     },
45083
45084     /**
45085      * Returns true if any fields in this form have changed since their original load.
45086      * @return Boolean
45087      */
45088     isDirty : function(){
45089         var dirty = false;
45090         this.items.each(function(f){
45091            if(f.isDirty()){
45092                dirty = true;
45093                return false;
45094            }
45095         });
45096         return dirty;
45097     },
45098
45099     /**
45100      * Performs a predefined action (submit or load) or custom actions you define on this form.
45101      * @param {String} actionName The name of the action type
45102      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45103      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45104      * accept other config options):
45105      * <pre>
45106 Property          Type             Description
45107 ----------------  ---------------  ----------------------------------------------------------------------------------
45108 url               String           The url for the action (defaults to the form's url)
45109 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45110 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45111 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45112                                    validate the form on the client (defaults to false)
45113      * </pre>
45114      * @return {BasicForm} this
45115      */
45116     doAction : function(action, options){
45117         if(typeof action == 'string'){
45118             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45119         }
45120         if(this.fireEvent('beforeaction', this, action) !== false){
45121             this.beforeAction(action);
45122             action.run.defer(100, action);
45123         }
45124         return this;
45125     },
45126
45127     /**
45128      * Shortcut to do a submit action.
45129      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45130      * @return {BasicForm} this
45131      */
45132     submit : function(options){
45133         this.doAction('submit', options);
45134         return this;
45135     },
45136
45137     /**
45138      * Shortcut to do a load action.
45139      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45140      * @return {BasicForm} this
45141      */
45142     load : function(options){
45143         this.doAction('load', options);
45144         return this;
45145     },
45146
45147     /**
45148      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45149      * @param {Record} record The record to edit
45150      * @return {BasicForm} this
45151      */
45152     updateRecord : function(record){
45153         record.beginEdit();
45154         var fs = record.fields;
45155         fs.each(function(f){
45156             var field = this.findField(f.name);
45157             if(field){
45158                 record.set(f.name, field.getValue());
45159             }
45160         }, this);
45161         record.endEdit();
45162         return this;
45163     },
45164
45165     /**
45166      * Loads an Roo.data.Record into this form.
45167      * @param {Record} record The record to load
45168      * @return {BasicForm} this
45169      */
45170     loadRecord : function(record){
45171         this.setValues(record.data);
45172         return this;
45173     },
45174
45175     // private
45176     beforeAction : function(action){
45177         var o = action.options;
45178         
45179        
45180         if(this.waitMsgTarget === true){
45181             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45182         }else if(this.waitMsgTarget){
45183             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45184             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45185         }else {
45186             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45187         }
45188          
45189     },
45190
45191     // private
45192     afterAction : function(action, success){
45193         this.activeAction = null;
45194         var o = action.options;
45195         
45196         if(this.waitMsgTarget === true){
45197             this.el.unmask();
45198         }else if(this.waitMsgTarget){
45199             this.waitMsgTarget.unmask();
45200         }else{
45201             Roo.MessageBox.updateProgress(1);
45202             Roo.MessageBox.hide();
45203         }
45204          
45205         if(success){
45206             if(o.reset){
45207                 this.reset();
45208             }
45209             Roo.callback(o.success, o.scope, [this, action]);
45210             this.fireEvent('actioncomplete', this, action);
45211             
45212         }else{
45213             
45214             // failure condition..
45215             // we have a scenario where updates need confirming.
45216             // eg. if a locking scenario exists..
45217             // we look for { errors : { needs_confirm : true }} in the response.
45218             if (
45219                 (typeof(action.result) != 'undefined')  &&
45220                 (typeof(action.result.errors) != 'undefined')  &&
45221                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45222            ){
45223                 var _t = this;
45224                 Roo.MessageBox.confirm(
45225                     "Change requires confirmation",
45226                     action.result.errorMsg,
45227                     function(r) {
45228                         if (r != 'yes') {
45229                             return;
45230                         }
45231                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45232                     }
45233                     
45234                 );
45235                 
45236                 
45237                 
45238                 return;
45239             }
45240             
45241             Roo.callback(o.failure, o.scope, [this, action]);
45242             // show an error message if no failed handler is set..
45243             if (!this.hasListener('actionfailed')) {
45244                 Roo.MessageBox.alert("Error",
45245                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45246                         action.result.errorMsg :
45247                         "Saving Failed, please check your entries or try again"
45248                 );
45249             }
45250             
45251             this.fireEvent('actionfailed', this, action);
45252         }
45253         
45254     },
45255
45256     /**
45257      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45258      * @param {String} id The value to search for
45259      * @return Field
45260      */
45261     findField : function(id){
45262         var field = this.items.get(id);
45263         if(!field){
45264             this.items.each(function(f){
45265                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45266                     field = f;
45267                     return false;
45268                 }
45269             });
45270         }
45271         return field || null;
45272     },
45273
45274     /**
45275      * Add a secondary form to this one, 
45276      * Used to provide tabbed forms. One form is primary, with hidden values 
45277      * which mirror the elements from the other forms.
45278      * 
45279      * @param {Roo.form.Form} form to add.
45280      * 
45281      */
45282     addForm : function(form)
45283     {
45284        
45285         if (this.childForms.indexOf(form) > -1) {
45286             // already added..
45287             return;
45288         }
45289         this.childForms.push(form);
45290         var n = '';
45291         Roo.each(form.allItems, function (fe) {
45292             
45293             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45294             if (this.findField(n)) { // already added..
45295                 return;
45296             }
45297             var add = new Roo.form.Hidden({
45298                 name : n
45299             });
45300             add.render(this.el);
45301             
45302             this.add( add );
45303         }, this);
45304         
45305     },
45306     /**
45307      * Mark fields in this form invalid in bulk.
45308      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45309      * @return {BasicForm} this
45310      */
45311     markInvalid : function(errors){
45312         if(errors instanceof Array){
45313             for(var i = 0, len = errors.length; i < len; i++){
45314                 var fieldError = errors[i];
45315                 var f = this.findField(fieldError.id);
45316                 if(f){
45317                     f.markInvalid(fieldError.msg);
45318                 }
45319             }
45320         }else{
45321             var field, id;
45322             for(id in errors){
45323                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45324                     field.markInvalid(errors[id]);
45325                 }
45326             }
45327         }
45328         Roo.each(this.childForms || [], function (f) {
45329             f.markInvalid(errors);
45330         });
45331         
45332         return this;
45333     },
45334
45335     /**
45336      * Set values for fields in this form in bulk.
45337      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45338      * @return {BasicForm} this
45339      */
45340     setValues : function(values){
45341         if(values instanceof Array){ // array of objects
45342             for(var i = 0, len = values.length; i < len; i++){
45343                 var v = values[i];
45344                 var f = this.findField(v.id);
45345                 if(f){
45346                     f.setValue(v.value);
45347                     if(this.trackResetOnLoad){
45348                         f.originalValue = f.getValue();
45349                     }
45350                 }
45351             }
45352         }else{ // object hash
45353             var field, id;
45354             for(id in values){
45355                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45356                     
45357                     if (field.setFromData && 
45358                         field.valueField && 
45359                         field.displayField &&
45360                         // combos' with local stores can 
45361                         // be queried via setValue()
45362                         // to set their value..
45363                         (field.store && !field.store.isLocal)
45364                         ) {
45365                         // it's a combo
45366                         var sd = { };
45367                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45368                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45369                         field.setFromData(sd);
45370                         
45371                     } else {
45372                         field.setValue(values[id]);
45373                     }
45374                     
45375                     
45376                     if(this.trackResetOnLoad){
45377                         field.originalValue = field.getValue();
45378                     }
45379                 }
45380             }
45381         }
45382          
45383         Roo.each(this.childForms || [], function (f) {
45384             f.setValues(values);
45385         });
45386                 
45387         return this;
45388     },
45389
45390     /**
45391      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45392      * they are returned as an array.
45393      * @param {Boolean} asString
45394      * @return {Object}
45395      */
45396     getValues : function(asString){
45397         if (this.childForms) {
45398             // copy values from the child forms
45399             Roo.each(this.childForms, function (f) {
45400                 this.setValues(f.getValues());
45401             }, this);
45402         }
45403         
45404         
45405         
45406         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45407         if(asString === true){
45408             return fs;
45409         }
45410         return Roo.urlDecode(fs);
45411     },
45412     
45413     /**
45414      * Returns the fields in this form as an object with key/value pairs. 
45415      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45416      * @return {Object}
45417      */
45418     getFieldValues : function(with_hidden)
45419     {
45420         if (this.childForms) {
45421             // copy values from the child forms
45422             // should this call getFieldValues - probably not as we do not currently copy
45423             // hidden fields when we generate..
45424             Roo.each(this.childForms, function (f) {
45425                 this.setValues(f.getValues());
45426             }, this);
45427         }
45428         
45429         var ret = {};
45430         this.items.each(function(f){
45431             if (!f.getName()) {
45432                 return;
45433             }
45434             var v = f.getValue();
45435             if (f.inputType =='radio') {
45436                 if (typeof(ret[f.getName()]) == 'undefined') {
45437                     ret[f.getName()] = ''; // empty..
45438                 }
45439                 
45440                 if (!f.el.dom.checked) {
45441                     return;
45442                     
45443                 }
45444                 v = f.el.dom.value;
45445                 
45446             }
45447             
45448             // not sure if this supported any more..
45449             if ((typeof(v) == 'object') && f.getRawValue) {
45450                 v = f.getRawValue() ; // dates..
45451             }
45452             // combo boxes where name != hiddenName...
45453             if (f.name != f.getName()) {
45454                 ret[f.name] = f.getRawValue();
45455             }
45456             ret[f.getName()] = v;
45457         });
45458         
45459         return ret;
45460     },
45461
45462     /**
45463      * Clears all invalid messages in this form.
45464      * @return {BasicForm} this
45465      */
45466     clearInvalid : function(){
45467         this.items.each(function(f){
45468            f.clearInvalid();
45469         });
45470         
45471         Roo.each(this.childForms || [], function (f) {
45472             f.clearInvalid();
45473         });
45474         
45475         
45476         return this;
45477     },
45478
45479     /**
45480      * Resets this form.
45481      * @return {BasicForm} this
45482      */
45483     reset : function(){
45484         this.items.each(function(f){
45485             f.reset();
45486         });
45487         
45488         Roo.each(this.childForms || [], function (f) {
45489             f.reset();
45490         });
45491        
45492         
45493         return this;
45494     },
45495
45496     /**
45497      * Add Roo.form components to this form.
45498      * @param {Field} field1
45499      * @param {Field} field2 (optional)
45500      * @param {Field} etc (optional)
45501      * @return {BasicForm} this
45502      */
45503     add : function(){
45504         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45505         return this;
45506     },
45507
45508
45509     /**
45510      * Removes a field from the items collection (does NOT remove its markup).
45511      * @param {Field} field
45512      * @return {BasicForm} this
45513      */
45514     remove : function(field){
45515         this.items.remove(field);
45516         return this;
45517     },
45518
45519     /**
45520      * Looks at the fields in this form, checks them for an id attribute,
45521      * and calls applyTo on the existing dom element with that id.
45522      * @return {BasicForm} this
45523      */
45524     render : function(){
45525         this.items.each(function(f){
45526             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45527                 f.applyTo(f.id);
45528             }
45529         });
45530         return this;
45531     },
45532
45533     /**
45534      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45535      * @param {Object} values
45536      * @return {BasicForm} this
45537      */
45538     applyToFields : function(o){
45539         this.items.each(function(f){
45540            Roo.apply(f, o);
45541         });
45542         return this;
45543     },
45544
45545     /**
45546      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45547      * @param {Object} values
45548      * @return {BasicForm} this
45549      */
45550     applyIfToFields : function(o){
45551         this.items.each(function(f){
45552            Roo.applyIf(f, o);
45553         });
45554         return this;
45555     }
45556 });
45557
45558 // back compat
45559 Roo.BasicForm = Roo.form.BasicForm;/*
45560  * Based on:
45561  * Ext JS Library 1.1.1
45562  * Copyright(c) 2006-2007, Ext JS, LLC.
45563  *
45564  * Originally Released Under LGPL - original licence link has changed is not relivant.
45565  *
45566  * Fork - LGPL
45567  * <script type="text/javascript">
45568  */
45569
45570 /**
45571  * @class Roo.form.Form
45572  * @extends Roo.form.BasicForm
45573  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45574  * @constructor
45575  * @param {Object} config Configuration options
45576  */
45577 Roo.form.Form = function(config){
45578     var xitems =  [];
45579     if (config.items) {
45580         xitems = config.items;
45581         delete config.items;
45582     }
45583    
45584     
45585     Roo.form.Form.superclass.constructor.call(this, null, config);
45586     this.url = this.url || this.action;
45587     if(!this.root){
45588         this.root = new Roo.form.Layout(Roo.applyIf({
45589             id: Roo.id()
45590         }, config));
45591     }
45592     this.active = this.root;
45593     /**
45594      * Array of all the buttons that have been added to this form via {@link addButton}
45595      * @type Array
45596      */
45597     this.buttons = [];
45598     this.allItems = [];
45599     this.addEvents({
45600         /**
45601          * @event clientvalidation
45602          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45603          * @param {Form} this
45604          * @param {Boolean} valid true if the form has passed client-side validation
45605          */
45606         clientvalidation: true,
45607         /**
45608          * @event rendered
45609          * Fires when the form is rendered
45610          * @param {Roo.form.Form} form
45611          */
45612         rendered : true
45613     });
45614     
45615     if (this.progressUrl) {
45616             // push a hidden field onto the list of fields..
45617             this.addxtype( {
45618                     xns: Roo.form, 
45619                     xtype : 'Hidden', 
45620                     name : 'UPLOAD_IDENTIFIER' 
45621             });
45622         }
45623         
45624     
45625     Roo.each(xitems, this.addxtype, this);
45626     
45627     
45628     
45629 };
45630
45631 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45632     /**
45633      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45634      */
45635     /**
45636      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45637      */
45638     /**
45639      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45640      */
45641     buttonAlign:'center',
45642
45643     /**
45644      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45645      */
45646     minButtonWidth:75,
45647
45648     /**
45649      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45650      * This property cascades to child containers if not set.
45651      */
45652     labelAlign:'left',
45653
45654     /**
45655      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45656      * fires a looping event with that state. This is required to bind buttons to the valid
45657      * state using the config value formBind:true on the button.
45658      */
45659     monitorValid : false,
45660
45661     /**
45662      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45663      */
45664     monitorPoll : 200,
45665     
45666     /**
45667      * @cfg {String} progressUrl - Url to return progress data 
45668      */
45669     
45670     progressUrl : false,
45671   
45672     /**
45673      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45674      * fields are added and the column is closed. If no fields are passed the column remains open
45675      * until end() is called.
45676      * @param {Object} config The config to pass to the column
45677      * @param {Field} field1 (optional)
45678      * @param {Field} field2 (optional)
45679      * @param {Field} etc (optional)
45680      * @return Column The column container object
45681      */
45682     column : function(c){
45683         var col = new Roo.form.Column(c);
45684         this.start(col);
45685         if(arguments.length > 1){ // duplicate code required because of Opera
45686             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45687             this.end();
45688         }
45689         return col;
45690     },
45691
45692     /**
45693      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45694      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45695      * until end() is called.
45696      * @param {Object} config The config to pass to the fieldset
45697      * @param {Field} field1 (optional)
45698      * @param {Field} field2 (optional)
45699      * @param {Field} etc (optional)
45700      * @return FieldSet The fieldset container object
45701      */
45702     fieldset : function(c){
45703         var fs = new Roo.form.FieldSet(c);
45704         this.start(fs);
45705         if(arguments.length > 1){ // duplicate code required because of Opera
45706             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45707             this.end();
45708         }
45709         return fs;
45710     },
45711
45712     /**
45713      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45714      * fields are added and the container is closed. If no fields are passed the container remains open
45715      * until end() is called.
45716      * @param {Object} config The config to pass to the Layout
45717      * @param {Field} field1 (optional)
45718      * @param {Field} field2 (optional)
45719      * @param {Field} etc (optional)
45720      * @return Layout The container object
45721      */
45722     container : function(c){
45723         var l = new Roo.form.Layout(c);
45724         this.start(l);
45725         if(arguments.length > 1){ // duplicate code required because of Opera
45726             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45727             this.end();
45728         }
45729         return l;
45730     },
45731
45732     /**
45733      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45734      * @param {Object} container A Roo.form.Layout or subclass of Layout
45735      * @return {Form} this
45736      */
45737     start : function(c){
45738         // cascade label info
45739         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45740         this.active.stack.push(c);
45741         c.ownerCt = this.active;
45742         this.active = c;
45743         return this;
45744     },
45745
45746     /**
45747      * Closes the current open container
45748      * @return {Form} this
45749      */
45750     end : function(){
45751         if(this.active == this.root){
45752             return this;
45753         }
45754         this.active = this.active.ownerCt;
45755         return this;
45756     },
45757
45758     /**
45759      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45760      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45761      * as the label of the field.
45762      * @param {Field} field1
45763      * @param {Field} field2 (optional)
45764      * @param {Field} etc. (optional)
45765      * @return {Form} this
45766      */
45767     add : function(){
45768         this.active.stack.push.apply(this.active.stack, arguments);
45769         this.allItems.push.apply(this.allItems,arguments);
45770         var r = [];
45771         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45772             if(a[i].isFormField){
45773                 r.push(a[i]);
45774             }
45775         }
45776         if(r.length > 0){
45777             Roo.form.Form.superclass.add.apply(this, r);
45778         }
45779         return this;
45780     },
45781     
45782
45783     
45784     
45785     
45786      /**
45787      * Find any element that has been added to a form, using it's ID or name
45788      * This can include framesets, columns etc. along with regular fields..
45789      * @param {String} id - id or name to find.
45790      
45791      * @return {Element} e - or false if nothing found.
45792      */
45793     findbyId : function(id)
45794     {
45795         var ret = false;
45796         if (!id) {
45797             return ret;
45798         }
45799         Roo.each(this.allItems, function(f){
45800             if (f.id == id || f.name == id ){
45801                 ret = f;
45802                 return false;
45803             }
45804         });
45805         return ret;
45806     },
45807
45808     
45809     
45810     /**
45811      * Render this form into the passed container. This should only be called once!
45812      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45813      * @return {Form} this
45814      */
45815     render : function(ct)
45816     {
45817         
45818         
45819         
45820         ct = Roo.get(ct);
45821         var o = this.autoCreate || {
45822             tag: 'form',
45823             method : this.method || 'POST',
45824             id : this.id || Roo.id()
45825         };
45826         this.initEl(ct.createChild(o));
45827
45828         this.root.render(this.el);
45829         
45830        
45831              
45832         this.items.each(function(f){
45833             f.render('x-form-el-'+f.id);
45834         });
45835
45836         if(this.buttons.length > 0){
45837             // tables are required to maintain order and for correct IE layout
45838             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45839                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45840                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45841             }}, null, true);
45842             var tr = tb.getElementsByTagName('tr')[0];
45843             for(var i = 0, len = this.buttons.length; i < len; i++) {
45844                 var b = this.buttons[i];
45845                 var td = document.createElement('td');
45846                 td.className = 'x-form-btn-td';
45847                 b.render(tr.appendChild(td));
45848             }
45849         }
45850         if(this.monitorValid){ // initialize after render
45851             this.startMonitoring();
45852         }
45853         this.fireEvent('rendered', this);
45854         return this;
45855     },
45856
45857     /**
45858      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45859      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45860      * object or a valid Roo.DomHelper element config
45861      * @param {Function} handler The function called when the button is clicked
45862      * @param {Object} scope (optional) The scope of the handler function
45863      * @return {Roo.Button}
45864      */
45865     addButton : function(config, handler, scope){
45866         var bc = {
45867             handler: handler,
45868             scope: scope,
45869             minWidth: this.minButtonWidth,
45870             hideParent:true
45871         };
45872         if(typeof config == "string"){
45873             bc.text = config;
45874         }else{
45875             Roo.apply(bc, config);
45876         }
45877         var btn = new Roo.Button(null, bc);
45878         this.buttons.push(btn);
45879         return btn;
45880     },
45881
45882      /**
45883      * Adds a series of form elements (using the xtype property as the factory method.
45884      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45885      * @param {Object} config 
45886      */
45887     
45888     addxtype : function()
45889     {
45890         var ar = Array.prototype.slice.call(arguments, 0);
45891         var ret = false;
45892         for(var i = 0; i < ar.length; i++) {
45893             if (!ar[i]) {
45894                 continue; // skip -- if this happends something invalid got sent, we 
45895                 // should ignore it, as basically that interface element will not show up
45896                 // and that should be pretty obvious!!
45897             }
45898             
45899             if (Roo.form[ar[i].xtype]) {
45900                 ar[i].form = this;
45901                 var fe = Roo.factory(ar[i], Roo.form);
45902                 if (!ret) {
45903                     ret = fe;
45904                 }
45905                 fe.form = this;
45906                 if (fe.store) {
45907                     fe.store.form = this;
45908                 }
45909                 if (fe.isLayout) {  
45910                          
45911                     this.start(fe);
45912                     this.allItems.push(fe);
45913                     if (fe.items && fe.addxtype) {
45914                         fe.addxtype.apply(fe, fe.items);
45915                         delete fe.items;
45916                     }
45917                      this.end();
45918                     continue;
45919                 }
45920                 
45921                 
45922                  
45923                 this.add(fe);
45924               //  console.log('adding ' + ar[i].xtype);
45925             }
45926             if (ar[i].xtype == 'Button') {  
45927                 //console.log('adding button');
45928                 //console.log(ar[i]);
45929                 this.addButton(ar[i]);
45930                 this.allItems.push(fe);
45931                 continue;
45932             }
45933             
45934             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45935                 alert('end is not supported on xtype any more, use items');
45936             //    this.end();
45937             //    //console.log('adding end');
45938             }
45939             
45940         }
45941         return ret;
45942     },
45943     
45944     /**
45945      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45946      * option "monitorValid"
45947      */
45948     startMonitoring : function(){
45949         if(!this.bound){
45950             this.bound = true;
45951             Roo.TaskMgr.start({
45952                 run : this.bindHandler,
45953                 interval : this.monitorPoll || 200,
45954                 scope: this
45955             });
45956         }
45957     },
45958
45959     /**
45960      * Stops monitoring of the valid state of this form
45961      */
45962     stopMonitoring : function(){
45963         this.bound = false;
45964     },
45965
45966     // private
45967     bindHandler : function(){
45968         if(!this.bound){
45969             return false; // stops binding
45970         }
45971         var valid = true;
45972         this.items.each(function(f){
45973             if(!f.isValid(true)){
45974                 valid = false;
45975                 return false;
45976             }
45977         });
45978         for(var i = 0, len = this.buttons.length; i < len; i++){
45979             var btn = this.buttons[i];
45980             if(btn.formBind === true && btn.disabled === valid){
45981                 btn.setDisabled(!valid);
45982             }
45983         }
45984         this.fireEvent('clientvalidation', this, valid);
45985     }
45986     
45987     
45988     
45989     
45990     
45991     
45992     
45993     
45994 });
45995
45996
45997 // back compat
45998 Roo.Form = Roo.form.Form;
45999 /*
46000  * Based on:
46001  * Ext JS Library 1.1.1
46002  * Copyright(c) 2006-2007, Ext JS, LLC.
46003  *
46004  * Originally Released Under LGPL - original licence link has changed is not relivant.
46005  *
46006  * Fork - LGPL
46007  * <script type="text/javascript">
46008  */
46009
46010 // as we use this in bootstrap.
46011 Roo.namespace('Roo.form');
46012  /**
46013  * @class Roo.form.Action
46014  * Internal Class used to handle form actions
46015  * @constructor
46016  * @param {Roo.form.BasicForm} el The form element or its id
46017  * @param {Object} config Configuration options
46018  */
46019
46020  
46021  
46022 // define the action interface
46023 Roo.form.Action = function(form, options){
46024     this.form = form;
46025     this.options = options || {};
46026 };
46027 /**
46028  * Client Validation Failed
46029  * @const 
46030  */
46031 Roo.form.Action.CLIENT_INVALID = 'client';
46032 /**
46033  * Server Validation Failed
46034  * @const 
46035  */
46036 Roo.form.Action.SERVER_INVALID = 'server';
46037  /**
46038  * Connect to Server Failed
46039  * @const 
46040  */
46041 Roo.form.Action.CONNECT_FAILURE = 'connect';
46042 /**
46043  * Reading Data from Server Failed
46044  * @const 
46045  */
46046 Roo.form.Action.LOAD_FAILURE = 'load';
46047
46048 Roo.form.Action.prototype = {
46049     type : 'default',
46050     failureType : undefined,
46051     response : undefined,
46052     result : undefined,
46053
46054     // interface method
46055     run : function(options){
46056
46057     },
46058
46059     // interface method
46060     success : function(response){
46061
46062     },
46063
46064     // interface method
46065     handleResponse : function(response){
46066
46067     },
46068
46069     // default connection failure
46070     failure : function(response){
46071         
46072         this.response = response;
46073         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46074         this.form.afterAction(this, false);
46075     },
46076
46077     processResponse : function(response){
46078         this.response = response;
46079         if(!response.responseText){
46080             return true;
46081         }
46082         this.result = this.handleResponse(response);
46083         return this.result;
46084     },
46085
46086     // utility functions used internally
46087     getUrl : function(appendParams){
46088         var url = this.options.url || this.form.url || this.form.el.dom.action;
46089         if(appendParams){
46090             var p = this.getParams();
46091             if(p){
46092                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46093             }
46094         }
46095         return url;
46096     },
46097
46098     getMethod : function(){
46099         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46100     },
46101
46102     getParams : function(){
46103         var bp = this.form.baseParams;
46104         var p = this.options.params;
46105         if(p){
46106             if(typeof p == "object"){
46107                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46108             }else if(typeof p == 'string' && bp){
46109                 p += '&' + Roo.urlEncode(bp);
46110             }
46111         }else if(bp){
46112             p = Roo.urlEncode(bp);
46113         }
46114         return p;
46115     },
46116
46117     createCallback : function(){
46118         return {
46119             success: this.success,
46120             failure: this.failure,
46121             scope: this,
46122             timeout: (this.form.timeout*1000),
46123             upload: this.form.fileUpload ? this.success : undefined
46124         };
46125     }
46126 };
46127
46128 Roo.form.Action.Submit = function(form, options){
46129     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46130 };
46131
46132 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46133     type : 'submit',
46134
46135     haveProgress : false,
46136     uploadComplete : false,
46137     
46138     // uploadProgress indicator.
46139     uploadProgress : function()
46140     {
46141         if (!this.form.progressUrl) {
46142             return;
46143         }
46144         
46145         if (!this.haveProgress) {
46146             Roo.MessageBox.progress("Uploading", "Uploading");
46147         }
46148         if (this.uploadComplete) {
46149            Roo.MessageBox.hide();
46150            return;
46151         }
46152         
46153         this.haveProgress = true;
46154    
46155         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46156         
46157         var c = new Roo.data.Connection();
46158         c.request({
46159             url : this.form.progressUrl,
46160             params: {
46161                 id : uid
46162             },
46163             method: 'GET',
46164             success : function(req){
46165                //console.log(data);
46166                 var rdata = false;
46167                 var edata;
46168                 try  {
46169                    rdata = Roo.decode(req.responseText)
46170                 } catch (e) {
46171                     Roo.log("Invalid data from server..");
46172                     Roo.log(edata);
46173                     return;
46174                 }
46175                 if (!rdata || !rdata.success) {
46176                     Roo.log(rdata);
46177                     Roo.MessageBox.alert(Roo.encode(rdata));
46178                     return;
46179                 }
46180                 var data = rdata.data;
46181                 
46182                 if (this.uploadComplete) {
46183                    Roo.MessageBox.hide();
46184                    return;
46185                 }
46186                    
46187                 if (data){
46188                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46189                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46190                     );
46191                 }
46192                 this.uploadProgress.defer(2000,this);
46193             },
46194        
46195             failure: function(data) {
46196                 Roo.log('progress url failed ');
46197                 Roo.log(data);
46198             },
46199             scope : this
46200         });
46201            
46202     },
46203     
46204     
46205     run : function()
46206     {
46207         // run get Values on the form, so it syncs any secondary forms.
46208         this.form.getValues();
46209         
46210         var o = this.options;
46211         var method = this.getMethod();
46212         var isPost = method == 'POST';
46213         if(o.clientValidation === false || this.form.isValid()){
46214             
46215             if (this.form.progressUrl) {
46216                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46217                     (new Date() * 1) + '' + Math.random());
46218                     
46219             } 
46220             
46221             
46222             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46223                 form:this.form.el.dom,
46224                 url:this.getUrl(!isPost),
46225                 method: method,
46226                 params:isPost ? this.getParams() : null,
46227                 isUpload: this.form.fileUpload
46228             }));
46229             
46230             this.uploadProgress();
46231
46232         }else if (o.clientValidation !== false){ // client validation failed
46233             this.failureType = Roo.form.Action.CLIENT_INVALID;
46234             this.form.afterAction(this, false);
46235         }
46236     },
46237
46238     success : function(response)
46239     {
46240         this.uploadComplete= true;
46241         if (this.haveProgress) {
46242             Roo.MessageBox.hide();
46243         }
46244         
46245         
46246         var result = this.processResponse(response);
46247         if(result === true || result.success){
46248             this.form.afterAction(this, true);
46249             return;
46250         }
46251         if(result.errors){
46252             this.form.markInvalid(result.errors);
46253             this.failureType = Roo.form.Action.SERVER_INVALID;
46254         }
46255         this.form.afterAction(this, false);
46256     },
46257     failure : function(response)
46258     {
46259         this.uploadComplete= true;
46260         if (this.haveProgress) {
46261             Roo.MessageBox.hide();
46262         }
46263         
46264         this.response = response;
46265         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46266         this.form.afterAction(this, false);
46267     },
46268     
46269     handleResponse : function(response){
46270         if(this.form.errorReader){
46271             var rs = this.form.errorReader.read(response);
46272             var errors = [];
46273             if(rs.records){
46274                 for(var i = 0, len = rs.records.length; i < len; i++) {
46275                     var r = rs.records[i];
46276                     errors[i] = r.data;
46277                 }
46278             }
46279             if(errors.length < 1){
46280                 errors = null;
46281             }
46282             return {
46283                 success : rs.success,
46284                 errors : errors
46285             };
46286         }
46287         var ret = false;
46288         try {
46289             ret = Roo.decode(response.responseText);
46290         } catch (e) {
46291             ret = {
46292                 success: false,
46293                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46294                 errors : []
46295             };
46296         }
46297         return ret;
46298         
46299     }
46300 });
46301
46302
46303 Roo.form.Action.Load = function(form, options){
46304     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46305     this.reader = this.form.reader;
46306 };
46307
46308 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46309     type : 'load',
46310
46311     run : function(){
46312         
46313         Roo.Ajax.request(Roo.apply(
46314                 this.createCallback(), {
46315                     method:this.getMethod(),
46316                     url:this.getUrl(false),
46317                     params:this.getParams()
46318         }));
46319     },
46320
46321     success : function(response){
46322         
46323         var result = this.processResponse(response);
46324         if(result === true || !result.success || !result.data){
46325             this.failureType = Roo.form.Action.LOAD_FAILURE;
46326             this.form.afterAction(this, false);
46327             return;
46328         }
46329         this.form.clearInvalid();
46330         this.form.setValues(result.data);
46331         this.form.afterAction(this, true);
46332     },
46333
46334     handleResponse : function(response){
46335         if(this.form.reader){
46336             var rs = this.form.reader.read(response);
46337             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46338             return {
46339                 success : rs.success,
46340                 data : data
46341             };
46342         }
46343         return Roo.decode(response.responseText);
46344     }
46345 });
46346
46347 Roo.form.Action.ACTION_TYPES = {
46348     'load' : Roo.form.Action.Load,
46349     'submit' : Roo.form.Action.Submit
46350 };/*
46351  * Based on:
46352  * Ext JS Library 1.1.1
46353  * Copyright(c) 2006-2007, Ext JS, LLC.
46354  *
46355  * Originally Released Under LGPL - original licence link has changed is not relivant.
46356  *
46357  * Fork - LGPL
46358  * <script type="text/javascript">
46359  */
46360  
46361 /**
46362  * @class Roo.form.Layout
46363  * @extends Roo.Component
46364  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46365  * @constructor
46366  * @param {Object} config Configuration options
46367  */
46368 Roo.form.Layout = function(config){
46369     var xitems = [];
46370     if (config.items) {
46371         xitems = config.items;
46372         delete config.items;
46373     }
46374     Roo.form.Layout.superclass.constructor.call(this, config);
46375     this.stack = [];
46376     Roo.each(xitems, this.addxtype, this);
46377      
46378 };
46379
46380 Roo.extend(Roo.form.Layout, Roo.Component, {
46381     /**
46382      * @cfg {String/Object} autoCreate
46383      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46384      */
46385     /**
46386      * @cfg {String/Object/Function} style
46387      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46388      * a function which returns such a specification.
46389      */
46390     /**
46391      * @cfg {String} labelAlign
46392      * Valid values are "left," "top" and "right" (defaults to "left")
46393      */
46394     /**
46395      * @cfg {Number} labelWidth
46396      * Fixed width in pixels of all field labels (defaults to undefined)
46397      */
46398     /**
46399      * @cfg {Boolean} clear
46400      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46401      */
46402     clear : true,
46403     /**
46404      * @cfg {String} labelSeparator
46405      * The separator to use after field labels (defaults to ':')
46406      */
46407     labelSeparator : ':',
46408     /**
46409      * @cfg {Boolean} hideLabels
46410      * True to suppress the display of field labels in this layout (defaults to false)
46411      */
46412     hideLabels : false,
46413
46414     // private
46415     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46416     
46417     isLayout : true,
46418     
46419     // private
46420     onRender : function(ct, position){
46421         if(this.el){ // from markup
46422             this.el = Roo.get(this.el);
46423         }else {  // generate
46424             var cfg = this.getAutoCreate();
46425             this.el = ct.createChild(cfg, position);
46426         }
46427         if(this.style){
46428             this.el.applyStyles(this.style);
46429         }
46430         if(this.labelAlign){
46431             this.el.addClass('x-form-label-'+this.labelAlign);
46432         }
46433         if(this.hideLabels){
46434             this.labelStyle = "display:none";
46435             this.elementStyle = "padding-left:0;";
46436         }else{
46437             if(typeof this.labelWidth == 'number'){
46438                 this.labelStyle = "width:"+this.labelWidth+"px;";
46439                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46440             }
46441             if(this.labelAlign == 'top'){
46442                 this.labelStyle = "width:auto;";
46443                 this.elementStyle = "padding-left:0;";
46444             }
46445         }
46446         var stack = this.stack;
46447         var slen = stack.length;
46448         if(slen > 0){
46449             if(!this.fieldTpl){
46450                 var t = new Roo.Template(
46451                     '<div class="x-form-item {5}">',
46452                         '<label for="{0}" style="{2}">{1}{4}</label>',
46453                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46454                         '</div>',
46455                     '</div><div class="x-form-clear-left"></div>'
46456                 );
46457                 t.disableFormats = true;
46458                 t.compile();
46459                 Roo.form.Layout.prototype.fieldTpl = t;
46460             }
46461             for(var i = 0; i < slen; i++) {
46462                 if(stack[i].isFormField){
46463                     this.renderField(stack[i]);
46464                 }else{
46465                     this.renderComponent(stack[i]);
46466                 }
46467             }
46468         }
46469         if(this.clear){
46470             this.el.createChild({cls:'x-form-clear'});
46471         }
46472     },
46473
46474     // private
46475     renderField : function(f){
46476         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46477                f.id, //0
46478                f.fieldLabel, //1
46479                f.labelStyle||this.labelStyle||'', //2
46480                this.elementStyle||'', //3
46481                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46482                f.itemCls||this.itemCls||''  //5
46483        ], true).getPrevSibling());
46484     },
46485
46486     // private
46487     renderComponent : function(c){
46488         c.render(c.isLayout ? this.el : this.el.createChild());    
46489     },
46490     /**
46491      * Adds a object form elements (using the xtype property as the factory method.)
46492      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46493      * @param {Object} config 
46494      */
46495     addxtype : function(o)
46496     {
46497         // create the lement.
46498         o.form = this.form;
46499         var fe = Roo.factory(o, Roo.form);
46500         this.form.allItems.push(fe);
46501         this.stack.push(fe);
46502         
46503         if (fe.isFormField) {
46504             this.form.items.add(fe);
46505         }
46506          
46507         return fe;
46508     }
46509 });
46510
46511 /**
46512  * @class Roo.form.Column
46513  * @extends Roo.form.Layout
46514  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46515  * @constructor
46516  * @param {Object} config Configuration options
46517  */
46518 Roo.form.Column = function(config){
46519     Roo.form.Column.superclass.constructor.call(this, config);
46520 };
46521
46522 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46523     /**
46524      * @cfg {Number/String} width
46525      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46526      */
46527     /**
46528      * @cfg {String/Object} autoCreate
46529      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46530      */
46531
46532     // private
46533     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46534
46535     // private
46536     onRender : function(ct, position){
46537         Roo.form.Column.superclass.onRender.call(this, ct, position);
46538         if(this.width){
46539             this.el.setWidth(this.width);
46540         }
46541     }
46542 });
46543
46544
46545 /**
46546  * @class Roo.form.Row
46547  * @extends Roo.form.Layout
46548  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46549  * @constructor
46550  * @param {Object} config Configuration options
46551  */
46552
46553  
46554 Roo.form.Row = function(config){
46555     Roo.form.Row.superclass.constructor.call(this, config);
46556 };
46557  
46558 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46559       /**
46560      * @cfg {Number/String} width
46561      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46562      */
46563     /**
46564      * @cfg {Number/String} height
46565      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46566      */
46567     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46568     
46569     padWidth : 20,
46570     // private
46571     onRender : function(ct, position){
46572         //console.log('row render');
46573         if(!this.rowTpl){
46574             var t = new Roo.Template(
46575                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46576                     '<label for="{0}" style="{2}">{1}{4}</label>',
46577                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46578                     '</div>',
46579                 '</div>'
46580             );
46581             t.disableFormats = true;
46582             t.compile();
46583             Roo.form.Layout.prototype.rowTpl = t;
46584         }
46585         this.fieldTpl = this.rowTpl;
46586         
46587         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46588         var labelWidth = 100;
46589         
46590         if ((this.labelAlign != 'top')) {
46591             if (typeof this.labelWidth == 'number') {
46592                 labelWidth = this.labelWidth
46593             }
46594             this.padWidth =  20 + labelWidth;
46595             
46596         }
46597         
46598         Roo.form.Column.superclass.onRender.call(this, ct, position);
46599         if(this.width){
46600             this.el.setWidth(this.width);
46601         }
46602         if(this.height){
46603             this.el.setHeight(this.height);
46604         }
46605     },
46606     
46607     // private
46608     renderField : function(f){
46609         f.fieldEl = this.fieldTpl.append(this.el, [
46610                f.id, f.fieldLabel,
46611                f.labelStyle||this.labelStyle||'',
46612                this.elementStyle||'',
46613                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46614                f.itemCls||this.itemCls||'',
46615                f.width ? f.width + this.padWidth : 160 + this.padWidth
46616        ],true);
46617     }
46618 });
46619  
46620
46621 /**
46622  * @class Roo.form.FieldSet
46623  * @extends Roo.form.Layout
46624  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46625  * @constructor
46626  * @param {Object} config Configuration options
46627  */
46628 Roo.form.FieldSet = function(config){
46629     Roo.form.FieldSet.superclass.constructor.call(this, config);
46630 };
46631
46632 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46633     /**
46634      * @cfg {String} legend
46635      * The text to display as the legend for the FieldSet (defaults to '')
46636      */
46637     /**
46638      * @cfg {String/Object} autoCreate
46639      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46640      */
46641
46642     // private
46643     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46644
46645     // private
46646     onRender : function(ct, position){
46647         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46648         if(this.legend){
46649             this.setLegend(this.legend);
46650         }
46651     },
46652
46653     // private
46654     setLegend : function(text){
46655         if(this.rendered){
46656             this.el.child('legend').update(text);
46657         }
46658     }
46659 });/*
46660  * Based on:
46661  * Ext JS Library 1.1.1
46662  * Copyright(c) 2006-2007, Ext JS, LLC.
46663  *
46664  * Originally Released Under LGPL - original licence link has changed is not relivant.
46665  *
46666  * Fork - LGPL
46667  * <script type="text/javascript">
46668  */
46669 /**
46670  * @class Roo.form.VTypes
46671  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46672  * @singleton
46673  */
46674 Roo.form.VTypes = function(){
46675     // closure these in so they are only created once.
46676     var alpha = /^[a-zA-Z_]+$/;
46677     var alphanum = /^[a-zA-Z0-9_]+$/;
46678     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46679     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46680
46681     // All these messages and functions are configurable
46682     return {
46683         /**
46684          * The function used to validate email addresses
46685          * @param {String} value The email address
46686          */
46687         'email' : function(v){
46688             return email.test(v);
46689         },
46690         /**
46691          * The error text to display when the email validation function returns false
46692          * @type String
46693          */
46694         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46695         /**
46696          * The keystroke filter mask to be applied on email input
46697          * @type RegExp
46698          */
46699         'emailMask' : /[a-z0-9_\.\-@]/i,
46700
46701         /**
46702          * The function used to validate URLs
46703          * @param {String} value The URL
46704          */
46705         'url' : function(v){
46706             return url.test(v);
46707         },
46708         /**
46709          * The error text to display when the url validation function returns false
46710          * @type String
46711          */
46712         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46713         
46714         /**
46715          * The function used to validate alpha values
46716          * @param {String} value The value
46717          */
46718         'alpha' : function(v){
46719             return alpha.test(v);
46720         },
46721         /**
46722          * The error text to display when the alpha validation function returns false
46723          * @type String
46724          */
46725         'alphaText' : 'This field should only contain letters and _',
46726         /**
46727          * The keystroke filter mask to be applied on alpha input
46728          * @type RegExp
46729          */
46730         'alphaMask' : /[a-z_]/i,
46731
46732         /**
46733          * The function used to validate alphanumeric values
46734          * @param {String} value The value
46735          */
46736         'alphanum' : function(v){
46737             return alphanum.test(v);
46738         },
46739         /**
46740          * The error text to display when the alphanumeric validation function returns false
46741          * @type String
46742          */
46743         'alphanumText' : 'This field should only contain letters, numbers and _',
46744         /**
46745          * The keystroke filter mask to be applied on alphanumeric input
46746          * @type RegExp
46747          */
46748         'alphanumMask' : /[a-z0-9_]/i
46749     };
46750 }();//<script type="text/javascript">
46751
46752 /**
46753  * @class Roo.form.FCKeditor
46754  * @extends Roo.form.TextArea
46755  * Wrapper around the FCKEditor http://www.fckeditor.net
46756  * @constructor
46757  * Creates a new FCKeditor
46758  * @param {Object} config Configuration options
46759  */
46760 Roo.form.FCKeditor = function(config){
46761     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46762     this.addEvents({
46763          /**
46764          * @event editorinit
46765          * Fired when the editor is initialized - you can add extra handlers here..
46766          * @param {FCKeditor} this
46767          * @param {Object} the FCK object.
46768          */
46769         editorinit : true
46770     });
46771     
46772     
46773 };
46774 Roo.form.FCKeditor.editors = { };
46775 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46776 {
46777     //defaultAutoCreate : {
46778     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46779     //},
46780     // private
46781     /**
46782      * @cfg {Object} fck options - see fck manual for details.
46783      */
46784     fckconfig : false,
46785     
46786     /**
46787      * @cfg {Object} fck toolbar set (Basic or Default)
46788      */
46789     toolbarSet : 'Basic',
46790     /**
46791      * @cfg {Object} fck BasePath
46792      */ 
46793     basePath : '/fckeditor/',
46794     
46795     
46796     frame : false,
46797     
46798     value : '',
46799     
46800    
46801     onRender : function(ct, position)
46802     {
46803         if(!this.el){
46804             this.defaultAutoCreate = {
46805                 tag: "textarea",
46806                 style:"width:300px;height:60px;",
46807                 autocomplete: "new-password"
46808             };
46809         }
46810         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46811         /*
46812         if(this.grow){
46813             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46814             if(this.preventScrollbars){
46815                 this.el.setStyle("overflow", "hidden");
46816             }
46817             this.el.setHeight(this.growMin);
46818         }
46819         */
46820         //console.log('onrender' + this.getId() );
46821         Roo.form.FCKeditor.editors[this.getId()] = this;
46822          
46823
46824         this.replaceTextarea() ;
46825         
46826     },
46827     
46828     getEditor : function() {
46829         return this.fckEditor;
46830     },
46831     /**
46832      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46833      * @param {Mixed} value The value to set
46834      */
46835     
46836     
46837     setValue : function(value)
46838     {
46839         //console.log('setValue: ' + value);
46840         
46841         if(typeof(value) == 'undefined') { // not sure why this is happending...
46842             return;
46843         }
46844         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46845         
46846         //if(!this.el || !this.getEditor()) {
46847         //    this.value = value;
46848             //this.setValue.defer(100,this,[value]);    
46849         //    return;
46850         //} 
46851         
46852         if(!this.getEditor()) {
46853             return;
46854         }
46855         
46856         this.getEditor().SetData(value);
46857         
46858         //
46859
46860     },
46861
46862     /**
46863      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46864      * @return {Mixed} value The field value
46865      */
46866     getValue : function()
46867     {
46868         
46869         if (this.frame && this.frame.dom.style.display == 'none') {
46870             return Roo.form.FCKeditor.superclass.getValue.call(this);
46871         }
46872         
46873         if(!this.el || !this.getEditor()) {
46874            
46875            // this.getValue.defer(100,this); 
46876             return this.value;
46877         }
46878        
46879         
46880         var value=this.getEditor().GetData();
46881         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46882         return Roo.form.FCKeditor.superclass.getValue.call(this);
46883         
46884
46885     },
46886
46887     /**
46888      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46889      * @return {Mixed} value The field value
46890      */
46891     getRawValue : function()
46892     {
46893         if (this.frame && this.frame.dom.style.display == 'none') {
46894             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46895         }
46896         
46897         if(!this.el || !this.getEditor()) {
46898             //this.getRawValue.defer(100,this); 
46899             return this.value;
46900             return;
46901         }
46902         
46903         
46904         
46905         var value=this.getEditor().GetData();
46906         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46907         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46908          
46909     },
46910     
46911     setSize : function(w,h) {
46912         
46913         
46914         
46915         //if (this.frame && this.frame.dom.style.display == 'none') {
46916         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46917         //    return;
46918         //}
46919         //if(!this.el || !this.getEditor()) {
46920         //    this.setSize.defer(100,this, [w,h]); 
46921         //    return;
46922         //}
46923         
46924         
46925         
46926         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46927         
46928         this.frame.dom.setAttribute('width', w);
46929         this.frame.dom.setAttribute('height', h);
46930         this.frame.setSize(w,h);
46931         
46932     },
46933     
46934     toggleSourceEdit : function(value) {
46935         
46936       
46937          
46938         this.el.dom.style.display = value ? '' : 'none';
46939         this.frame.dom.style.display = value ?  'none' : '';
46940         
46941     },
46942     
46943     
46944     focus: function(tag)
46945     {
46946         if (this.frame.dom.style.display == 'none') {
46947             return Roo.form.FCKeditor.superclass.focus.call(this);
46948         }
46949         if(!this.el || !this.getEditor()) {
46950             this.focus.defer(100,this, [tag]); 
46951             return;
46952         }
46953         
46954         
46955         
46956         
46957         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46958         this.getEditor().Focus();
46959         if (tgs.length) {
46960             if (!this.getEditor().Selection.GetSelection()) {
46961                 this.focus.defer(100,this, [tag]); 
46962                 return;
46963             }
46964             
46965             
46966             var r = this.getEditor().EditorDocument.createRange();
46967             r.setStart(tgs[0],0);
46968             r.setEnd(tgs[0],0);
46969             this.getEditor().Selection.GetSelection().removeAllRanges();
46970             this.getEditor().Selection.GetSelection().addRange(r);
46971             this.getEditor().Focus();
46972         }
46973         
46974     },
46975     
46976     
46977     
46978     replaceTextarea : function()
46979     {
46980         if ( document.getElementById( this.getId() + '___Frame' ) )
46981             return ;
46982         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46983         //{
46984             // We must check the elements firstly using the Id and then the name.
46985         var oTextarea = document.getElementById( this.getId() );
46986         
46987         var colElementsByName = document.getElementsByName( this.getId() ) ;
46988          
46989         oTextarea.style.display = 'none' ;
46990
46991         if ( oTextarea.tabIndex ) {            
46992             this.TabIndex = oTextarea.tabIndex ;
46993         }
46994         
46995         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46996         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46997         this.frame = Roo.get(this.getId() + '___Frame')
46998     },
46999     
47000     _getConfigHtml : function()
47001     {
47002         var sConfig = '' ;
47003
47004         for ( var o in this.fckconfig ) {
47005             sConfig += sConfig.length > 0  ? '&amp;' : '';
47006             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47007         }
47008
47009         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47010     },
47011     
47012     
47013     _getIFrameHtml : function()
47014     {
47015         var sFile = 'fckeditor.html' ;
47016         /* no idea what this is about..
47017         try
47018         {
47019             if ( (/fcksource=true/i).test( window.top.location.search ) )
47020                 sFile = 'fckeditor.original.html' ;
47021         }
47022         catch (e) { 
47023         */
47024
47025         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47026         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47027         
47028         
47029         var html = '<iframe id="' + this.getId() +
47030             '___Frame" src="' + sLink +
47031             '" width="' + this.width +
47032             '" height="' + this.height + '"' +
47033             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47034             ' frameborder="0" scrolling="no"></iframe>' ;
47035
47036         return html ;
47037     },
47038     
47039     _insertHtmlBefore : function( html, element )
47040     {
47041         if ( element.insertAdjacentHTML )       {
47042             // IE
47043             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47044         } else { // Gecko
47045             var oRange = document.createRange() ;
47046             oRange.setStartBefore( element ) ;
47047             var oFragment = oRange.createContextualFragment( html );
47048             element.parentNode.insertBefore( oFragment, element ) ;
47049         }
47050     }
47051     
47052     
47053   
47054     
47055     
47056     
47057     
47058
47059 });
47060
47061 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47062
47063 function FCKeditor_OnComplete(editorInstance){
47064     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47065     f.fckEditor = editorInstance;
47066     //console.log("loaded");
47067     f.fireEvent('editorinit', f, editorInstance);
47068
47069   
47070
47071  
47072
47073
47074
47075
47076
47077
47078
47079
47080
47081
47082
47083
47084
47085
47086
47087 //<script type="text/javascript">
47088 /**
47089  * @class Roo.form.GridField
47090  * @extends Roo.form.Field
47091  * Embed a grid (or editable grid into a form)
47092  * STATUS ALPHA
47093  * 
47094  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47095  * it needs 
47096  * xgrid.store = Roo.data.Store
47097  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47098  * xgrid.store.reader = Roo.data.JsonReader 
47099  * 
47100  * 
47101  * @constructor
47102  * Creates a new GridField
47103  * @param {Object} config Configuration options
47104  */
47105 Roo.form.GridField = function(config){
47106     Roo.form.GridField.superclass.constructor.call(this, config);
47107      
47108 };
47109
47110 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47111     /**
47112      * @cfg {Number} width  - used to restrict width of grid..
47113      */
47114     width : 100,
47115     /**
47116      * @cfg {Number} height - used to restrict height of grid..
47117      */
47118     height : 50,
47119      /**
47120      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47121          * 
47122          *}
47123      */
47124     xgrid : false, 
47125     /**
47126      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47127      * {tag: "input", type: "checkbox", autocomplete: "off"})
47128      */
47129    // defaultAutoCreate : { tag: 'div' },
47130     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47131     /**
47132      * @cfg {String} addTitle Text to include for adding a title.
47133      */
47134     addTitle : false,
47135     //
47136     onResize : function(){
47137         Roo.form.Field.superclass.onResize.apply(this, arguments);
47138     },
47139
47140     initEvents : function(){
47141         // Roo.form.Checkbox.superclass.initEvents.call(this);
47142         // has no events...
47143        
47144     },
47145
47146
47147     getResizeEl : function(){
47148         return this.wrap;
47149     },
47150
47151     getPositionEl : function(){
47152         return this.wrap;
47153     },
47154
47155     // private
47156     onRender : function(ct, position){
47157         
47158         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47159         var style = this.style;
47160         delete this.style;
47161         
47162         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47163         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47164         this.viewEl = this.wrap.createChild({ tag: 'div' });
47165         if (style) {
47166             this.viewEl.applyStyles(style);
47167         }
47168         if (this.width) {
47169             this.viewEl.setWidth(this.width);
47170         }
47171         if (this.height) {
47172             this.viewEl.setHeight(this.height);
47173         }
47174         //if(this.inputValue !== undefined){
47175         //this.setValue(this.value);
47176         
47177         
47178         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47179         
47180         
47181         this.grid.render();
47182         this.grid.getDataSource().on('remove', this.refreshValue, this);
47183         this.grid.getDataSource().on('update', this.refreshValue, this);
47184         this.grid.on('afteredit', this.refreshValue, this);
47185  
47186     },
47187      
47188     
47189     /**
47190      * Sets the value of the item. 
47191      * @param {String} either an object  or a string..
47192      */
47193     setValue : function(v){
47194         //this.value = v;
47195         v = v || []; // empty set..
47196         // this does not seem smart - it really only affects memoryproxy grids..
47197         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47198             var ds = this.grid.getDataSource();
47199             // assumes a json reader..
47200             var data = {}
47201             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47202             ds.loadData( data);
47203         }
47204         // clear selection so it does not get stale.
47205         if (this.grid.sm) { 
47206             this.grid.sm.clearSelections();
47207         }
47208         
47209         Roo.form.GridField.superclass.setValue.call(this, v);
47210         this.refreshValue();
47211         // should load data in the grid really....
47212     },
47213     
47214     // private
47215     refreshValue: function() {
47216          var val = [];
47217         this.grid.getDataSource().each(function(r) {
47218             val.push(r.data);
47219         });
47220         this.el.dom.value = Roo.encode(val);
47221     }
47222     
47223      
47224     
47225     
47226 });/*
47227  * Based on:
47228  * Ext JS Library 1.1.1
47229  * Copyright(c) 2006-2007, Ext JS, LLC.
47230  *
47231  * Originally Released Under LGPL - original licence link has changed is not relivant.
47232  *
47233  * Fork - LGPL
47234  * <script type="text/javascript">
47235  */
47236 /**
47237  * @class Roo.form.DisplayField
47238  * @extends Roo.form.Field
47239  * A generic Field to display non-editable data.
47240  * @constructor
47241  * Creates a new Display Field item.
47242  * @param {Object} config Configuration options
47243  */
47244 Roo.form.DisplayField = function(config){
47245     Roo.form.DisplayField.superclass.constructor.call(this, config);
47246     
47247 };
47248
47249 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47250     inputType:      'hidden',
47251     allowBlank:     true,
47252     readOnly:         true,
47253     
47254  
47255     /**
47256      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47257      */
47258     focusClass : undefined,
47259     /**
47260      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47261      */
47262     fieldClass: 'x-form-field',
47263     
47264      /**
47265      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47266      */
47267     valueRenderer: undefined,
47268     
47269     width: 100,
47270     /**
47271      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47272      * {tag: "input", type: "checkbox", autocomplete: "off"})
47273      */
47274      
47275  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47276
47277     onResize : function(){
47278         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47279         
47280     },
47281
47282     initEvents : function(){
47283         // Roo.form.Checkbox.superclass.initEvents.call(this);
47284         // has no events...
47285        
47286     },
47287
47288
47289     getResizeEl : function(){
47290         return this.wrap;
47291     },
47292
47293     getPositionEl : function(){
47294         return this.wrap;
47295     },
47296
47297     // private
47298     onRender : function(ct, position){
47299         
47300         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47301         //if(this.inputValue !== undefined){
47302         this.wrap = this.el.wrap();
47303         
47304         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47305         
47306         if (this.bodyStyle) {
47307             this.viewEl.applyStyles(this.bodyStyle);
47308         }
47309         //this.viewEl.setStyle('padding', '2px');
47310         
47311         this.setValue(this.value);
47312         
47313     },
47314 /*
47315     // private
47316     initValue : Roo.emptyFn,
47317
47318   */
47319
47320         // private
47321     onClick : function(){
47322         
47323     },
47324
47325     /**
47326      * Sets the checked state of the checkbox.
47327      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47328      */
47329     setValue : function(v){
47330         this.value = v;
47331         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47332         // this might be called before we have a dom element..
47333         if (!this.viewEl) {
47334             return;
47335         }
47336         this.viewEl.dom.innerHTML = html;
47337         Roo.form.DisplayField.superclass.setValue.call(this, v);
47338
47339     }
47340 });/*
47341  * 
47342  * Licence- LGPL
47343  * 
47344  */
47345
47346 /**
47347  * @class Roo.form.DayPicker
47348  * @extends Roo.form.Field
47349  * A Day picker show [M] [T] [W] ....
47350  * @constructor
47351  * Creates a new Day Picker
47352  * @param {Object} config Configuration options
47353  */
47354 Roo.form.DayPicker= function(config){
47355     Roo.form.DayPicker.superclass.constructor.call(this, config);
47356      
47357 };
47358
47359 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47360     /**
47361      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47362      */
47363     focusClass : undefined,
47364     /**
47365      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47366      */
47367     fieldClass: "x-form-field",
47368    
47369     /**
47370      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47371      * {tag: "input", type: "checkbox", autocomplete: "off"})
47372      */
47373     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47374     
47375    
47376     actionMode : 'viewEl', 
47377     //
47378     // private
47379  
47380     inputType : 'hidden',
47381     
47382      
47383     inputElement: false, // real input element?
47384     basedOn: false, // ????
47385     
47386     isFormField: true, // not sure where this is needed!!!!
47387
47388     onResize : function(){
47389         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47390         if(!this.boxLabel){
47391             this.el.alignTo(this.wrap, 'c-c');
47392         }
47393     },
47394
47395     initEvents : function(){
47396         Roo.form.Checkbox.superclass.initEvents.call(this);
47397         this.el.on("click", this.onClick,  this);
47398         this.el.on("change", this.onClick,  this);
47399     },
47400
47401
47402     getResizeEl : function(){
47403         return this.wrap;
47404     },
47405
47406     getPositionEl : function(){
47407         return this.wrap;
47408     },
47409
47410     
47411     // private
47412     onRender : function(ct, position){
47413         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47414        
47415         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47416         
47417         var r1 = '<table><tr>';
47418         var r2 = '<tr class="x-form-daypick-icons">';
47419         for (var i=0; i < 7; i++) {
47420             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47421             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47422         }
47423         
47424         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47425         viewEl.select('img').on('click', this.onClick, this);
47426         this.viewEl = viewEl;   
47427         
47428         
47429         // this will not work on Chrome!!!
47430         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47431         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47432         
47433         
47434           
47435
47436     },
47437
47438     // private
47439     initValue : Roo.emptyFn,
47440
47441     /**
47442      * Returns the checked state of the checkbox.
47443      * @return {Boolean} True if checked, else false
47444      */
47445     getValue : function(){
47446         return this.el.dom.value;
47447         
47448     },
47449
47450         // private
47451     onClick : function(e){ 
47452         //this.setChecked(!this.checked);
47453         Roo.get(e.target).toggleClass('x-menu-item-checked');
47454         this.refreshValue();
47455         //if(this.el.dom.checked != this.checked){
47456         //    this.setValue(this.el.dom.checked);
47457        // }
47458     },
47459     
47460     // private
47461     refreshValue : function()
47462     {
47463         var val = '';
47464         this.viewEl.select('img',true).each(function(e,i,n)  {
47465             val += e.is(".x-menu-item-checked") ? String(n) : '';
47466         });
47467         this.setValue(val, true);
47468     },
47469
47470     /**
47471      * Sets the checked state of the checkbox.
47472      * On is always based on a string comparison between inputValue and the param.
47473      * @param {Boolean/String} value - the value to set 
47474      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47475      */
47476     setValue : function(v,suppressEvent){
47477         if (!this.el.dom) {
47478             return;
47479         }
47480         var old = this.el.dom.value ;
47481         this.el.dom.value = v;
47482         if (suppressEvent) {
47483             return ;
47484         }
47485          
47486         // update display..
47487         this.viewEl.select('img',true).each(function(e,i,n)  {
47488             
47489             var on = e.is(".x-menu-item-checked");
47490             var newv = v.indexOf(String(n)) > -1;
47491             if (on != newv) {
47492                 e.toggleClass('x-menu-item-checked');
47493             }
47494             
47495         });
47496         
47497         
47498         this.fireEvent('change', this, v, old);
47499         
47500         
47501     },
47502    
47503     // handle setting of hidden value by some other method!!?!?
47504     setFromHidden: function()
47505     {
47506         if(!this.el){
47507             return;
47508         }
47509         //console.log("SET FROM HIDDEN");
47510         //alert('setFrom hidden');
47511         this.setValue(this.el.dom.value);
47512     },
47513     
47514     onDestroy : function()
47515     {
47516         if(this.viewEl){
47517             Roo.get(this.viewEl).remove();
47518         }
47519          
47520         Roo.form.DayPicker.superclass.onDestroy.call(this);
47521     }
47522
47523 });/*
47524  * RooJS Library 1.1.1
47525  * Copyright(c) 2008-2011  Alan Knowles
47526  *
47527  * License - LGPL
47528  */
47529  
47530
47531 /**
47532  * @class Roo.form.ComboCheck
47533  * @extends Roo.form.ComboBox
47534  * A combobox for multiple select items.
47535  *
47536  * FIXME - could do with a reset button..
47537  * 
47538  * @constructor
47539  * Create a new ComboCheck
47540  * @param {Object} config Configuration options
47541  */
47542 Roo.form.ComboCheck = function(config){
47543     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47544     // should verify some data...
47545     // like
47546     // hiddenName = required..
47547     // displayField = required
47548     // valudField == required
47549     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47550     var _t = this;
47551     Roo.each(req, function(e) {
47552         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47553             throw "Roo.form.ComboCheck : missing value for: " + e;
47554         }
47555     });
47556     
47557     
47558 };
47559
47560 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47561      
47562      
47563     editable : false,
47564      
47565     selectedClass: 'x-menu-item-checked', 
47566     
47567     // private
47568     onRender : function(ct, position){
47569         var _t = this;
47570         
47571         
47572         
47573         if(!this.tpl){
47574             var cls = 'x-combo-list';
47575
47576             
47577             this.tpl =  new Roo.Template({
47578                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47579                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47580                    '<span>{' + this.displayField + '}</span>' +
47581                     '</div>' 
47582                 
47583             });
47584         }
47585  
47586         
47587         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47588         this.view.singleSelect = false;
47589         this.view.multiSelect = true;
47590         this.view.toggleSelect = true;
47591         this.pageTb.add(new Roo.Toolbar.Fill(), {
47592             
47593             text: 'Done',
47594             handler: function()
47595             {
47596                 _t.collapse();
47597             }
47598         });
47599     },
47600     
47601     onViewOver : function(e, t){
47602         // do nothing...
47603         return;
47604         
47605     },
47606     
47607     onViewClick : function(doFocus,index){
47608         return;
47609         
47610     },
47611     select: function () {
47612         //Roo.log("SELECT CALLED");
47613     },
47614      
47615     selectByValue : function(xv, scrollIntoView){
47616         var ar = this.getValueArray();
47617         var sels = [];
47618         
47619         Roo.each(ar, function(v) {
47620             if(v === undefined || v === null){
47621                 return;
47622             }
47623             var r = this.findRecord(this.valueField, v);
47624             if(r){
47625                 sels.push(this.store.indexOf(r))
47626                 
47627             }
47628         },this);
47629         this.view.select(sels);
47630         return false;
47631     },
47632     
47633     
47634     
47635     onSelect : function(record, index){
47636        // Roo.log("onselect Called");
47637        // this is only called by the clear button now..
47638         this.view.clearSelections();
47639         this.setValue('[]');
47640         if (this.value != this.valueBefore) {
47641             this.fireEvent('change', this, this.value, this.valueBefore);
47642             this.valueBefore = this.value;
47643         }
47644     },
47645     getValueArray : function()
47646     {
47647         var ar = [] ;
47648         
47649         try {
47650             //Roo.log(this.value);
47651             if (typeof(this.value) == 'undefined') {
47652                 return [];
47653             }
47654             var ar = Roo.decode(this.value);
47655             return  ar instanceof Array ? ar : []; //?? valid?
47656             
47657         } catch(e) {
47658             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47659             return [];
47660         }
47661          
47662     },
47663     expand : function ()
47664     {
47665         
47666         Roo.form.ComboCheck.superclass.expand.call(this);
47667         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47668         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47669         
47670
47671     },
47672     
47673     collapse : function(){
47674         Roo.form.ComboCheck.superclass.collapse.call(this);
47675         var sl = this.view.getSelectedIndexes();
47676         var st = this.store;
47677         var nv = [];
47678         var tv = [];
47679         var r;
47680         Roo.each(sl, function(i) {
47681             r = st.getAt(i);
47682             nv.push(r.get(this.valueField));
47683         },this);
47684         this.setValue(Roo.encode(nv));
47685         if (this.value != this.valueBefore) {
47686
47687             this.fireEvent('change', this, this.value, this.valueBefore);
47688             this.valueBefore = this.value;
47689         }
47690         
47691     },
47692     
47693     setValue : function(v){
47694         // Roo.log(v);
47695         this.value = v;
47696         
47697         var vals = this.getValueArray();
47698         var tv = [];
47699         Roo.each(vals, function(k) {
47700             var r = this.findRecord(this.valueField, k);
47701             if(r){
47702                 tv.push(r.data[this.displayField]);
47703             }else if(this.valueNotFoundText !== undefined){
47704                 tv.push( this.valueNotFoundText );
47705             }
47706         },this);
47707        // Roo.log(tv);
47708         
47709         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47710         this.hiddenField.value = v;
47711         this.value = v;
47712     }
47713     
47714 });/*
47715  * Based on:
47716  * Ext JS Library 1.1.1
47717  * Copyright(c) 2006-2007, Ext JS, LLC.
47718  *
47719  * Originally Released Under LGPL - original licence link has changed is not relivant.
47720  *
47721  * Fork - LGPL
47722  * <script type="text/javascript">
47723  */
47724  
47725 /**
47726  * @class Roo.form.Signature
47727  * @extends Roo.form.Field
47728  * Signature field.  
47729  * @constructor
47730  * 
47731  * @param {Object} config Configuration options
47732  */
47733
47734 Roo.form.Signature = function(config){
47735     Roo.form.Signature.superclass.constructor.call(this, config);
47736     
47737     this.addEvents({// not in used??
47738          /**
47739          * @event confirm
47740          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47741              * @param {Roo.form.Signature} combo This combo box
47742              */
47743         'confirm' : true,
47744         /**
47745          * @event reset
47746          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47747              * @param {Roo.form.ComboBox} combo This combo box
47748              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47749              */
47750         'reset' : true
47751     });
47752 };
47753
47754 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47755     /**
47756      * @cfg {Object} labels Label to use when rendering a form.
47757      * defaults to 
47758      * labels : { 
47759      *      clear : "Clear",
47760      *      confirm : "Confirm"
47761      *  }
47762      */
47763     labels : { 
47764         clear : "Clear",
47765         confirm : "Confirm"
47766     },
47767     /**
47768      * @cfg {Number} width The signature panel width (defaults to 300)
47769      */
47770     width: 300,
47771     /**
47772      * @cfg {Number} height The signature panel height (defaults to 100)
47773      */
47774     height : 100,
47775     /**
47776      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47777      */
47778     allowBlank : false,
47779     
47780     //private
47781     // {Object} signPanel The signature SVG panel element (defaults to {})
47782     signPanel : {},
47783     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47784     isMouseDown : false,
47785     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47786     isConfirmed : false,
47787     // {String} signatureTmp SVG mapping string (defaults to empty string)
47788     signatureTmp : '',
47789     
47790     
47791     defaultAutoCreate : { // modified by initCompnoent..
47792         tag: "input",
47793         type:"hidden"
47794     },
47795
47796     // private
47797     onRender : function(ct, position){
47798         
47799         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47800         
47801         this.wrap = this.el.wrap({
47802             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47803         });
47804         
47805         this.createToolbar(this);
47806         this.signPanel = this.wrap.createChild({
47807                 tag: 'div',
47808                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47809             }, this.el
47810         );
47811             
47812         this.svgID = Roo.id();
47813         this.svgEl = this.signPanel.createChild({
47814               xmlns : 'http://www.w3.org/2000/svg',
47815               tag : 'svg',
47816               id : this.svgID + "-svg",
47817               width: this.width,
47818               height: this.height,
47819               viewBox: '0 0 '+this.width+' '+this.height,
47820               cn : [
47821                 {
47822                     tag: "rect",
47823                     id: this.svgID + "-svg-r",
47824                     width: this.width,
47825                     height: this.height,
47826                     fill: "#ffa"
47827                 },
47828                 {
47829                     tag: "line",
47830                     id: this.svgID + "-svg-l",
47831                     x1: "0", // start
47832                     y1: (this.height*0.8), // start set the line in 80% of height
47833                     x2: this.width, // end
47834                     y2: (this.height*0.8), // end set the line in 80% of height
47835                     'stroke': "#666",
47836                     'stroke-width': "1",
47837                     'stroke-dasharray': "3",
47838                     'shape-rendering': "crispEdges",
47839                     'pointer-events': "none"
47840                 },
47841                 {
47842                     tag: "path",
47843                     id: this.svgID + "-svg-p",
47844                     'stroke': "navy",
47845                     'stroke-width': "3",
47846                     'fill': "none",
47847                     'pointer-events': 'none'
47848                 }
47849               ]
47850         });
47851         this.createSVG();
47852         this.svgBox = this.svgEl.dom.getScreenCTM();
47853     },
47854     createSVG : function(){ 
47855         var svg = this.signPanel;
47856         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47857         var t = this;
47858
47859         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47860         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47861         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47862         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47863         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47864         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47865         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47866         
47867     },
47868     isTouchEvent : function(e){
47869         return e.type.match(/^touch/);
47870     },
47871     getCoords : function (e) {
47872         var pt    = this.svgEl.dom.createSVGPoint();
47873         pt.x = e.clientX; 
47874         pt.y = e.clientY;
47875         if (this.isTouchEvent(e)) {
47876             pt.x =  e.targetTouches[0].clientX 
47877             pt.y = e.targetTouches[0].clientY;
47878         }
47879         var a = this.svgEl.dom.getScreenCTM();
47880         var b = a.inverse();
47881         var mx = pt.matrixTransform(b);
47882         return mx.x + ',' + mx.y;
47883     },
47884     //mouse event headler 
47885     down : function (e) {
47886         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47887         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47888         
47889         this.isMouseDown = true;
47890         
47891         e.preventDefault();
47892     },
47893     move : function (e) {
47894         if (this.isMouseDown) {
47895             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47896             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47897         }
47898         
47899         e.preventDefault();
47900     },
47901     up : function (e) {
47902         this.isMouseDown = false;
47903         var sp = this.signatureTmp.split(' ');
47904         
47905         if(sp.length > 1){
47906             if(!sp[sp.length-2].match(/^L/)){
47907                 sp.pop();
47908                 sp.pop();
47909                 sp.push("");
47910                 this.signatureTmp = sp.join(" ");
47911             }
47912         }
47913         if(this.getValue() != this.signatureTmp){
47914             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47915             this.isConfirmed = false;
47916         }
47917         e.preventDefault();
47918     },
47919     
47920     /**
47921      * Protected method that will not generally be called directly. It
47922      * is called when the editor creates its toolbar. Override this method if you need to
47923      * add custom toolbar buttons.
47924      * @param {HtmlEditor} editor
47925      */
47926     createToolbar : function(editor){
47927          function btn(id, toggle, handler){
47928             var xid = fid + '-'+ id ;
47929             return {
47930                 id : xid,
47931                 cmd : id,
47932                 cls : 'x-btn-icon x-edit-'+id,
47933                 enableToggle:toggle !== false,
47934                 scope: editor, // was editor...
47935                 handler:handler||editor.relayBtnCmd,
47936                 clickEvent:'mousedown',
47937                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47938                 tabIndex:-1
47939             };
47940         }
47941         
47942         
47943         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47944         this.tb = tb;
47945         this.tb.add(
47946            {
47947                 cls : ' x-signature-btn x-signature-'+id,
47948                 scope: editor, // was editor...
47949                 handler: this.reset,
47950                 clickEvent:'mousedown',
47951                 text: this.labels.clear
47952             },
47953             {
47954                  xtype : 'Fill',
47955                  xns: Roo.Toolbar
47956             }, 
47957             {
47958                 cls : '  x-signature-btn x-signature-'+id,
47959                 scope: editor, // was editor...
47960                 handler: this.confirmHandler,
47961                 clickEvent:'mousedown',
47962                 text: this.labels.confirm
47963             }
47964         );
47965     
47966     },
47967     //public
47968     /**
47969      * when user is clicked confirm then show this image.....
47970      * 
47971      * @return {String} Image Data URI
47972      */
47973     getImageDataURI : function(){
47974         var svg = this.svgEl.dom.parentNode.innerHTML;
47975         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47976         return src; 
47977     },
47978     /**
47979      * 
47980      * @return {Boolean} this.isConfirmed
47981      */
47982     getConfirmed : function(){
47983         return this.isConfirmed;
47984     },
47985     /**
47986      * 
47987      * @return {Number} this.width
47988      */
47989     getWidth : function(){
47990         return this.width;
47991     },
47992     /**
47993      * 
47994      * @return {Number} this.height
47995      */
47996     getHeight : function(){
47997         return this.height;
47998     },
47999     // private
48000     getSignature : function(){
48001         return this.signatureTmp;
48002     },
48003     // private
48004     reset : function(){
48005         this.signatureTmp = '';
48006         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48007         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48008         this.isConfirmed = false;
48009         Roo.form.Signature.superclass.reset.call(this);
48010     },
48011     setSignature : function(s){
48012         this.signatureTmp = s;
48013         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48014         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48015         this.setValue(s);
48016         this.isConfirmed = false;
48017         Roo.form.Signature.superclass.reset.call(this);
48018     }, 
48019     test : function(){
48020 //        Roo.log(this.signPanel.dom.contentWindow.up())
48021     },
48022     //private
48023     setConfirmed : function(){
48024         
48025         
48026         
48027 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48028     },
48029     // private
48030     confirmHandler : function(){
48031         if(!this.getSignature()){
48032             return;
48033         }
48034         
48035         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48036         this.setValue(this.getSignature());
48037         this.isConfirmed = true;
48038         
48039         this.fireEvent('confirm', this);
48040     },
48041     // private
48042     // Subclasses should provide the validation implementation by overriding this
48043     validateValue : function(value){
48044         if(this.allowBlank){
48045             return true;
48046         }
48047         
48048         if(this.isConfirmed){
48049             return true;
48050         }
48051         return false;
48052     }
48053 });/*
48054  * Based on:
48055  * Ext JS Library 1.1.1
48056  * Copyright(c) 2006-2007, Ext JS, LLC.
48057  *
48058  * Originally Released Under LGPL - original licence link has changed is not relivant.
48059  *
48060  * Fork - LGPL
48061  * <script type="text/javascript">
48062  */
48063  
48064
48065 /**
48066  * @class Roo.form.ComboBox
48067  * @extends Roo.form.TriggerField
48068  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48069  * @constructor
48070  * Create a new ComboBox.
48071  * @param {Object} config Configuration options
48072  */
48073 Roo.form.Select = function(config){
48074     Roo.form.Select.superclass.constructor.call(this, config);
48075      
48076 };
48077
48078 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48079     /**
48080      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48081      */
48082     /**
48083      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48084      * rendering into an Roo.Editor, defaults to false)
48085      */
48086     /**
48087      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48088      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48089      */
48090     /**
48091      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48092      */
48093     /**
48094      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48095      * the dropdown list (defaults to undefined, with no header element)
48096      */
48097
48098      /**
48099      * @cfg {String/Roo.Template} tpl The template to use to render the output
48100      */
48101      
48102     // private
48103     defaultAutoCreate : {tag: "select"  },
48104     /**
48105      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48106      */
48107     listWidth: undefined,
48108     /**
48109      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48110      * mode = 'remote' or 'text' if mode = 'local')
48111      */
48112     displayField: undefined,
48113     /**
48114      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48115      * mode = 'remote' or 'value' if mode = 'local'). 
48116      * Note: use of a valueField requires the user make a selection
48117      * in order for a value to be mapped.
48118      */
48119     valueField: undefined,
48120     
48121     
48122     /**
48123      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48124      * field's data value (defaults to the underlying DOM element's name)
48125      */
48126     hiddenName: undefined,
48127     /**
48128      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48129      */
48130     listClass: '',
48131     /**
48132      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48133      */
48134     selectedClass: 'x-combo-selected',
48135     /**
48136      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48137      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48138      * which displays a downward arrow icon).
48139      */
48140     triggerClass : 'x-form-arrow-trigger',
48141     /**
48142      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48143      */
48144     shadow:'sides',
48145     /**
48146      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48147      * anchor positions (defaults to 'tl-bl')
48148      */
48149     listAlign: 'tl-bl?',
48150     /**
48151      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48152      */
48153     maxHeight: 300,
48154     /**
48155      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48156      * query specified by the allQuery config option (defaults to 'query')
48157      */
48158     triggerAction: 'query',
48159     /**
48160      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48161      * (defaults to 4, does not apply if editable = false)
48162      */
48163     minChars : 4,
48164     /**
48165      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48166      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48167      */
48168     typeAhead: false,
48169     /**
48170      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48171      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48172      */
48173     queryDelay: 500,
48174     /**
48175      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48176      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48177      */
48178     pageSize: 0,
48179     /**
48180      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48181      * when editable = true (defaults to false)
48182      */
48183     selectOnFocus:false,
48184     /**
48185      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48186      */
48187     queryParam: 'query',
48188     /**
48189      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48190      * when mode = 'remote' (defaults to 'Loading...')
48191      */
48192     loadingText: 'Loading...',
48193     /**
48194      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48195      */
48196     resizable: false,
48197     /**
48198      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48199      */
48200     handleHeight : 8,
48201     /**
48202      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48203      * traditional select (defaults to true)
48204      */
48205     editable: true,
48206     /**
48207      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48208      */
48209     allQuery: '',
48210     /**
48211      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48212      */
48213     mode: 'remote',
48214     /**
48215      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48216      * listWidth has a higher value)
48217      */
48218     minListWidth : 70,
48219     /**
48220      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48221      * allow the user to set arbitrary text into the field (defaults to false)
48222      */
48223     forceSelection:false,
48224     /**
48225      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48226      * if typeAhead = true (defaults to 250)
48227      */
48228     typeAheadDelay : 250,
48229     /**
48230      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48231      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48232      */
48233     valueNotFoundText : undefined,
48234     
48235     /**
48236      * @cfg {String} defaultValue The value displayed after loading the store.
48237      */
48238     defaultValue: '',
48239     
48240     /**
48241      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48242      */
48243     blockFocus : false,
48244     
48245     /**
48246      * @cfg {Boolean} disableClear Disable showing of clear button.
48247      */
48248     disableClear : false,
48249     /**
48250      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48251      */
48252     alwaysQuery : false,
48253     
48254     //private
48255     addicon : false,
48256     editicon: false,
48257     
48258     // element that contains real text value.. (when hidden is used..)
48259      
48260     // private
48261     onRender : function(ct, position){
48262         Roo.form.Field.prototype.onRender.call(this, ct, position);
48263         
48264         if(this.store){
48265             this.store.on('beforeload', this.onBeforeLoad, this);
48266             this.store.on('load', this.onLoad, this);
48267             this.store.on('loadexception', this.onLoadException, this);
48268             this.store.load({});
48269         }
48270         
48271         
48272         
48273     },
48274
48275     // private
48276     initEvents : function(){
48277         //Roo.form.ComboBox.superclass.initEvents.call(this);
48278  
48279     },
48280
48281     onDestroy : function(){
48282        
48283         if(this.store){
48284             this.store.un('beforeload', this.onBeforeLoad, this);
48285             this.store.un('load', this.onLoad, this);
48286             this.store.un('loadexception', this.onLoadException, this);
48287         }
48288         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48289     },
48290
48291     // private
48292     fireKey : function(e){
48293         if(e.isNavKeyPress() && !this.list.isVisible()){
48294             this.fireEvent("specialkey", this, e);
48295         }
48296     },
48297
48298     // private
48299     onResize: function(w, h){
48300         
48301         return; 
48302     
48303         
48304     },
48305
48306     /**
48307      * Allow or prevent the user from directly editing the field text.  If false is passed,
48308      * the user will only be able to select from the items defined in the dropdown list.  This method
48309      * is the runtime equivalent of setting the 'editable' config option at config time.
48310      * @param {Boolean} value True to allow the user to directly edit the field text
48311      */
48312     setEditable : function(value){
48313          
48314     },
48315
48316     // private
48317     onBeforeLoad : function(){
48318         
48319         Roo.log("Select before load");
48320         return;
48321     
48322         this.innerList.update(this.loadingText ?
48323                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48324         //this.restrictHeight();
48325         this.selectedIndex = -1;
48326     },
48327
48328     // private
48329     onLoad : function(){
48330
48331     
48332         var dom = this.el.dom;
48333         dom.innerHTML = '';
48334          var od = dom.ownerDocument;
48335          
48336         if (this.emptyText) {
48337             var op = od.createElement('option');
48338             op.setAttribute('value', '');
48339             op.innerHTML = String.format('{0}', this.emptyText);
48340             dom.appendChild(op);
48341         }
48342         if(this.store.getCount() > 0){
48343            
48344             var vf = this.valueField;
48345             var df = this.displayField;
48346             this.store.data.each(function(r) {
48347                 // which colmsn to use... testing - cdoe / title..
48348                 var op = od.createElement('option');
48349                 op.setAttribute('value', r.data[vf]);
48350                 op.innerHTML = String.format('{0}', r.data[df]);
48351                 dom.appendChild(op);
48352             });
48353             if (typeof(this.defaultValue != 'undefined')) {
48354                 this.setValue(this.defaultValue);
48355             }
48356             
48357              
48358         }else{
48359             //this.onEmptyResults();
48360         }
48361         //this.el.focus();
48362     },
48363     // private
48364     onLoadException : function()
48365     {
48366         dom.innerHTML = '';
48367             
48368         Roo.log("Select on load exception");
48369         return;
48370     
48371         this.collapse();
48372         Roo.log(this.store.reader.jsonData);
48373         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48374             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48375         }
48376         
48377         
48378     },
48379     // private
48380     onTypeAhead : function(){
48381          
48382     },
48383
48384     // private
48385     onSelect : function(record, index){
48386         Roo.log('on select?');
48387         return;
48388         if(this.fireEvent('beforeselect', this, record, index) !== false){
48389             this.setFromData(index > -1 ? record.data : false);
48390             this.collapse();
48391             this.fireEvent('select', this, record, index);
48392         }
48393     },
48394
48395     /**
48396      * Returns the currently selected field value or empty string if no value is set.
48397      * @return {String} value The selected value
48398      */
48399     getValue : function(){
48400         var dom = this.el.dom;
48401         this.value = dom.options[dom.selectedIndex].value;
48402         return this.value;
48403         
48404     },
48405
48406     /**
48407      * Clears any text/value currently set in the field
48408      */
48409     clearValue : function(){
48410         this.value = '';
48411         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48412         
48413     },
48414
48415     /**
48416      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48417      * will be displayed in the field.  If the value does not match the data value of an existing item,
48418      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48419      * Otherwise the field will be blank (although the value will still be set).
48420      * @param {String} value The value to match
48421      */
48422     setValue : function(v){
48423         var d = this.el.dom;
48424         for (var i =0; i < d.options.length;i++) {
48425             if (v == d.options[i].value) {
48426                 d.selectedIndex = i;
48427                 this.value = v;
48428                 return;
48429             }
48430         }
48431         this.clearValue();
48432     },
48433     /**
48434      * @property {Object} the last set data for the element
48435      */
48436     
48437     lastData : false,
48438     /**
48439      * Sets the value of the field based on a object which is related to the record format for the store.
48440      * @param {Object} value the value to set as. or false on reset?
48441      */
48442     setFromData : function(o){
48443         Roo.log('setfrom data?');
48444          
48445         
48446         
48447     },
48448     // private
48449     reset : function(){
48450         this.clearValue();
48451     },
48452     // private
48453     findRecord : function(prop, value){
48454         
48455         return false;
48456     
48457         var record;
48458         if(this.store.getCount() > 0){
48459             this.store.each(function(r){
48460                 if(r.data[prop] == value){
48461                     record = r;
48462                     return false;
48463                 }
48464                 return true;
48465             });
48466         }
48467         return record;
48468     },
48469     
48470     getName: function()
48471     {
48472         // returns hidden if it's set..
48473         if (!this.rendered) {return ''};
48474         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48475         
48476     },
48477      
48478
48479     
48480
48481     // private
48482     onEmptyResults : function(){
48483         Roo.log('empty results');
48484         //this.collapse();
48485     },
48486
48487     /**
48488      * Returns true if the dropdown list is expanded, else false.
48489      */
48490     isExpanded : function(){
48491         return false;
48492     },
48493
48494     /**
48495      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48496      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48497      * @param {String} value The data value of the item to select
48498      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48499      * selected item if it is not currently in view (defaults to true)
48500      * @return {Boolean} True if the value matched an item in the list, else false
48501      */
48502     selectByValue : function(v, scrollIntoView){
48503         Roo.log('select By Value');
48504         return false;
48505     
48506         if(v !== undefined && v !== null){
48507             var r = this.findRecord(this.valueField || this.displayField, v);
48508             if(r){
48509                 this.select(this.store.indexOf(r), scrollIntoView);
48510                 return true;
48511             }
48512         }
48513         return false;
48514     },
48515
48516     /**
48517      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48518      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48519      * @param {Number} index The zero-based index of the list item to select
48520      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48521      * selected item if it is not currently in view (defaults to true)
48522      */
48523     select : function(index, scrollIntoView){
48524         Roo.log('select ');
48525         return  ;
48526         
48527         this.selectedIndex = index;
48528         this.view.select(index);
48529         if(scrollIntoView !== false){
48530             var el = this.view.getNode(index);
48531             if(el){
48532                 this.innerList.scrollChildIntoView(el, false);
48533             }
48534         }
48535     },
48536
48537       
48538
48539     // private
48540     validateBlur : function(){
48541         
48542         return;
48543         
48544     },
48545
48546     // private
48547     initQuery : function(){
48548         this.doQuery(this.getRawValue());
48549     },
48550
48551     // private
48552     doForce : function(){
48553         if(this.el.dom.value.length > 0){
48554             this.el.dom.value =
48555                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48556              
48557         }
48558     },
48559
48560     /**
48561      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48562      * query allowing the query action to be canceled if needed.
48563      * @param {String} query The SQL query to execute
48564      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48565      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48566      * saved in the current store (defaults to false)
48567      */
48568     doQuery : function(q, forceAll){
48569         
48570         Roo.log('doQuery?');
48571         if(q === undefined || q === null){
48572             q = '';
48573         }
48574         var qe = {
48575             query: q,
48576             forceAll: forceAll,
48577             combo: this,
48578             cancel:false
48579         };
48580         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48581             return false;
48582         }
48583         q = qe.query;
48584         forceAll = qe.forceAll;
48585         if(forceAll === true || (q.length >= this.minChars)){
48586             if(this.lastQuery != q || this.alwaysQuery){
48587                 this.lastQuery = q;
48588                 if(this.mode == 'local'){
48589                     this.selectedIndex = -1;
48590                     if(forceAll){
48591                         this.store.clearFilter();
48592                     }else{
48593                         this.store.filter(this.displayField, q);
48594                     }
48595                     this.onLoad();
48596                 }else{
48597                     this.store.baseParams[this.queryParam] = q;
48598                     this.store.load({
48599                         params: this.getParams(q)
48600                     });
48601                     this.expand();
48602                 }
48603             }else{
48604                 this.selectedIndex = -1;
48605                 this.onLoad();   
48606             }
48607         }
48608     },
48609
48610     // private
48611     getParams : function(q){
48612         var p = {};
48613         //p[this.queryParam] = q;
48614         if(this.pageSize){
48615             p.start = 0;
48616             p.limit = this.pageSize;
48617         }
48618         return p;
48619     },
48620
48621     /**
48622      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48623      */
48624     collapse : function(){
48625         
48626     },
48627
48628     // private
48629     collapseIf : function(e){
48630         
48631     },
48632
48633     /**
48634      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48635      */
48636     expand : function(){
48637         
48638     } ,
48639
48640     // private
48641      
48642
48643     /** 
48644     * @cfg {Boolean} grow 
48645     * @hide 
48646     */
48647     /** 
48648     * @cfg {Number} growMin 
48649     * @hide 
48650     */
48651     /** 
48652     * @cfg {Number} growMax 
48653     * @hide 
48654     */
48655     /**
48656      * @hide
48657      * @method autoSize
48658      */
48659     
48660     setWidth : function()
48661     {
48662         
48663     },
48664     getResizeEl : function(){
48665         return this.el;
48666     }
48667 });//<script type="text/javasscript">
48668  
48669
48670 /**
48671  * @class Roo.DDView
48672  * A DnD enabled version of Roo.View.
48673  * @param {Element/String} container The Element in which to create the View.
48674  * @param {String} tpl The template string used to create the markup for each element of the View
48675  * @param {Object} config The configuration properties. These include all the config options of
48676  * {@link Roo.View} plus some specific to this class.<br>
48677  * <p>
48678  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48679  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48680  * <p>
48681  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48682 .x-view-drag-insert-above {
48683         border-top:1px dotted #3366cc;
48684 }
48685 .x-view-drag-insert-below {
48686         border-bottom:1px dotted #3366cc;
48687 }
48688 </code></pre>
48689  * 
48690  */
48691  
48692 Roo.DDView = function(container, tpl, config) {
48693     Roo.DDView.superclass.constructor.apply(this, arguments);
48694     this.getEl().setStyle("outline", "0px none");
48695     this.getEl().unselectable();
48696     if (this.dragGroup) {
48697                 this.setDraggable(this.dragGroup.split(","));
48698     }
48699     if (this.dropGroup) {
48700                 this.setDroppable(this.dropGroup.split(","));
48701     }
48702     if (this.deletable) {
48703         this.setDeletable();
48704     }
48705     this.isDirtyFlag = false;
48706         this.addEvents({
48707                 "drop" : true
48708         });
48709 };
48710
48711 Roo.extend(Roo.DDView, Roo.View, {
48712 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48713 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48714 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48715 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48716
48717         isFormField: true,
48718
48719         reset: Roo.emptyFn,
48720         
48721         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48722
48723         validate: function() {
48724                 return true;
48725         },
48726         
48727         destroy: function() {
48728                 this.purgeListeners();
48729                 this.getEl.removeAllListeners();
48730                 this.getEl().remove();
48731                 if (this.dragZone) {
48732                         if (this.dragZone.destroy) {
48733                                 this.dragZone.destroy();
48734                         }
48735                 }
48736                 if (this.dropZone) {
48737                         if (this.dropZone.destroy) {
48738                                 this.dropZone.destroy();
48739                         }
48740                 }
48741         },
48742
48743 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48744         getName: function() {
48745                 return this.name;
48746         },
48747
48748 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48749         setValue: function(v) {
48750                 if (!this.store) {
48751                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48752                 }
48753                 var data = {};
48754                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48755                 this.store.proxy = new Roo.data.MemoryProxy(data);
48756                 this.store.load();
48757         },
48758
48759 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48760         getValue: function() {
48761                 var result = '(';
48762                 this.store.each(function(rec) {
48763                         result += rec.id + ',';
48764                 });
48765                 return result.substr(0, result.length - 1) + ')';
48766         },
48767         
48768         getIds: function() {
48769                 var i = 0, result = new Array(this.store.getCount());
48770                 this.store.each(function(rec) {
48771                         result[i++] = rec.id;
48772                 });
48773                 return result;
48774         },
48775         
48776         isDirty: function() {
48777                 return this.isDirtyFlag;
48778         },
48779
48780 /**
48781  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48782  *      whole Element becomes the target, and this causes the drop gesture to append.
48783  */
48784     getTargetFromEvent : function(e) {
48785                 var target = e.getTarget();
48786                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48787                 target = target.parentNode;
48788                 }
48789                 if (!target) {
48790                         target = this.el.dom.lastChild || this.el.dom;
48791                 }
48792                 return target;
48793     },
48794
48795 /**
48796  *      Create the drag data which consists of an object which has the property "ddel" as
48797  *      the drag proxy element. 
48798  */
48799     getDragData : function(e) {
48800         var target = this.findItemFromChild(e.getTarget());
48801                 if(target) {
48802                         this.handleSelection(e);
48803                         var selNodes = this.getSelectedNodes();
48804             var dragData = {
48805                 source: this,
48806                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48807                 nodes: selNodes,
48808                 records: []
48809                         };
48810                         var selectedIndices = this.getSelectedIndexes();
48811                         for (var i = 0; i < selectedIndices.length; i++) {
48812                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48813                         }
48814                         if (selNodes.length == 1) {
48815                                 dragData.ddel = target.cloneNode(true); // the div element
48816                         } else {
48817                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48818                                 div.className = 'multi-proxy';
48819                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48820                                         div.appendChild(selNodes[i].cloneNode(true));
48821                                 }
48822                                 dragData.ddel = div;
48823                         }
48824             //console.log(dragData)
48825             //console.log(dragData.ddel.innerHTML)
48826                         return dragData;
48827                 }
48828         //console.log('nodragData')
48829                 return false;
48830     },
48831     
48832 /**     Specify to which ddGroup items in this DDView may be dragged. */
48833     setDraggable: function(ddGroup) {
48834         if (ddGroup instanceof Array) {
48835                 Roo.each(ddGroup, this.setDraggable, this);
48836                 return;
48837         }
48838         if (this.dragZone) {
48839                 this.dragZone.addToGroup(ddGroup);
48840         } else {
48841                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48842                                 containerScroll: true,
48843                                 ddGroup: ddGroup 
48844
48845                         });
48846 //                      Draggability implies selection. DragZone's mousedown selects the element.
48847                         if (!this.multiSelect) { this.singleSelect = true; }
48848
48849 //                      Wire the DragZone's handlers up to methods in *this*
48850                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48851                 }
48852     },
48853
48854 /**     Specify from which ddGroup this DDView accepts drops. */
48855     setDroppable: function(ddGroup) {
48856         if (ddGroup instanceof Array) {
48857                 Roo.each(ddGroup, this.setDroppable, this);
48858                 return;
48859         }
48860         if (this.dropZone) {
48861                 this.dropZone.addToGroup(ddGroup);
48862         } else {
48863                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48864                                 containerScroll: true,
48865                                 ddGroup: ddGroup
48866                         });
48867
48868 //                      Wire the DropZone's handlers up to methods in *this*
48869                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48870                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48871                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48872                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48873                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48874                 }
48875     },
48876
48877 /**     Decide whether to drop above or below a View node. */
48878     getDropPoint : function(e, n, dd){
48879         if (n == this.el.dom) { return "above"; }
48880                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48881                 var c = t + (b - t) / 2;
48882                 var y = Roo.lib.Event.getPageY(e);
48883                 if(y <= c) {
48884                         return "above";
48885                 }else{
48886                         return "below";
48887                 }
48888     },
48889
48890     onNodeEnter : function(n, dd, e, data){
48891                 return false;
48892     },
48893     
48894     onNodeOver : function(n, dd, e, data){
48895                 var pt = this.getDropPoint(e, n, dd);
48896                 // set the insert point style on the target node
48897                 var dragElClass = this.dropNotAllowed;
48898                 if (pt) {
48899                         var targetElClass;
48900                         if (pt == "above"){
48901                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48902                                 targetElClass = "x-view-drag-insert-above";
48903                         } else {
48904                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48905                                 targetElClass = "x-view-drag-insert-below";
48906                         }
48907                         if (this.lastInsertClass != targetElClass){
48908                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48909                                 this.lastInsertClass = targetElClass;
48910                         }
48911                 }
48912                 return dragElClass;
48913         },
48914
48915     onNodeOut : function(n, dd, e, data){
48916                 this.removeDropIndicators(n);
48917     },
48918
48919     onNodeDrop : function(n, dd, e, data){
48920         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48921                 return false;
48922         }
48923         var pt = this.getDropPoint(e, n, dd);
48924                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48925                 if (pt == "below") { insertAt++; }
48926                 for (var i = 0; i < data.records.length; i++) {
48927                         var r = data.records[i];
48928                         var dup = this.store.getById(r.id);
48929                         if (dup && (dd != this.dragZone)) {
48930                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48931                         } else {
48932                                 if (data.copy) {
48933                                         this.store.insert(insertAt++, r.copy());
48934                                 } else {
48935                                         data.source.isDirtyFlag = true;
48936                                         r.store.remove(r);
48937                                         this.store.insert(insertAt++, r);
48938                                 }
48939                                 this.isDirtyFlag = true;
48940                         }
48941                 }
48942                 this.dragZone.cachedTarget = null;
48943                 return true;
48944     },
48945
48946     removeDropIndicators : function(n){
48947                 if(n){
48948                         Roo.fly(n).removeClass([
48949                                 "x-view-drag-insert-above",
48950                                 "x-view-drag-insert-below"]);
48951                         this.lastInsertClass = "_noclass";
48952                 }
48953     },
48954
48955 /**
48956  *      Utility method. Add a delete option to the DDView's context menu.
48957  *      @param {String} imageUrl The URL of the "delete" icon image.
48958  */
48959         setDeletable: function(imageUrl) {
48960                 if (!this.singleSelect && !this.multiSelect) {
48961                         this.singleSelect = true;
48962                 }
48963                 var c = this.getContextMenu();
48964                 this.contextMenu.on("itemclick", function(item) {
48965                         switch (item.id) {
48966                                 case "delete":
48967                                         this.remove(this.getSelectedIndexes());
48968                                         break;
48969                         }
48970                 }, this);
48971                 this.contextMenu.add({
48972                         icon: imageUrl,
48973                         id: "delete",
48974                         text: 'Delete'
48975                 });
48976         },
48977         
48978 /**     Return the context menu for this DDView. */
48979         getContextMenu: function() {
48980                 if (!this.contextMenu) {
48981 //                      Create the View's context menu
48982                         this.contextMenu = new Roo.menu.Menu({
48983                                 id: this.id + "-contextmenu"
48984                         });
48985                         this.el.on("contextmenu", this.showContextMenu, this);
48986                 }
48987                 return this.contextMenu;
48988         },
48989         
48990         disableContextMenu: function() {
48991                 if (this.contextMenu) {
48992                         this.el.un("contextmenu", this.showContextMenu, this);
48993                 }
48994         },
48995
48996         showContextMenu: function(e, item) {
48997         item = this.findItemFromChild(e.getTarget());
48998                 if (item) {
48999                         e.stopEvent();
49000                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
49001                         this.contextMenu.showAt(e.getXY());
49002             }
49003     },
49004
49005 /**
49006  *      Remove {@link Roo.data.Record}s at the specified indices.
49007  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49008  */
49009     remove: function(selectedIndices) {
49010                 selectedIndices = [].concat(selectedIndices);
49011                 for (var i = 0; i < selectedIndices.length; i++) {
49012                         var rec = this.store.getAt(selectedIndices[i]);
49013                         this.store.remove(rec);
49014                 }
49015     },
49016
49017 /**
49018  *      Double click fires the event, but also, if this is draggable, and there is only one other
49019  *      related DropZone, it transfers the selected node.
49020  */
49021     onDblClick : function(e){
49022         var item = this.findItemFromChild(e.getTarget());
49023         if(item){
49024             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49025                 return false;
49026             }
49027             if (this.dragGroup) {
49028                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49029                     while (targets.indexOf(this.dropZone) > -1) {
49030                             targets.remove(this.dropZone);
49031                                 }
49032                     if (targets.length == 1) {
49033                                         this.dragZone.cachedTarget = null;
49034                         var el = Roo.get(targets[0].getEl());
49035                         var box = el.getBox(true);
49036                         targets[0].onNodeDrop(el.dom, {
49037                                 target: el.dom,
49038                                 xy: [box.x, box.y + box.height - 1]
49039                         }, null, this.getDragData(e));
49040                     }
49041                 }
49042         }
49043     },
49044     
49045     handleSelection: function(e) {
49046                 this.dragZone.cachedTarget = null;
49047         var item = this.findItemFromChild(e.getTarget());
49048         if (!item) {
49049                 this.clearSelections(true);
49050                 return;
49051         }
49052                 if (item && (this.multiSelect || this.singleSelect)){
49053                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49054                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49055                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49056                                 this.unselect(item);
49057                         } else {
49058                                 this.select(item, this.multiSelect && e.ctrlKey);
49059                                 this.lastSelection = item;
49060                         }
49061                 }
49062     },
49063
49064     onItemClick : function(item, index, e){
49065                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49066                         return false;
49067                 }
49068                 return true;
49069     },
49070
49071     unselect : function(nodeInfo, suppressEvent){
49072                 var node = this.getNode(nodeInfo);
49073                 if(node && this.isSelected(node)){
49074                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49075                                 Roo.fly(node).removeClass(this.selectedClass);
49076                                 this.selections.remove(node);
49077                                 if(!suppressEvent){
49078                                         this.fireEvent("selectionchange", this, this.selections);
49079                                 }
49080                         }
49081                 }
49082     }
49083 });
49084 /*
49085  * Based on:
49086  * Ext JS Library 1.1.1
49087  * Copyright(c) 2006-2007, Ext JS, LLC.
49088  *
49089  * Originally Released Under LGPL - original licence link has changed is not relivant.
49090  *
49091  * Fork - LGPL
49092  * <script type="text/javascript">
49093  */
49094  
49095 /**
49096  * @class Roo.LayoutManager
49097  * @extends Roo.util.Observable
49098  * Base class for layout managers.
49099  */
49100 Roo.LayoutManager = function(container, config){
49101     Roo.LayoutManager.superclass.constructor.call(this);
49102     this.el = Roo.get(container);
49103     // ie scrollbar fix
49104     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49105         document.body.scroll = "no";
49106     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49107         this.el.position('relative');
49108     }
49109     this.id = this.el.id;
49110     this.el.addClass("x-layout-container");
49111     /** false to disable window resize monitoring @type Boolean */
49112     this.monitorWindowResize = true;
49113     this.regions = {};
49114     this.addEvents({
49115         /**
49116          * @event layout
49117          * Fires when a layout is performed. 
49118          * @param {Roo.LayoutManager} this
49119          */
49120         "layout" : true,
49121         /**
49122          * @event regionresized
49123          * Fires when the user resizes a region. 
49124          * @param {Roo.LayoutRegion} region The resized region
49125          * @param {Number} newSize The new size (width for east/west, height for north/south)
49126          */
49127         "regionresized" : true,
49128         /**
49129          * @event regioncollapsed
49130          * Fires when a region is collapsed. 
49131          * @param {Roo.LayoutRegion} region The collapsed region
49132          */
49133         "regioncollapsed" : true,
49134         /**
49135          * @event regionexpanded
49136          * Fires when a region is expanded.  
49137          * @param {Roo.LayoutRegion} region The expanded region
49138          */
49139         "regionexpanded" : true
49140     });
49141     this.updating = false;
49142     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49143 };
49144
49145 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49146     /**
49147      * Returns true if this layout is currently being updated
49148      * @return {Boolean}
49149      */
49150     isUpdating : function(){
49151         return this.updating; 
49152     },
49153     
49154     /**
49155      * Suspend the LayoutManager from doing auto-layouts while
49156      * making multiple add or remove calls
49157      */
49158     beginUpdate : function(){
49159         this.updating = true;    
49160     },
49161     
49162     /**
49163      * Restore auto-layouts and optionally disable the manager from performing a layout
49164      * @param {Boolean} noLayout true to disable a layout update 
49165      */
49166     endUpdate : function(noLayout){
49167         this.updating = false;
49168         if(!noLayout){
49169             this.layout();
49170         }    
49171     },
49172     
49173     layout: function(){
49174         
49175     },
49176     
49177     onRegionResized : function(region, newSize){
49178         this.fireEvent("regionresized", region, newSize);
49179         this.layout();
49180     },
49181     
49182     onRegionCollapsed : function(region){
49183         this.fireEvent("regioncollapsed", region);
49184     },
49185     
49186     onRegionExpanded : function(region){
49187         this.fireEvent("regionexpanded", region);
49188     },
49189         
49190     /**
49191      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49192      * performs box-model adjustments.
49193      * @return {Object} The size as an object {width: (the width), height: (the height)}
49194      */
49195     getViewSize : function(){
49196         var size;
49197         if(this.el.dom != document.body){
49198             size = this.el.getSize();
49199         }else{
49200             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49201         }
49202         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49203         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49204         return size;
49205     },
49206     
49207     /**
49208      * Returns the Element this layout is bound to.
49209      * @return {Roo.Element}
49210      */
49211     getEl : function(){
49212         return this.el;
49213     },
49214     
49215     /**
49216      * Returns the specified region.
49217      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49218      * @return {Roo.LayoutRegion}
49219      */
49220     getRegion : function(target){
49221         return this.regions[target.toLowerCase()];
49222     },
49223     
49224     onWindowResize : function(){
49225         if(this.monitorWindowResize){
49226             this.layout();
49227         }
49228     }
49229 });/*
49230  * Based on:
49231  * Ext JS Library 1.1.1
49232  * Copyright(c) 2006-2007, Ext JS, LLC.
49233  *
49234  * Originally Released Under LGPL - original licence link has changed is not relivant.
49235  *
49236  * Fork - LGPL
49237  * <script type="text/javascript">
49238  */
49239 /**
49240  * @class Roo.BorderLayout
49241  * @extends Roo.LayoutManager
49242  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49243  * please see: <br><br>
49244  * <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>
49245  * <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>
49246  * Example:
49247  <pre><code>
49248  var layout = new Roo.BorderLayout(document.body, {
49249     north: {
49250         initialSize: 25,
49251         titlebar: false
49252     },
49253     west: {
49254         split:true,
49255         initialSize: 200,
49256         minSize: 175,
49257         maxSize: 400,
49258         titlebar: true,
49259         collapsible: true
49260     },
49261     east: {
49262         split:true,
49263         initialSize: 202,
49264         minSize: 175,
49265         maxSize: 400,
49266         titlebar: true,
49267         collapsible: true
49268     },
49269     south: {
49270         split:true,
49271         initialSize: 100,
49272         minSize: 100,
49273         maxSize: 200,
49274         titlebar: true,
49275         collapsible: true
49276     },
49277     center: {
49278         titlebar: true,
49279         autoScroll:true,
49280         resizeTabs: true,
49281         minTabWidth: 50,
49282         preferredTabWidth: 150
49283     }
49284 });
49285
49286 // shorthand
49287 var CP = Roo.ContentPanel;
49288
49289 layout.beginUpdate();
49290 layout.add("north", new CP("north", "North"));
49291 layout.add("south", new CP("south", {title: "South", closable: true}));
49292 layout.add("west", new CP("west", {title: "West"}));
49293 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49294 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49295 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49296 layout.getRegion("center").showPanel("center1");
49297 layout.endUpdate();
49298 </code></pre>
49299
49300 <b>The container the layout is rendered into can be either the body element or any other element.
49301 If it is not the body element, the container needs to either be an absolute positioned element,
49302 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49303 the container size if it is not the body element.</b>
49304
49305 * @constructor
49306 * Create a new BorderLayout
49307 * @param {String/HTMLElement/Element} container The container this layout is bound to
49308 * @param {Object} config Configuration options
49309  */
49310 Roo.BorderLayout = function(container, config){
49311     config = config || {};
49312     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49313     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49314     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49315         var target = this.factory.validRegions[i];
49316         if(config[target]){
49317             this.addRegion(target, config[target]);
49318         }
49319     }
49320 };
49321
49322 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49323     /**
49324      * Creates and adds a new region if it doesn't already exist.
49325      * @param {String} target The target region key (north, south, east, west or center).
49326      * @param {Object} config The regions config object
49327      * @return {BorderLayoutRegion} The new region
49328      */
49329     addRegion : function(target, config){
49330         if(!this.regions[target]){
49331             var r = this.factory.create(target, this, config);
49332             this.bindRegion(target, r);
49333         }
49334         return this.regions[target];
49335     },
49336
49337     // private (kinda)
49338     bindRegion : function(name, r){
49339         this.regions[name] = r;
49340         r.on("visibilitychange", this.layout, this);
49341         r.on("paneladded", this.layout, this);
49342         r.on("panelremoved", this.layout, this);
49343         r.on("invalidated", this.layout, this);
49344         r.on("resized", this.onRegionResized, this);
49345         r.on("collapsed", this.onRegionCollapsed, this);
49346         r.on("expanded", this.onRegionExpanded, this);
49347     },
49348
49349     /**
49350      * Performs a layout update.
49351      */
49352     layout : function(){
49353         if(this.updating) return;
49354         var size = this.getViewSize();
49355         var w = size.width;
49356         var h = size.height;
49357         var centerW = w;
49358         var centerH = h;
49359         var centerY = 0;
49360         var centerX = 0;
49361         //var x = 0, y = 0;
49362
49363         var rs = this.regions;
49364         var north = rs["north"];
49365         var south = rs["south"]; 
49366         var west = rs["west"];
49367         var east = rs["east"];
49368         var center = rs["center"];
49369         //if(this.hideOnLayout){ // not supported anymore
49370             //c.el.setStyle("display", "none");
49371         //}
49372         if(north && north.isVisible()){
49373             var b = north.getBox();
49374             var m = north.getMargins();
49375             b.width = w - (m.left+m.right);
49376             b.x = m.left;
49377             b.y = m.top;
49378             centerY = b.height + b.y + m.bottom;
49379             centerH -= centerY;
49380             north.updateBox(this.safeBox(b));
49381         }
49382         if(south && south.isVisible()){
49383             var b = south.getBox();
49384             var m = south.getMargins();
49385             b.width = w - (m.left+m.right);
49386             b.x = m.left;
49387             var totalHeight = (b.height + m.top + m.bottom);
49388             b.y = h - totalHeight + m.top;
49389             centerH -= totalHeight;
49390             south.updateBox(this.safeBox(b));
49391         }
49392         if(west && west.isVisible()){
49393             var b = west.getBox();
49394             var m = west.getMargins();
49395             b.height = centerH - (m.top+m.bottom);
49396             b.x = m.left;
49397             b.y = centerY + m.top;
49398             var totalWidth = (b.width + m.left + m.right);
49399             centerX += totalWidth;
49400             centerW -= totalWidth;
49401             west.updateBox(this.safeBox(b));
49402         }
49403         if(east && east.isVisible()){
49404             var b = east.getBox();
49405             var m = east.getMargins();
49406             b.height = centerH - (m.top+m.bottom);
49407             var totalWidth = (b.width + m.left + m.right);
49408             b.x = w - totalWidth + m.left;
49409             b.y = centerY + m.top;
49410             centerW -= totalWidth;
49411             east.updateBox(this.safeBox(b));
49412         }
49413         if(center){
49414             var m = center.getMargins();
49415             var centerBox = {
49416                 x: centerX + m.left,
49417                 y: centerY + m.top,
49418                 width: centerW - (m.left+m.right),
49419                 height: centerH - (m.top+m.bottom)
49420             };
49421             //if(this.hideOnLayout){
49422                 //center.el.setStyle("display", "block");
49423             //}
49424             center.updateBox(this.safeBox(centerBox));
49425         }
49426         this.el.repaint();
49427         this.fireEvent("layout", this);
49428     },
49429
49430     // private
49431     safeBox : function(box){
49432         box.width = Math.max(0, box.width);
49433         box.height = Math.max(0, box.height);
49434         return box;
49435     },
49436
49437     /**
49438      * Adds a ContentPanel (or subclass) to this layout.
49439      * @param {String} target The target region key (north, south, east, west or center).
49440      * @param {Roo.ContentPanel} panel The panel to add
49441      * @return {Roo.ContentPanel} The added panel
49442      */
49443     add : function(target, panel){
49444          
49445         target = target.toLowerCase();
49446         return this.regions[target].add(panel);
49447     },
49448
49449     /**
49450      * Remove a ContentPanel (or subclass) to this layout.
49451      * @param {String} target The target region key (north, south, east, west or center).
49452      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49453      * @return {Roo.ContentPanel} The removed panel
49454      */
49455     remove : function(target, panel){
49456         target = target.toLowerCase();
49457         return this.regions[target].remove(panel);
49458     },
49459
49460     /**
49461      * Searches all regions for a panel with the specified id
49462      * @param {String} panelId
49463      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49464      */
49465     findPanel : function(panelId){
49466         var rs = this.regions;
49467         for(var target in rs){
49468             if(typeof rs[target] != "function"){
49469                 var p = rs[target].getPanel(panelId);
49470                 if(p){
49471                     return p;
49472                 }
49473             }
49474         }
49475         return null;
49476     },
49477
49478     /**
49479      * Searches all regions for a panel with the specified id and activates (shows) it.
49480      * @param {String/ContentPanel} panelId The panels id or the panel itself
49481      * @return {Roo.ContentPanel} The shown panel or null
49482      */
49483     showPanel : function(panelId) {
49484       var rs = this.regions;
49485       for(var target in rs){
49486          var r = rs[target];
49487          if(typeof r != "function"){
49488             if(r.hasPanel(panelId)){
49489                return r.showPanel(panelId);
49490             }
49491          }
49492       }
49493       return null;
49494    },
49495
49496    /**
49497      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49498      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49499      */
49500     restoreState : function(provider){
49501         if(!provider){
49502             provider = Roo.state.Manager;
49503         }
49504         var sm = new Roo.LayoutStateManager();
49505         sm.init(this, provider);
49506     },
49507
49508     /**
49509      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49510      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49511      * a valid ContentPanel config object.  Example:
49512      * <pre><code>
49513 // Create the main layout
49514 var layout = new Roo.BorderLayout('main-ct', {
49515     west: {
49516         split:true,
49517         minSize: 175,
49518         titlebar: true
49519     },
49520     center: {
49521         title:'Components'
49522     }
49523 }, 'main-ct');
49524
49525 // Create and add multiple ContentPanels at once via configs
49526 layout.batchAdd({
49527    west: {
49528        id: 'source-files',
49529        autoCreate:true,
49530        title:'Ext Source Files',
49531        autoScroll:true,
49532        fitToFrame:true
49533    },
49534    center : {
49535        el: cview,
49536        autoScroll:true,
49537        fitToFrame:true,
49538        toolbar: tb,
49539        resizeEl:'cbody'
49540    }
49541 });
49542 </code></pre>
49543      * @param {Object} regions An object containing ContentPanel configs by region name
49544      */
49545     batchAdd : function(regions){
49546         this.beginUpdate();
49547         for(var rname in regions){
49548             var lr = this.regions[rname];
49549             if(lr){
49550                 this.addTypedPanels(lr, regions[rname]);
49551             }
49552         }
49553         this.endUpdate();
49554     },
49555
49556     // private
49557     addTypedPanels : function(lr, ps){
49558         if(typeof ps == 'string'){
49559             lr.add(new Roo.ContentPanel(ps));
49560         }
49561         else if(ps instanceof Array){
49562             for(var i =0, len = ps.length; i < len; i++){
49563                 this.addTypedPanels(lr, ps[i]);
49564             }
49565         }
49566         else if(!ps.events){ // raw config?
49567             var el = ps.el;
49568             delete ps.el; // prevent conflict
49569             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49570         }
49571         else {  // panel object assumed!
49572             lr.add(ps);
49573         }
49574     },
49575     /**
49576      * Adds a xtype elements to the layout.
49577      * <pre><code>
49578
49579 layout.addxtype({
49580        xtype : 'ContentPanel',
49581        region: 'west',
49582        items: [ .... ]
49583    }
49584 );
49585
49586 layout.addxtype({
49587         xtype : 'NestedLayoutPanel',
49588         region: 'west',
49589         layout: {
49590            center: { },
49591            west: { }   
49592         },
49593         items : [ ... list of content panels or nested layout panels.. ]
49594    }
49595 );
49596 </code></pre>
49597      * @param {Object} cfg Xtype definition of item to add.
49598      */
49599     addxtype : function(cfg)
49600     {
49601         // basically accepts a pannel...
49602         // can accept a layout region..!?!?
49603         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49604         
49605         if (!cfg.xtype.match(/Panel$/)) {
49606             return false;
49607         }
49608         var ret = false;
49609         
49610         if (typeof(cfg.region) == 'undefined') {
49611             Roo.log("Failed to add Panel, region was not set");
49612             Roo.log(cfg);
49613             return false;
49614         }
49615         var region = cfg.region;
49616         delete cfg.region;
49617         
49618           
49619         var xitems = [];
49620         if (cfg.items) {
49621             xitems = cfg.items;
49622             delete cfg.items;
49623         }
49624         var nb = false;
49625         
49626         switch(cfg.xtype) 
49627         {
49628             case 'ContentPanel':  // ContentPanel (el, cfg)
49629             case 'ScrollPanel':  // ContentPanel (el, cfg)
49630             case 'ViewPanel': 
49631                 if(cfg.autoCreate) {
49632                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49633                 } else {
49634                     var el = this.el.createChild();
49635                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49636                 }
49637                 
49638                 this.add(region, ret);
49639                 break;
49640             
49641             
49642             case 'TreePanel': // our new panel!
49643                 cfg.el = this.el.createChild();
49644                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49645                 this.add(region, ret);
49646                 break;
49647             
49648             case 'NestedLayoutPanel': 
49649                 // create a new Layout (which is  a Border Layout...
49650                 var el = this.el.createChild();
49651                 var clayout = cfg.layout;
49652                 delete cfg.layout;
49653                 clayout.items   = clayout.items  || [];
49654                 // replace this exitems with the clayout ones..
49655                 xitems = clayout.items;
49656                  
49657                 
49658                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49659                     cfg.background = false;
49660                 }
49661                 var layout = new Roo.BorderLayout(el, clayout);
49662                 
49663                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49664                 //console.log('adding nested layout panel '  + cfg.toSource());
49665                 this.add(region, ret);
49666                 nb = {}; /// find first...
49667                 break;
49668                 
49669             case 'GridPanel': 
49670             
49671                 // needs grid and region
49672                 
49673                 //var el = this.getRegion(region).el.createChild();
49674                 var el = this.el.createChild();
49675                 // create the grid first...
49676                 
49677                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49678                 delete cfg.grid;
49679                 if (region == 'center' && this.active ) {
49680                     cfg.background = false;
49681                 }
49682                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49683                 
49684                 this.add(region, ret);
49685                 if (cfg.background) {
49686                     ret.on('activate', function(gp) {
49687                         if (!gp.grid.rendered) {
49688                             gp.grid.render();
49689                         }
49690                     });
49691                 } else {
49692                     grid.render();
49693                 }
49694                 break;
49695            
49696            
49697            
49698                 
49699                 
49700                 
49701             default:
49702                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49703                     
49704                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49705                     this.add(region, ret);
49706                 } else {
49707                 
49708                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49709                     return null;
49710                 }
49711                 
49712              // GridPanel (grid, cfg)
49713             
49714         }
49715         this.beginUpdate();
49716         // add children..
49717         var region = '';
49718         var abn = {};
49719         Roo.each(xitems, function(i)  {
49720             region = nb && i.region ? i.region : false;
49721             
49722             var add = ret.addxtype(i);
49723            
49724             if (region) {
49725                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49726                 if (!i.background) {
49727                     abn[region] = nb[region] ;
49728                 }
49729             }
49730             
49731         });
49732         this.endUpdate();
49733
49734         // make the last non-background panel active..
49735         //if (nb) { Roo.log(abn); }
49736         if (nb) {
49737             
49738             for(var r in abn) {
49739                 region = this.getRegion(r);
49740                 if (region) {
49741                     // tried using nb[r], but it does not work..
49742                      
49743                     region.showPanel(abn[r]);
49744                    
49745                 }
49746             }
49747         }
49748         return ret;
49749         
49750     }
49751 });
49752
49753 /**
49754  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49755  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49756  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49757  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49758  * <pre><code>
49759 // shorthand
49760 var CP = Roo.ContentPanel;
49761
49762 var layout = Roo.BorderLayout.create({
49763     north: {
49764         initialSize: 25,
49765         titlebar: false,
49766         panels: [new CP("north", "North")]
49767     },
49768     west: {
49769         split:true,
49770         initialSize: 200,
49771         minSize: 175,
49772         maxSize: 400,
49773         titlebar: true,
49774         collapsible: true,
49775         panels: [new CP("west", {title: "West"})]
49776     },
49777     east: {
49778         split:true,
49779         initialSize: 202,
49780         minSize: 175,
49781         maxSize: 400,
49782         titlebar: true,
49783         collapsible: true,
49784         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49785     },
49786     south: {
49787         split:true,
49788         initialSize: 100,
49789         minSize: 100,
49790         maxSize: 200,
49791         titlebar: true,
49792         collapsible: true,
49793         panels: [new CP("south", {title: "South", closable: true})]
49794     },
49795     center: {
49796         titlebar: true,
49797         autoScroll:true,
49798         resizeTabs: true,
49799         minTabWidth: 50,
49800         preferredTabWidth: 150,
49801         panels: [
49802             new CP("center1", {title: "Close Me", closable: true}),
49803             new CP("center2", {title: "Center Panel", closable: false})
49804         ]
49805     }
49806 }, document.body);
49807
49808 layout.getRegion("center").showPanel("center1");
49809 </code></pre>
49810  * @param config
49811  * @param targetEl
49812  */
49813 Roo.BorderLayout.create = function(config, targetEl){
49814     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49815     layout.beginUpdate();
49816     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49817     for(var j = 0, jlen = regions.length; j < jlen; j++){
49818         var lr = regions[j];
49819         if(layout.regions[lr] && config[lr].panels){
49820             var r = layout.regions[lr];
49821             var ps = config[lr].panels;
49822             layout.addTypedPanels(r, ps);
49823         }
49824     }
49825     layout.endUpdate();
49826     return layout;
49827 };
49828
49829 // private
49830 Roo.BorderLayout.RegionFactory = {
49831     // private
49832     validRegions : ["north","south","east","west","center"],
49833
49834     // private
49835     create : function(target, mgr, config){
49836         target = target.toLowerCase();
49837         if(config.lightweight || config.basic){
49838             return new Roo.BasicLayoutRegion(mgr, config, target);
49839         }
49840         switch(target){
49841             case "north":
49842                 return new Roo.NorthLayoutRegion(mgr, config);
49843             case "south":
49844                 return new Roo.SouthLayoutRegion(mgr, config);
49845             case "east":
49846                 return new Roo.EastLayoutRegion(mgr, config);
49847             case "west":
49848                 return new Roo.WestLayoutRegion(mgr, config);
49849             case "center":
49850                 return new Roo.CenterLayoutRegion(mgr, config);
49851         }
49852         throw 'Layout region "'+target+'" not supported.';
49853     }
49854 };/*
49855  * Based on:
49856  * Ext JS Library 1.1.1
49857  * Copyright(c) 2006-2007, Ext JS, LLC.
49858  *
49859  * Originally Released Under LGPL - original licence link has changed is not relivant.
49860  *
49861  * Fork - LGPL
49862  * <script type="text/javascript">
49863  */
49864  
49865 /**
49866  * @class Roo.BasicLayoutRegion
49867  * @extends Roo.util.Observable
49868  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49869  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49870  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49871  */
49872 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49873     this.mgr = mgr;
49874     this.position  = pos;
49875     this.events = {
49876         /**
49877          * @scope Roo.BasicLayoutRegion
49878          */
49879         
49880         /**
49881          * @event beforeremove
49882          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49883          * @param {Roo.LayoutRegion} this
49884          * @param {Roo.ContentPanel} panel The panel
49885          * @param {Object} e The cancel event object
49886          */
49887         "beforeremove" : true,
49888         /**
49889          * @event invalidated
49890          * Fires when the layout for this region is changed.
49891          * @param {Roo.LayoutRegion} this
49892          */
49893         "invalidated" : true,
49894         /**
49895          * @event visibilitychange
49896          * Fires when this region is shown or hidden 
49897          * @param {Roo.LayoutRegion} this
49898          * @param {Boolean} visibility true or false
49899          */
49900         "visibilitychange" : true,
49901         /**
49902          * @event paneladded
49903          * Fires when a panel is added. 
49904          * @param {Roo.LayoutRegion} this
49905          * @param {Roo.ContentPanel} panel The panel
49906          */
49907         "paneladded" : true,
49908         /**
49909          * @event panelremoved
49910          * Fires when a panel is removed. 
49911          * @param {Roo.LayoutRegion} this
49912          * @param {Roo.ContentPanel} panel The panel
49913          */
49914         "panelremoved" : true,
49915         /**
49916          * @event collapsed
49917          * Fires when this region is collapsed.
49918          * @param {Roo.LayoutRegion} this
49919          */
49920         "collapsed" : true,
49921         /**
49922          * @event expanded
49923          * Fires when this region is expanded.
49924          * @param {Roo.LayoutRegion} this
49925          */
49926         "expanded" : true,
49927         /**
49928          * @event slideshow
49929          * Fires when this region is slid into view.
49930          * @param {Roo.LayoutRegion} this
49931          */
49932         "slideshow" : true,
49933         /**
49934          * @event slidehide
49935          * Fires when this region slides out of view. 
49936          * @param {Roo.LayoutRegion} this
49937          */
49938         "slidehide" : true,
49939         /**
49940          * @event panelactivated
49941          * Fires when a panel is activated. 
49942          * @param {Roo.LayoutRegion} this
49943          * @param {Roo.ContentPanel} panel The activated panel
49944          */
49945         "panelactivated" : true,
49946         /**
49947          * @event resized
49948          * Fires when the user resizes this region. 
49949          * @param {Roo.LayoutRegion} this
49950          * @param {Number} newSize The new size (width for east/west, height for north/south)
49951          */
49952         "resized" : true
49953     };
49954     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49955     this.panels = new Roo.util.MixedCollection();
49956     this.panels.getKey = this.getPanelId.createDelegate(this);
49957     this.box = null;
49958     this.activePanel = null;
49959     // ensure listeners are added...
49960     
49961     if (config.listeners || config.events) {
49962         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49963             listeners : config.listeners || {},
49964             events : config.events || {}
49965         });
49966     }
49967     
49968     if(skipConfig !== true){
49969         this.applyConfig(config);
49970     }
49971 };
49972
49973 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49974     getPanelId : function(p){
49975         return p.getId();
49976     },
49977     
49978     applyConfig : function(config){
49979         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49980         this.config = config;
49981         
49982     },
49983     
49984     /**
49985      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49986      * the width, for horizontal (north, south) the height.
49987      * @param {Number} newSize The new width or height
49988      */
49989     resizeTo : function(newSize){
49990         var el = this.el ? this.el :
49991                  (this.activePanel ? this.activePanel.getEl() : null);
49992         if(el){
49993             switch(this.position){
49994                 case "east":
49995                 case "west":
49996                     el.setWidth(newSize);
49997                     this.fireEvent("resized", this, newSize);
49998                 break;
49999                 case "north":
50000                 case "south":
50001                     el.setHeight(newSize);
50002                     this.fireEvent("resized", this, newSize);
50003                 break;                
50004             }
50005         }
50006     },
50007     
50008     getBox : function(){
50009         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50010     },
50011     
50012     getMargins : function(){
50013         return this.margins;
50014     },
50015     
50016     updateBox : function(box){
50017         this.box = box;
50018         var el = this.activePanel.getEl();
50019         el.dom.style.left = box.x + "px";
50020         el.dom.style.top = box.y + "px";
50021         this.activePanel.setSize(box.width, box.height);
50022     },
50023     
50024     /**
50025      * Returns the container element for this region.
50026      * @return {Roo.Element}
50027      */
50028     getEl : function(){
50029         return this.activePanel;
50030     },
50031     
50032     /**
50033      * Returns true if this region is currently visible.
50034      * @return {Boolean}
50035      */
50036     isVisible : function(){
50037         return this.activePanel ? true : false;
50038     },
50039     
50040     setActivePanel : function(panel){
50041         panel = this.getPanel(panel);
50042         if(this.activePanel && this.activePanel != panel){
50043             this.activePanel.setActiveState(false);
50044             this.activePanel.getEl().setLeftTop(-10000,-10000);
50045         }
50046         this.activePanel = panel;
50047         panel.setActiveState(true);
50048         if(this.box){
50049             panel.setSize(this.box.width, this.box.height);
50050         }
50051         this.fireEvent("panelactivated", this, panel);
50052         this.fireEvent("invalidated");
50053     },
50054     
50055     /**
50056      * Show the specified panel.
50057      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50058      * @return {Roo.ContentPanel} The shown panel or null
50059      */
50060     showPanel : function(panel){
50061         if(panel = this.getPanel(panel)){
50062             this.setActivePanel(panel);
50063         }
50064         return panel;
50065     },
50066     
50067     /**
50068      * Get the active panel for this region.
50069      * @return {Roo.ContentPanel} The active panel or null
50070      */
50071     getActivePanel : function(){
50072         return this.activePanel;
50073     },
50074     
50075     /**
50076      * Add the passed ContentPanel(s)
50077      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50078      * @return {Roo.ContentPanel} The panel added (if only one was added)
50079      */
50080     add : function(panel){
50081         if(arguments.length > 1){
50082             for(var i = 0, len = arguments.length; i < len; i++) {
50083                 this.add(arguments[i]);
50084             }
50085             return null;
50086         }
50087         if(this.hasPanel(panel)){
50088             this.showPanel(panel);
50089             return panel;
50090         }
50091         var el = panel.getEl();
50092         if(el.dom.parentNode != this.mgr.el.dom){
50093             this.mgr.el.dom.appendChild(el.dom);
50094         }
50095         if(panel.setRegion){
50096             panel.setRegion(this);
50097         }
50098         this.panels.add(panel);
50099         el.setStyle("position", "absolute");
50100         if(!panel.background){
50101             this.setActivePanel(panel);
50102             if(this.config.initialSize && this.panels.getCount()==1){
50103                 this.resizeTo(this.config.initialSize);
50104             }
50105         }
50106         this.fireEvent("paneladded", this, panel);
50107         return panel;
50108     },
50109     
50110     /**
50111      * Returns true if the panel is in this region.
50112      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50113      * @return {Boolean}
50114      */
50115     hasPanel : function(panel){
50116         if(typeof panel == "object"){ // must be panel obj
50117             panel = panel.getId();
50118         }
50119         return this.getPanel(panel) ? true : false;
50120     },
50121     
50122     /**
50123      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50124      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50125      * @param {Boolean} preservePanel Overrides the config preservePanel option
50126      * @return {Roo.ContentPanel} The panel that was removed
50127      */
50128     remove : function(panel, preservePanel){
50129         panel = this.getPanel(panel);
50130         if(!panel){
50131             return null;
50132         }
50133         var e = {};
50134         this.fireEvent("beforeremove", this, panel, e);
50135         if(e.cancel === true){
50136             return null;
50137         }
50138         var panelId = panel.getId();
50139         this.panels.removeKey(panelId);
50140         return panel;
50141     },
50142     
50143     /**
50144      * Returns the panel specified or null if it's not in this region.
50145      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50146      * @return {Roo.ContentPanel}
50147      */
50148     getPanel : function(id){
50149         if(typeof id == "object"){ // must be panel obj
50150             return id;
50151         }
50152         return this.panels.get(id);
50153     },
50154     
50155     /**
50156      * Returns this regions position (north/south/east/west/center).
50157      * @return {String} 
50158      */
50159     getPosition: function(){
50160         return this.position;    
50161     }
50162 });/*
50163  * Based on:
50164  * Ext JS Library 1.1.1
50165  * Copyright(c) 2006-2007, Ext JS, LLC.
50166  *
50167  * Originally Released Under LGPL - original licence link has changed is not relivant.
50168  *
50169  * Fork - LGPL
50170  * <script type="text/javascript">
50171  */
50172  
50173 /**
50174  * @class Roo.LayoutRegion
50175  * @extends Roo.BasicLayoutRegion
50176  * This class represents a region in a layout manager.
50177  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50178  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50179  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50180  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50181  * @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})
50182  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50183  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50184  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50185  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50186  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50187  * @cfg {String}    title           The title for the region (overrides panel titles)
50188  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50189  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50190  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50191  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50192  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50193  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50194  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50195  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50196  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50197  * @cfg {Boolean}   showPin         True to show a pin button
50198  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50199  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50200  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50201  * @cfg {Number}    width           For East/West panels
50202  * @cfg {Number}    height          For North/South panels
50203  * @cfg {Boolean}   split           To show the splitter
50204  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50205  */
50206 Roo.LayoutRegion = function(mgr, config, pos){
50207     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50208     var dh = Roo.DomHelper;
50209     /** This region's container element 
50210     * @type Roo.Element */
50211     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50212     /** This region's title element 
50213     * @type Roo.Element */
50214
50215     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50216         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50217         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50218     ]}, true);
50219     this.titleEl.enableDisplayMode();
50220     /** This region's title text element 
50221     * @type HTMLElement */
50222     this.titleTextEl = this.titleEl.dom.firstChild;
50223     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50224     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50225     this.closeBtn.enableDisplayMode();
50226     this.closeBtn.on("click", this.closeClicked, this);
50227     this.closeBtn.hide();
50228
50229     this.createBody(config);
50230     this.visible = true;
50231     this.collapsed = false;
50232
50233     if(config.hideWhenEmpty){
50234         this.hide();
50235         this.on("paneladded", this.validateVisibility, this);
50236         this.on("panelremoved", this.validateVisibility, this);
50237     }
50238     this.applyConfig(config);
50239 };
50240
50241 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50242
50243     createBody : function(){
50244         /** This region's body element 
50245         * @type Roo.Element */
50246         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50247     },
50248
50249     applyConfig : function(c){
50250         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50251             var dh = Roo.DomHelper;
50252             if(c.titlebar !== false){
50253                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50254                 this.collapseBtn.on("click", this.collapse, this);
50255                 this.collapseBtn.enableDisplayMode();
50256
50257                 if(c.showPin === true || this.showPin){
50258                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50259                     this.stickBtn.enableDisplayMode();
50260                     this.stickBtn.on("click", this.expand, this);
50261                     this.stickBtn.hide();
50262                 }
50263             }
50264             /** This region's collapsed element
50265             * @type Roo.Element */
50266             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50267                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50268             ]}, true);
50269             if(c.floatable !== false){
50270                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50271                this.collapsedEl.on("click", this.collapseClick, this);
50272             }
50273
50274             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50275                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50276                    id: "message", unselectable: "on", style:{"float":"left"}});
50277                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50278              }
50279             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50280             this.expandBtn.on("click", this.expand, this);
50281         }
50282         if(this.collapseBtn){
50283             this.collapseBtn.setVisible(c.collapsible == true);
50284         }
50285         this.cmargins = c.cmargins || this.cmargins ||
50286                          (this.position == "west" || this.position == "east" ?
50287                              {top: 0, left: 2, right:2, bottom: 0} :
50288                              {top: 2, left: 0, right:0, bottom: 2});
50289         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50290         this.bottomTabs = c.tabPosition != "top";
50291         this.autoScroll = c.autoScroll || false;
50292         if(this.autoScroll){
50293             this.bodyEl.setStyle("overflow", "auto");
50294         }else{
50295             this.bodyEl.setStyle("overflow", "hidden");
50296         }
50297         //if(c.titlebar !== false){
50298             if((!c.titlebar && !c.title) || c.titlebar === false){
50299                 this.titleEl.hide();
50300             }else{
50301                 this.titleEl.show();
50302                 if(c.title){
50303                     this.titleTextEl.innerHTML = c.title;
50304                 }
50305             }
50306         //}
50307         this.duration = c.duration || .30;
50308         this.slideDuration = c.slideDuration || .45;
50309         this.config = c;
50310         if(c.collapsed){
50311             this.collapse(true);
50312         }
50313         if(c.hidden){
50314             this.hide();
50315         }
50316     },
50317     /**
50318      * Returns true if this region is currently visible.
50319      * @return {Boolean}
50320      */
50321     isVisible : function(){
50322         return this.visible;
50323     },
50324
50325     /**
50326      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50327      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50328      */
50329     setCollapsedTitle : function(title){
50330         title = title || "&#160;";
50331         if(this.collapsedTitleTextEl){
50332             this.collapsedTitleTextEl.innerHTML = title;
50333         }
50334     },
50335
50336     getBox : function(){
50337         var b;
50338         if(!this.collapsed){
50339             b = this.el.getBox(false, true);
50340         }else{
50341             b = this.collapsedEl.getBox(false, true);
50342         }
50343         return b;
50344     },
50345
50346     getMargins : function(){
50347         return this.collapsed ? this.cmargins : this.margins;
50348     },
50349
50350     highlight : function(){
50351         this.el.addClass("x-layout-panel-dragover");
50352     },
50353
50354     unhighlight : function(){
50355         this.el.removeClass("x-layout-panel-dragover");
50356     },
50357
50358     updateBox : function(box){
50359         this.box = box;
50360         if(!this.collapsed){
50361             this.el.dom.style.left = box.x + "px";
50362             this.el.dom.style.top = box.y + "px";
50363             this.updateBody(box.width, box.height);
50364         }else{
50365             this.collapsedEl.dom.style.left = box.x + "px";
50366             this.collapsedEl.dom.style.top = box.y + "px";
50367             this.collapsedEl.setSize(box.width, box.height);
50368         }
50369         if(this.tabs){
50370             this.tabs.autoSizeTabs();
50371         }
50372     },
50373
50374     updateBody : function(w, h){
50375         if(w !== null){
50376             this.el.setWidth(w);
50377             w -= this.el.getBorderWidth("rl");
50378             if(this.config.adjustments){
50379                 w += this.config.adjustments[0];
50380             }
50381         }
50382         if(h !== null){
50383             this.el.setHeight(h);
50384             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50385             h -= this.el.getBorderWidth("tb");
50386             if(this.config.adjustments){
50387                 h += this.config.adjustments[1];
50388             }
50389             this.bodyEl.setHeight(h);
50390             if(this.tabs){
50391                 h = this.tabs.syncHeight(h);
50392             }
50393         }
50394         if(this.panelSize){
50395             w = w !== null ? w : this.panelSize.width;
50396             h = h !== null ? h : this.panelSize.height;
50397         }
50398         if(this.activePanel){
50399             var el = this.activePanel.getEl();
50400             w = w !== null ? w : el.getWidth();
50401             h = h !== null ? h : el.getHeight();
50402             this.panelSize = {width: w, height: h};
50403             this.activePanel.setSize(w, h);
50404         }
50405         if(Roo.isIE && this.tabs){
50406             this.tabs.el.repaint();
50407         }
50408     },
50409
50410     /**
50411      * Returns the container element for this region.
50412      * @return {Roo.Element}
50413      */
50414     getEl : function(){
50415         return this.el;
50416     },
50417
50418     /**
50419      * Hides this region.
50420      */
50421     hide : function(){
50422         if(!this.collapsed){
50423             this.el.dom.style.left = "-2000px";
50424             this.el.hide();
50425         }else{
50426             this.collapsedEl.dom.style.left = "-2000px";
50427             this.collapsedEl.hide();
50428         }
50429         this.visible = false;
50430         this.fireEvent("visibilitychange", this, false);
50431     },
50432
50433     /**
50434      * Shows this region if it was previously hidden.
50435      */
50436     show : function(){
50437         if(!this.collapsed){
50438             this.el.show();
50439         }else{
50440             this.collapsedEl.show();
50441         }
50442         this.visible = true;
50443         this.fireEvent("visibilitychange", this, true);
50444     },
50445
50446     closeClicked : function(){
50447         if(this.activePanel){
50448             this.remove(this.activePanel);
50449         }
50450     },
50451
50452     collapseClick : function(e){
50453         if(this.isSlid){
50454            e.stopPropagation();
50455            this.slideIn();
50456         }else{
50457            e.stopPropagation();
50458            this.slideOut();
50459         }
50460     },
50461
50462     /**
50463      * Collapses this region.
50464      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50465      */
50466     collapse : function(skipAnim){
50467         if(this.collapsed) return;
50468         this.collapsed = true;
50469         if(this.split){
50470             this.split.el.hide();
50471         }
50472         if(this.config.animate && skipAnim !== true){
50473             this.fireEvent("invalidated", this);
50474             this.animateCollapse();
50475         }else{
50476             this.el.setLocation(-20000,-20000);
50477             this.el.hide();
50478             this.collapsedEl.show();
50479             this.fireEvent("collapsed", this);
50480             this.fireEvent("invalidated", this);
50481         }
50482     },
50483
50484     animateCollapse : function(){
50485         // overridden
50486     },
50487
50488     /**
50489      * Expands this region if it was previously collapsed.
50490      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50491      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50492      */
50493     expand : function(e, skipAnim){
50494         if(e) e.stopPropagation();
50495         if(!this.collapsed || this.el.hasActiveFx()) return;
50496         if(this.isSlid){
50497             this.afterSlideIn();
50498             skipAnim = true;
50499         }
50500         this.collapsed = false;
50501         if(this.config.animate && skipAnim !== true){
50502             this.animateExpand();
50503         }else{
50504             this.el.show();
50505             if(this.split){
50506                 this.split.el.show();
50507             }
50508             this.collapsedEl.setLocation(-2000,-2000);
50509             this.collapsedEl.hide();
50510             this.fireEvent("invalidated", this);
50511             this.fireEvent("expanded", this);
50512         }
50513     },
50514
50515     animateExpand : function(){
50516         // overridden
50517     },
50518
50519     initTabs : function()
50520     {
50521         this.bodyEl.setStyle("overflow", "hidden");
50522         var ts = new Roo.TabPanel(
50523                 this.bodyEl.dom,
50524                 {
50525                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50526                     disableTooltips: this.config.disableTabTips,
50527                     toolbar : this.config.toolbar
50528                 }
50529         );
50530         if(this.config.hideTabs){
50531             ts.stripWrap.setDisplayed(false);
50532         }
50533         this.tabs = ts;
50534         ts.resizeTabs = this.config.resizeTabs === true;
50535         ts.minTabWidth = this.config.minTabWidth || 40;
50536         ts.maxTabWidth = this.config.maxTabWidth || 250;
50537         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50538         ts.monitorResize = false;
50539         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50540         ts.bodyEl.addClass('x-layout-tabs-body');
50541         this.panels.each(this.initPanelAsTab, this);
50542     },
50543
50544     initPanelAsTab : function(panel){
50545         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50546                     this.config.closeOnTab && panel.isClosable());
50547         if(panel.tabTip !== undefined){
50548             ti.setTooltip(panel.tabTip);
50549         }
50550         ti.on("activate", function(){
50551               this.setActivePanel(panel);
50552         }, this);
50553         if(this.config.closeOnTab){
50554             ti.on("beforeclose", function(t, e){
50555                 e.cancel = true;
50556                 this.remove(panel);
50557             }, this);
50558         }
50559         return ti;
50560     },
50561
50562     updatePanelTitle : function(panel, title){
50563         if(this.activePanel == panel){
50564             this.updateTitle(title);
50565         }
50566         if(this.tabs){
50567             var ti = this.tabs.getTab(panel.getEl().id);
50568             ti.setText(title);
50569             if(panel.tabTip !== undefined){
50570                 ti.setTooltip(panel.tabTip);
50571             }
50572         }
50573     },
50574
50575     updateTitle : function(title){
50576         if(this.titleTextEl && !this.config.title){
50577             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50578         }
50579     },
50580
50581     setActivePanel : function(panel){
50582         panel = this.getPanel(panel);
50583         if(this.activePanel && this.activePanel != panel){
50584             this.activePanel.setActiveState(false);
50585         }
50586         this.activePanel = panel;
50587         panel.setActiveState(true);
50588         if(this.panelSize){
50589             panel.setSize(this.panelSize.width, this.panelSize.height);
50590         }
50591         if(this.closeBtn){
50592             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50593         }
50594         this.updateTitle(panel.getTitle());
50595         if(this.tabs){
50596             this.fireEvent("invalidated", this);
50597         }
50598         this.fireEvent("panelactivated", this, panel);
50599     },
50600
50601     /**
50602      * Shows the specified panel.
50603      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50604      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50605      */
50606     showPanel : function(panel)
50607     {
50608         panel = this.getPanel(panel);
50609         if(panel){
50610             if(this.tabs){
50611                 var tab = this.tabs.getTab(panel.getEl().id);
50612                 if(tab.isHidden()){
50613                     this.tabs.unhideTab(tab.id);
50614                 }
50615                 tab.activate();
50616             }else{
50617                 this.setActivePanel(panel);
50618             }
50619         }
50620         return panel;
50621     },
50622
50623     /**
50624      * Get the active panel for this region.
50625      * @return {Roo.ContentPanel} The active panel or null
50626      */
50627     getActivePanel : function(){
50628         return this.activePanel;
50629     },
50630
50631     validateVisibility : function(){
50632         if(this.panels.getCount() < 1){
50633             this.updateTitle("&#160;");
50634             this.closeBtn.hide();
50635             this.hide();
50636         }else{
50637             if(!this.isVisible()){
50638                 this.show();
50639             }
50640         }
50641     },
50642
50643     /**
50644      * Adds the passed ContentPanel(s) to this region.
50645      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50646      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50647      */
50648     add : function(panel){
50649         if(arguments.length > 1){
50650             for(var i = 0, len = arguments.length; i < len; i++) {
50651                 this.add(arguments[i]);
50652             }
50653             return null;
50654         }
50655         if(this.hasPanel(panel)){
50656             this.showPanel(panel);
50657             return panel;
50658         }
50659         panel.setRegion(this);
50660         this.panels.add(panel);
50661         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50662             this.bodyEl.dom.appendChild(panel.getEl().dom);
50663             if(panel.background !== true){
50664                 this.setActivePanel(panel);
50665             }
50666             this.fireEvent("paneladded", this, panel);
50667             return panel;
50668         }
50669         if(!this.tabs){
50670             this.initTabs();
50671         }else{
50672             this.initPanelAsTab(panel);
50673         }
50674         if(panel.background !== true){
50675             this.tabs.activate(panel.getEl().id);
50676         }
50677         this.fireEvent("paneladded", this, panel);
50678         return panel;
50679     },
50680
50681     /**
50682      * Hides the tab for the specified panel.
50683      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50684      */
50685     hidePanel : function(panel){
50686         if(this.tabs && (panel = this.getPanel(panel))){
50687             this.tabs.hideTab(panel.getEl().id);
50688         }
50689     },
50690
50691     /**
50692      * Unhides the tab for a previously hidden panel.
50693      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50694      */
50695     unhidePanel : function(panel){
50696         if(this.tabs && (panel = this.getPanel(panel))){
50697             this.tabs.unhideTab(panel.getEl().id);
50698         }
50699     },
50700
50701     clearPanels : function(){
50702         while(this.panels.getCount() > 0){
50703              this.remove(this.panels.first());
50704         }
50705     },
50706
50707     /**
50708      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50709      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50710      * @param {Boolean} preservePanel Overrides the config preservePanel option
50711      * @return {Roo.ContentPanel} The panel that was removed
50712      */
50713     remove : function(panel, preservePanel){
50714         panel = this.getPanel(panel);
50715         if(!panel){
50716             return null;
50717         }
50718         var e = {};
50719         this.fireEvent("beforeremove", this, panel, e);
50720         if(e.cancel === true){
50721             return null;
50722         }
50723         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50724         var panelId = panel.getId();
50725         this.panels.removeKey(panelId);
50726         if(preservePanel){
50727             document.body.appendChild(panel.getEl().dom);
50728         }
50729         if(this.tabs){
50730             this.tabs.removeTab(panel.getEl().id);
50731         }else if (!preservePanel){
50732             this.bodyEl.dom.removeChild(panel.getEl().dom);
50733         }
50734         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50735             var p = this.panels.first();
50736             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50737             tempEl.appendChild(p.getEl().dom);
50738             this.bodyEl.update("");
50739             this.bodyEl.dom.appendChild(p.getEl().dom);
50740             tempEl = null;
50741             this.updateTitle(p.getTitle());
50742             this.tabs = null;
50743             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50744             this.setActivePanel(p);
50745         }
50746         panel.setRegion(null);
50747         if(this.activePanel == panel){
50748             this.activePanel = null;
50749         }
50750         if(this.config.autoDestroy !== false && preservePanel !== true){
50751             try{panel.destroy();}catch(e){}
50752         }
50753         this.fireEvent("panelremoved", this, panel);
50754         return panel;
50755     },
50756
50757     /**
50758      * Returns the TabPanel component used by this region
50759      * @return {Roo.TabPanel}
50760      */
50761     getTabs : function(){
50762         return this.tabs;
50763     },
50764
50765     createTool : function(parentEl, className){
50766         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50767             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50768         btn.addClassOnOver("x-layout-tools-button-over");
50769         return btn;
50770     }
50771 });/*
50772  * Based on:
50773  * Ext JS Library 1.1.1
50774  * Copyright(c) 2006-2007, Ext JS, LLC.
50775  *
50776  * Originally Released Under LGPL - original licence link has changed is not relivant.
50777  *
50778  * Fork - LGPL
50779  * <script type="text/javascript">
50780  */
50781  
50782
50783
50784 /**
50785  * @class Roo.SplitLayoutRegion
50786  * @extends Roo.LayoutRegion
50787  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50788  */
50789 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50790     this.cursor = cursor;
50791     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50792 };
50793
50794 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50795     splitTip : "Drag to resize.",
50796     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50797     useSplitTips : false,
50798
50799     applyConfig : function(config){
50800         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50801         if(config.split){
50802             if(!this.split){
50803                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50804                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50805                 /** The SplitBar for this region 
50806                 * @type Roo.SplitBar */
50807                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50808                 this.split.on("moved", this.onSplitMove, this);
50809                 this.split.useShim = config.useShim === true;
50810                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50811                 if(this.useSplitTips){
50812                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50813                 }
50814                 if(config.collapsible){
50815                     this.split.el.on("dblclick", this.collapse,  this);
50816                 }
50817             }
50818             if(typeof config.minSize != "undefined"){
50819                 this.split.minSize = config.minSize;
50820             }
50821             if(typeof config.maxSize != "undefined"){
50822                 this.split.maxSize = config.maxSize;
50823             }
50824             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50825                 this.hideSplitter();
50826             }
50827         }
50828     },
50829
50830     getHMaxSize : function(){
50831          var cmax = this.config.maxSize || 10000;
50832          var center = this.mgr.getRegion("center");
50833          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50834     },
50835
50836     getVMaxSize : function(){
50837          var cmax = this.config.maxSize || 10000;
50838          var center = this.mgr.getRegion("center");
50839          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50840     },
50841
50842     onSplitMove : function(split, newSize){
50843         this.fireEvent("resized", this, newSize);
50844     },
50845     
50846     /** 
50847      * Returns the {@link Roo.SplitBar} for this region.
50848      * @return {Roo.SplitBar}
50849      */
50850     getSplitBar : function(){
50851         return this.split;
50852     },
50853     
50854     hide : function(){
50855         this.hideSplitter();
50856         Roo.SplitLayoutRegion.superclass.hide.call(this);
50857     },
50858
50859     hideSplitter : function(){
50860         if(this.split){
50861             this.split.el.setLocation(-2000,-2000);
50862             this.split.el.hide();
50863         }
50864     },
50865
50866     show : function(){
50867         if(this.split){
50868             this.split.el.show();
50869         }
50870         Roo.SplitLayoutRegion.superclass.show.call(this);
50871     },
50872     
50873     beforeSlide: function(){
50874         if(Roo.isGecko){// firefox overflow auto bug workaround
50875             this.bodyEl.clip();
50876             if(this.tabs) this.tabs.bodyEl.clip();
50877             if(this.activePanel){
50878                 this.activePanel.getEl().clip();
50879                 
50880                 if(this.activePanel.beforeSlide){
50881                     this.activePanel.beforeSlide();
50882                 }
50883             }
50884         }
50885     },
50886     
50887     afterSlide : function(){
50888         if(Roo.isGecko){// firefox overflow auto bug workaround
50889             this.bodyEl.unclip();
50890             if(this.tabs) this.tabs.bodyEl.unclip();
50891             if(this.activePanel){
50892                 this.activePanel.getEl().unclip();
50893                 if(this.activePanel.afterSlide){
50894                     this.activePanel.afterSlide();
50895                 }
50896             }
50897         }
50898     },
50899
50900     initAutoHide : function(){
50901         if(this.autoHide !== false){
50902             if(!this.autoHideHd){
50903                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50904                 this.autoHideHd = {
50905                     "mouseout": function(e){
50906                         if(!e.within(this.el, true)){
50907                             st.delay(500);
50908                         }
50909                     },
50910                     "mouseover" : function(e){
50911                         st.cancel();
50912                     },
50913                     scope : this
50914                 };
50915             }
50916             this.el.on(this.autoHideHd);
50917         }
50918     },
50919
50920     clearAutoHide : function(){
50921         if(this.autoHide !== false){
50922             this.el.un("mouseout", this.autoHideHd.mouseout);
50923             this.el.un("mouseover", this.autoHideHd.mouseover);
50924         }
50925     },
50926
50927     clearMonitor : function(){
50928         Roo.get(document).un("click", this.slideInIf, this);
50929     },
50930
50931     // these names are backwards but not changed for compat
50932     slideOut : function(){
50933         if(this.isSlid || this.el.hasActiveFx()){
50934             return;
50935         }
50936         this.isSlid = true;
50937         if(this.collapseBtn){
50938             this.collapseBtn.hide();
50939         }
50940         this.closeBtnState = this.closeBtn.getStyle('display');
50941         this.closeBtn.hide();
50942         if(this.stickBtn){
50943             this.stickBtn.show();
50944         }
50945         this.el.show();
50946         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50947         this.beforeSlide();
50948         this.el.setStyle("z-index", 10001);
50949         this.el.slideIn(this.getSlideAnchor(), {
50950             callback: function(){
50951                 this.afterSlide();
50952                 this.initAutoHide();
50953                 Roo.get(document).on("click", this.slideInIf, this);
50954                 this.fireEvent("slideshow", this);
50955             },
50956             scope: this,
50957             block: true
50958         });
50959     },
50960
50961     afterSlideIn : function(){
50962         this.clearAutoHide();
50963         this.isSlid = false;
50964         this.clearMonitor();
50965         this.el.setStyle("z-index", "");
50966         if(this.collapseBtn){
50967             this.collapseBtn.show();
50968         }
50969         this.closeBtn.setStyle('display', this.closeBtnState);
50970         if(this.stickBtn){
50971             this.stickBtn.hide();
50972         }
50973         this.fireEvent("slidehide", this);
50974     },
50975
50976     slideIn : function(cb){
50977         if(!this.isSlid || this.el.hasActiveFx()){
50978             Roo.callback(cb);
50979             return;
50980         }
50981         this.isSlid = false;
50982         this.beforeSlide();
50983         this.el.slideOut(this.getSlideAnchor(), {
50984             callback: function(){
50985                 this.el.setLeftTop(-10000, -10000);
50986                 this.afterSlide();
50987                 this.afterSlideIn();
50988                 Roo.callback(cb);
50989             },
50990             scope: this,
50991             block: true
50992         });
50993     },
50994     
50995     slideInIf : function(e){
50996         if(!e.within(this.el)){
50997             this.slideIn();
50998         }
50999     },
51000
51001     animateCollapse : function(){
51002         this.beforeSlide();
51003         this.el.setStyle("z-index", 20000);
51004         var anchor = this.getSlideAnchor();
51005         this.el.slideOut(anchor, {
51006             callback : function(){
51007                 this.el.setStyle("z-index", "");
51008                 this.collapsedEl.slideIn(anchor, {duration:.3});
51009                 this.afterSlide();
51010                 this.el.setLocation(-10000,-10000);
51011                 this.el.hide();
51012                 this.fireEvent("collapsed", this);
51013             },
51014             scope: this,
51015             block: true
51016         });
51017     },
51018
51019     animateExpand : function(){
51020         this.beforeSlide();
51021         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51022         this.el.setStyle("z-index", 20000);
51023         this.collapsedEl.hide({
51024             duration:.1
51025         });
51026         this.el.slideIn(this.getSlideAnchor(), {
51027             callback : function(){
51028                 this.el.setStyle("z-index", "");
51029                 this.afterSlide();
51030                 if(this.split){
51031                     this.split.el.show();
51032                 }
51033                 this.fireEvent("invalidated", this);
51034                 this.fireEvent("expanded", this);
51035             },
51036             scope: this,
51037             block: true
51038         });
51039     },
51040
51041     anchors : {
51042         "west" : "left",
51043         "east" : "right",
51044         "north" : "top",
51045         "south" : "bottom"
51046     },
51047
51048     sanchors : {
51049         "west" : "l",
51050         "east" : "r",
51051         "north" : "t",
51052         "south" : "b"
51053     },
51054
51055     canchors : {
51056         "west" : "tl-tr",
51057         "east" : "tr-tl",
51058         "north" : "tl-bl",
51059         "south" : "bl-tl"
51060     },
51061
51062     getAnchor : function(){
51063         return this.anchors[this.position];
51064     },
51065
51066     getCollapseAnchor : function(){
51067         return this.canchors[this.position];
51068     },
51069
51070     getSlideAnchor : function(){
51071         return this.sanchors[this.position];
51072     },
51073
51074     getAlignAdj : function(){
51075         var cm = this.cmargins;
51076         switch(this.position){
51077             case "west":
51078                 return [0, 0];
51079             break;
51080             case "east":
51081                 return [0, 0];
51082             break;
51083             case "north":
51084                 return [0, 0];
51085             break;
51086             case "south":
51087                 return [0, 0];
51088             break;
51089         }
51090     },
51091
51092     getExpandAdj : function(){
51093         var c = this.collapsedEl, cm = this.cmargins;
51094         switch(this.position){
51095             case "west":
51096                 return [-(cm.right+c.getWidth()+cm.left), 0];
51097             break;
51098             case "east":
51099                 return [cm.right+c.getWidth()+cm.left, 0];
51100             break;
51101             case "north":
51102                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51103             break;
51104             case "south":
51105                 return [0, cm.top+cm.bottom+c.getHeight()];
51106             break;
51107         }
51108     }
51109 });/*
51110  * Based on:
51111  * Ext JS Library 1.1.1
51112  * Copyright(c) 2006-2007, Ext JS, LLC.
51113  *
51114  * Originally Released Under LGPL - original licence link has changed is not relivant.
51115  *
51116  * Fork - LGPL
51117  * <script type="text/javascript">
51118  */
51119 /*
51120  * These classes are private internal classes
51121  */
51122 Roo.CenterLayoutRegion = function(mgr, config){
51123     Roo.LayoutRegion.call(this, mgr, config, "center");
51124     this.visible = true;
51125     this.minWidth = config.minWidth || 20;
51126     this.minHeight = config.minHeight || 20;
51127 };
51128
51129 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51130     hide : function(){
51131         // center panel can't be hidden
51132     },
51133     
51134     show : function(){
51135         // center panel can't be hidden
51136     },
51137     
51138     getMinWidth: function(){
51139         return this.minWidth;
51140     },
51141     
51142     getMinHeight: function(){
51143         return this.minHeight;
51144     }
51145 });
51146
51147
51148 Roo.NorthLayoutRegion = function(mgr, config){
51149     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51150     if(this.split){
51151         this.split.placement = Roo.SplitBar.TOP;
51152         this.split.orientation = Roo.SplitBar.VERTICAL;
51153         this.split.el.addClass("x-layout-split-v");
51154     }
51155     var size = config.initialSize || config.height;
51156     if(typeof size != "undefined"){
51157         this.el.setHeight(size);
51158     }
51159 };
51160 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51161     orientation: Roo.SplitBar.VERTICAL,
51162     getBox : function(){
51163         if(this.collapsed){
51164             return this.collapsedEl.getBox();
51165         }
51166         var box = this.el.getBox();
51167         if(this.split){
51168             box.height += this.split.el.getHeight();
51169         }
51170         return box;
51171     },
51172     
51173     updateBox : function(box){
51174         if(this.split && !this.collapsed){
51175             box.height -= this.split.el.getHeight();
51176             this.split.el.setLeft(box.x);
51177             this.split.el.setTop(box.y+box.height);
51178             this.split.el.setWidth(box.width);
51179         }
51180         if(this.collapsed){
51181             this.updateBody(box.width, null);
51182         }
51183         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51184     }
51185 });
51186
51187 Roo.SouthLayoutRegion = function(mgr, config){
51188     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51189     if(this.split){
51190         this.split.placement = Roo.SplitBar.BOTTOM;
51191         this.split.orientation = Roo.SplitBar.VERTICAL;
51192         this.split.el.addClass("x-layout-split-v");
51193     }
51194     var size = config.initialSize || config.height;
51195     if(typeof size != "undefined"){
51196         this.el.setHeight(size);
51197     }
51198 };
51199 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51200     orientation: Roo.SplitBar.VERTICAL,
51201     getBox : function(){
51202         if(this.collapsed){
51203             return this.collapsedEl.getBox();
51204         }
51205         var box = this.el.getBox();
51206         if(this.split){
51207             var sh = this.split.el.getHeight();
51208             box.height += sh;
51209             box.y -= sh;
51210         }
51211         return box;
51212     },
51213     
51214     updateBox : function(box){
51215         if(this.split && !this.collapsed){
51216             var sh = this.split.el.getHeight();
51217             box.height -= sh;
51218             box.y += sh;
51219             this.split.el.setLeft(box.x);
51220             this.split.el.setTop(box.y-sh);
51221             this.split.el.setWidth(box.width);
51222         }
51223         if(this.collapsed){
51224             this.updateBody(box.width, null);
51225         }
51226         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51227     }
51228 });
51229
51230 Roo.EastLayoutRegion = function(mgr, config){
51231     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51232     if(this.split){
51233         this.split.placement = Roo.SplitBar.RIGHT;
51234         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51235         this.split.el.addClass("x-layout-split-h");
51236     }
51237     var size = config.initialSize || config.width;
51238     if(typeof size != "undefined"){
51239         this.el.setWidth(size);
51240     }
51241 };
51242 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51243     orientation: Roo.SplitBar.HORIZONTAL,
51244     getBox : function(){
51245         if(this.collapsed){
51246             return this.collapsedEl.getBox();
51247         }
51248         var box = this.el.getBox();
51249         if(this.split){
51250             var sw = this.split.el.getWidth();
51251             box.width += sw;
51252             box.x -= sw;
51253         }
51254         return box;
51255     },
51256
51257     updateBox : function(box){
51258         if(this.split && !this.collapsed){
51259             var sw = this.split.el.getWidth();
51260             box.width -= sw;
51261             this.split.el.setLeft(box.x);
51262             this.split.el.setTop(box.y);
51263             this.split.el.setHeight(box.height);
51264             box.x += sw;
51265         }
51266         if(this.collapsed){
51267             this.updateBody(null, box.height);
51268         }
51269         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51270     }
51271 });
51272
51273 Roo.WestLayoutRegion = function(mgr, config){
51274     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51275     if(this.split){
51276         this.split.placement = Roo.SplitBar.LEFT;
51277         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51278         this.split.el.addClass("x-layout-split-h");
51279     }
51280     var size = config.initialSize || config.width;
51281     if(typeof size != "undefined"){
51282         this.el.setWidth(size);
51283     }
51284 };
51285 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51286     orientation: Roo.SplitBar.HORIZONTAL,
51287     getBox : function(){
51288         if(this.collapsed){
51289             return this.collapsedEl.getBox();
51290         }
51291         var box = this.el.getBox();
51292         if(this.split){
51293             box.width += this.split.el.getWidth();
51294         }
51295         return box;
51296     },
51297     
51298     updateBox : function(box){
51299         if(this.split && !this.collapsed){
51300             var sw = this.split.el.getWidth();
51301             box.width -= sw;
51302             this.split.el.setLeft(box.x+box.width);
51303             this.split.el.setTop(box.y);
51304             this.split.el.setHeight(box.height);
51305         }
51306         if(this.collapsed){
51307             this.updateBody(null, box.height);
51308         }
51309         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51310     }
51311 });
51312 /*
51313  * Based on:
51314  * Ext JS Library 1.1.1
51315  * Copyright(c) 2006-2007, Ext JS, LLC.
51316  *
51317  * Originally Released Under LGPL - original licence link has changed is not relivant.
51318  *
51319  * Fork - LGPL
51320  * <script type="text/javascript">
51321  */
51322  
51323  
51324 /*
51325  * Private internal class for reading and applying state
51326  */
51327 Roo.LayoutStateManager = function(layout){
51328      // default empty state
51329      this.state = {
51330         north: {},
51331         south: {},
51332         east: {},
51333         west: {}       
51334     };
51335 };
51336
51337 Roo.LayoutStateManager.prototype = {
51338     init : function(layout, provider){
51339         this.provider = provider;
51340         var state = provider.get(layout.id+"-layout-state");
51341         if(state){
51342             var wasUpdating = layout.isUpdating();
51343             if(!wasUpdating){
51344                 layout.beginUpdate();
51345             }
51346             for(var key in state){
51347                 if(typeof state[key] != "function"){
51348                     var rstate = state[key];
51349                     var r = layout.getRegion(key);
51350                     if(r && rstate){
51351                         if(rstate.size){
51352                             r.resizeTo(rstate.size);
51353                         }
51354                         if(rstate.collapsed == true){
51355                             r.collapse(true);
51356                         }else{
51357                             r.expand(null, true);
51358                         }
51359                     }
51360                 }
51361             }
51362             if(!wasUpdating){
51363                 layout.endUpdate();
51364             }
51365             this.state = state; 
51366         }
51367         this.layout = layout;
51368         layout.on("regionresized", this.onRegionResized, this);
51369         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51370         layout.on("regionexpanded", this.onRegionExpanded, this);
51371     },
51372     
51373     storeState : function(){
51374         this.provider.set(this.layout.id+"-layout-state", this.state);
51375     },
51376     
51377     onRegionResized : function(region, newSize){
51378         this.state[region.getPosition()].size = newSize;
51379         this.storeState();
51380     },
51381     
51382     onRegionCollapsed : function(region){
51383         this.state[region.getPosition()].collapsed = true;
51384         this.storeState();
51385     },
51386     
51387     onRegionExpanded : function(region){
51388         this.state[region.getPosition()].collapsed = false;
51389         this.storeState();
51390     }
51391 };/*
51392  * Based on:
51393  * Ext JS Library 1.1.1
51394  * Copyright(c) 2006-2007, Ext JS, LLC.
51395  *
51396  * Originally Released Under LGPL - original licence link has changed is not relivant.
51397  *
51398  * Fork - LGPL
51399  * <script type="text/javascript">
51400  */
51401 /**
51402  * @class Roo.ContentPanel
51403  * @extends Roo.util.Observable
51404  * A basic ContentPanel element.
51405  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51406  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51407  * @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
51408  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51409  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51410  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51411  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51412  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51413  * @cfg {String} title          The title for this panel
51414  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51415  * @cfg {String} url            Calls {@link #setUrl} with this value
51416  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51417  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51418  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51419  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51420
51421  * @constructor
51422  * Create a new ContentPanel.
51423  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51424  * @param {String/Object} config A string to set only the title or a config object
51425  * @param {String} content (optional) Set the HTML content for this panel
51426  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51427  */
51428 Roo.ContentPanel = function(el, config, content){
51429     
51430      
51431     /*
51432     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51433         config = el;
51434         el = Roo.id();
51435     }
51436     if (config && config.parentLayout) { 
51437         el = config.parentLayout.el.createChild(); 
51438     }
51439     */
51440     if(el.autoCreate){ // xtype is available if this is called from factory
51441         config = el;
51442         el = Roo.id();
51443     }
51444     this.el = Roo.get(el);
51445     if(!this.el && config && config.autoCreate){
51446         if(typeof config.autoCreate == "object"){
51447             if(!config.autoCreate.id){
51448                 config.autoCreate.id = config.id||el;
51449             }
51450             this.el = Roo.DomHelper.append(document.body,
51451                         config.autoCreate, true);
51452         }else{
51453             this.el = Roo.DomHelper.append(document.body,
51454                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51455         }
51456     }
51457     this.closable = false;
51458     this.loaded = false;
51459     this.active = false;
51460     if(typeof config == "string"){
51461         this.title = config;
51462     }else{
51463         Roo.apply(this, config);
51464     }
51465     
51466     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51467         this.wrapEl = this.el.wrap();
51468         this.toolbar.container = this.el.insertSibling(false, 'before');
51469         this.toolbar = new Roo.Toolbar(this.toolbar);
51470     }
51471     
51472     // xtype created footer. - not sure if will work as we normally have to render first..
51473     if (this.footer && !this.footer.el && this.footer.xtype) {
51474         if (!this.wrapEl) {
51475             this.wrapEl = this.el.wrap();
51476         }
51477     
51478         this.footer.container = this.wrapEl.createChild();
51479          
51480         this.footer = Roo.factory(this.footer, Roo);
51481         
51482     }
51483     
51484     if(this.resizeEl){
51485         this.resizeEl = Roo.get(this.resizeEl, true);
51486     }else{
51487         this.resizeEl = this.el;
51488     }
51489     // handle view.xtype
51490     
51491  
51492     
51493     
51494     this.addEvents({
51495         /**
51496          * @event activate
51497          * Fires when this panel is activated. 
51498          * @param {Roo.ContentPanel} this
51499          */
51500         "activate" : true,
51501         /**
51502          * @event deactivate
51503          * Fires when this panel is activated. 
51504          * @param {Roo.ContentPanel} this
51505          */
51506         "deactivate" : true,
51507
51508         /**
51509          * @event resize
51510          * Fires when this panel is resized if fitToFrame is true.
51511          * @param {Roo.ContentPanel} this
51512          * @param {Number} width The width after any component adjustments
51513          * @param {Number} height The height after any component adjustments
51514          */
51515         "resize" : true,
51516         
51517          /**
51518          * @event render
51519          * Fires when this tab is created
51520          * @param {Roo.ContentPanel} this
51521          */
51522         "render" : true
51523         
51524         
51525         
51526     });
51527     
51528
51529     
51530     
51531     if(this.autoScroll){
51532         this.resizeEl.setStyle("overflow", "auto");
51533     } else {
51534         // fix randome scrolling
51535         this.el.on('scroll', function() {
51536             Roo.log('fix random scolling');
51537             this.scrollTo('top',0); 
51538         });
51539     }
51540     content = content || this.content;
51541     if(content){
51542         this.setContent(content);
51543     }
51544     if(config && config.url){
51545         this.setUrl(this.url, this.params, this.loadOnce);
51546     }
51547     
51548     
51549     
51550     Roo.ContentPanel.superclass.constructor.call(this);
51551     
51552     if (this.view && typeof(this.view.xtype) != 'undefined') {
51553         this.view.el = this.el.appendChild(document.createElement("div"));
51554         this.view = Roo.factory(this.view); 
51555         this.view.render  &&  this.view.render(false, '');  
51556     }
51557     
51558     
51559     this.fireEvent('render', this);
51560 };
51561
51562 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51563     tabTip:'',
51564     setRegion : function(region){
51565         this.region = region;
51566         if(region){
51567            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51568         }else{
51569            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51570         } 
51571     },
51572     
51573     /**
51574      * Returns the toolbar for this Panel if one was configured. 
51575      * @return {Roo.Toolbar} 
51576      */
51577     getToolbar : function(){
51578         return this.toolbar;
51579     },
51580     
51581     setActiveState : function(active){
51582         this.active = active;
51583         if(!active){
51584             this.fireEvent("deactivate", this);
51585         }else{
51586             this.fireEvent("activate", this);
51587         }
51588     },
51589     /**
51590      * Updates this panel's element
51591      * @param {String} content The new content
51592      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51593     */
51594     setContent : function(content, loadScripts){
51595         this.el.update(content, loadScripts);
51596     },
51597
51598     ignoreResize : function(w, h){
51599         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51600             return true;
51601         }else{
51602             this.lastSize = {width: w, height: h};
51603             return false;
51604         }
51605     },
51606     /**
51607      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51608      * @return {Roo.UpdateManager} The UpdateManager
51609      */
51610     getUpdateManager : function(){
51611         return this.el.getUpdateManager();
51612     },
51613      /**
51614      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51615      * @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:
51616 <pre><code>
51617 panel.load({
51618     url: "your-url.php",
51619     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51620     callback: yourFunction,
51621     scope: yourObject, //(optional scope)
51622     discardUrl: false,
51623     nocache: false,
51624     text: "Loading...",
51625     timeout: 30,
51626     scripts: false
51627 });
51628 </code></pre>
51629      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51630      * 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.
51631      * @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}
51632      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51633      * @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.
51634      * @return {Roo.ContentPanel} this
51635      */
51636     load : function(){
51637         var um = this.el.getUpdateManager();
51638         um.update.apply(um, arguments);
51639         return this;
51640     },
51641
51642
51643     /**
51644      * 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.
51645      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51646      * @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)
51647      * @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)
51648      * @return {Roo.UpdateManager} The UpdateManager
51649      */
51650     setUrl : function(url, params, loadOnce){
51651         if(this.refreshDelegate){
51652             this.removeListener("activate", this.refreshDelegate);
51653         }
51654         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51655         this.on("activate", this.refreshDelegate);
51656         return this.el.getUpdateManager();
51657     },
51658     
51659     _handleRefresh : function(url, params, loadOnce){
51660         if(!loadOnce || !this.loaded){
51661             var updater = this.el.getUpdateManager();
51662             updater.update(url, params, this._setLoaded.createDelegate(this));
51663         }
51664     },
51665     
51666     _setLoaded : function(){
51667         this.loaded = true;
51668     }, 
51669     
51670     /**
51671      * Returns this panel's id
51672      * @return {String} 
51673      */
51674     getId : function(){
51675         return this.el.id;
51676     },
51677     
51678     /** 
51679      * Returns this panel's element - used by regiosn to add.
51680      * @return {Roo.Element} 
51681      */
51682     getEl : function(){
51683         return this.wrapEl || this.el;
51684     },
51685     
51686     adjustForComponents : function(width, height)
51687     {
51688         //Roo.log('adjustForComponents ');
51689         if(this.resizeEl != this.el){
51690             width -= this.el.getFrameWidth('lr');
51691             height -= this.el.getFrameWidth('tb');
51692         }
51693         if(this.toolbar){
51694             var te = this.toolbar.getEl();
51695             height -= te.getHeight();
51696             te.setWidth(width);
51697         }
51698         if(this.footer){
51699             var te = this.footer.getEl();
51700             Roo.log("footer:" + te.getHeight());
51701             
51702             height -= te.getHeight();
51703             te.setWidth(width);
51704         }
51705         
51706         
51707         if(this.adjustments){
51708             width += this.adjustments[0];
51709             height += this.adjustments[1];
51710         }
51711         return {"width": width, "height": height};
51712     },
51713     
51714     setSize : function(width, height){
51715         if(this.fitToFrame && !this.ignoreResize(width, height)){
51716             if(this.fitContainer && this.resizeEl != this.el){
51717                 this.el.setSize(width, height);
51718             }
51719             var size = this.adjustForComponents(width, height);
51720             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51721             this.fireEvent('resize', this, size.width, size.height);
51722         }
51723     },
51724     
51725     /**
51726      * Returns this panel's title
51727      * @return {String} 
51728      */
51729     getTitle : function(){
51730         return this.title;
51731     },
51732     
51733     /**
51734      * Set this panel's title
51735      * @param {String} title
51736      */
51737     setTitle : function(title){
51738         this.title = title;
51739         if(this.region){
51740             this.region.updatePanelTitle(this, title);
51741         }
51742     },
51743     
51744     /**
51745      * Returns true is this panel was configured to be closable
51746      * @return {Boolean} 
51747      */
51748     isClosable : function(){
51749         return this.closable;
51750     },
51751     
51752     beforeSlide : function(){
51753         this.el.clip();
51754         this.resizeEl.clip();
51755     },
51756     
51757     afterSlide : function(){
51758         this.el.unclip();
51759         this.resizeEl.unclip();
51760     },
51761     
51762     /**
51763      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51764      *   Will fail silently if the {@link #setUrl} method has not been called.
51765      *   This does not activate the panel, just updates its content.
51766      */
51767     refresh : function(){
51768         if(this.refreshDelegate){
51769            this.loaded = false;
51770            this.refreshDelegate();
51771         }
51772     },
51773     
51774     /**
51775      * Destroys this panel
51776      */
51777     destroy : function(){
51778         this.el.removeAllListeners();
51779         var tempEl = document.createElement("span");
51780         tempEl.appendChild(this.el.dom);
51781         tempEl.innerHTML = "";
51782         this.el.remove();
51783         this.el = null;
51784     },
51785     
51786     /**
51787      * form - if the content panel contains a form - this is a reference to it.
51788      * @type {Roo.form.Form}
51789      */
51790     form : false,
51791     /**
51792      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51793      *    This contains a reference to it.
51794      * @type {Roo.View}
51795      */
51796     view : false,
51797     
51798       /**
51799      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51800      * <pre><code>
51801
51802 layout.addxtype({
51803        xtype : 'Form',
51804        items: [ .... ]
51805    }
51806 );
51807
51808 </code></pre>
51809      * @param {Object} cfg Xtype definition of item to add.
51810      */
51811     
51812     addxtype : function(cfg) {
51813         // add form..
51814         if (cfg.xtype.match(/^Form$/)) {
51815             
51816             var el;
51817             //if (this.footer) {
51818             //    el = this.footer.container.insertSibling(false, 'before');
51819             //} else {
51820                 el = this.el.createChild();
51821             //}
51822
51823             this.form = new  Roo.form.Form(cfg);
51824             
51825             
51826             if ( this.form.allItems.length) this.form.render(el.dom);
51827             return this.form;
51828         }
51829         // should only have one of theses..
51830         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51831             // views.. should not be just added - used named prop 'view''
51832             
51833             cfg.el = this.el.appendChild(document.createElement("div"));
51834             // factory?
51835             
51836             var ret = new Roo.factory(cfg);
51837              
51838              ret.render && ret.render(false, ''); // render blank..
51839             this.view = ret;
51840             return ret;
51841         }
51842         return false;
51843     }
51844 });
51845
51846 /**
51847  * @class Roo.GridPanel
51848  * @extends Roo.ContentPanel
51849  * @constructor
51850  * Create a new GridPanel.
51851  * @param {Roo.grid.Grid} grid The grid for this panel
51852  * @param {String/Object} config A string to set only the panel's title, or a config object
51853  */
51854 Roo.GridPanel = function(grid, config){
51855     
51856   
51857     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51858         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51859         
51860     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51861     
51862     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51863     
51864     if(this.toolbar){
51865         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51866     }
51867     // xtype created footer. - not sure if will work as we normally have to render first..
51868     if (this.footer && !this.footer.el && this.footer.xtype) {
51869         
51870         this.footer.container = this.grid.getView().getFooterPanel(true);
51871         this.footer.dataSource = this.grid.dataSource;
51872         this.footer = Roo.factory(this.footer, Roo);
51873         
51874     }
51875     
51876     grid.monitorWindowResize = false; // turn off autosizing
51877     grid.autoHeight = false;
51878     grid.autoWidth = false;
51879     this.grid = grid;
51880     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51881 };
51882
51883 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51884     getId : function(){
51885         return this.grid.id;
51886     },
51887     
51888     /**
51889      * Returns the grid for this panel
51890      * @return {Roo.grid.Grid} 
51891      */
51892     getGrid : function(){
51893         return this.grid;    
51894     },
51895     
51896     setSize : function(width, height){
51897         if(!this.ignoreResize(width, height)){
51898             var grid = this.grid;
51899             var size = this.adjustForComponents(width, height);
51900             grid.getGridEl().setSize(size.width, size.height);
51901             grid.autoSize();
51902         }
51903     },
51904     
51905     beforeSlide : function(){
51906         this.grid.getView().scroller.clip();
51907     },
51908     
51909     afterSlide : function(){
51910         this.grid.getView().scroller.unclip();
51911     },
51912     
51913     destroy : function(){
51914         this.grid.destroy();
51915         delete this.grid;
51916         Roo.GridPanel.superclass.destroy.call(this); 
51917     }
51918 });
51919
51920
51921 /**
51922  * @class Roo.NestedLayoutPanel
51923  * @extends Roo.ContentPanel
51924  * @constructor
51925  * Create a new NestedLayoutPanel.
51926  * 
51927  * 
51928  * @param {Roo.BorderLayout} layout The layout for this panel
51929  * @param {String/Object} config A string to set only the title or a config object
51930  */
51931 Roo.NestedLayoutPanel = function(layout, config)
51932 {
51933     // construct with only one argument..
51934     /* FIXME - implement nicer consturctors
51935     if (layout.layout) {
51936         config = layout;
51937         layout = config.layout;
51938         delete config.layout;
51939     }
51940     if (layout.xtype && !layout.getEl) {
51941         // then layout needs constructing..
51942         layout = Roo.factory(layout, Roo);
51943     }
51944     */
51945     
51946     
51947     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51948     
51949     layout.monitorWindowResize = false; // turn off autosizing
51950     this.layout = layout;
51951     this.layout.getEl().addClass("x-layout-nested-layout");
51952     
51953     
51954     
51955     
51956 };
51957
51958 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51959
51960     setSize : function(width, height){
51961         if(!this.ignoreResize(width, height)){
51962             var size = this.adjustForComponents(width, height);
51963             var el = this.layout.getEl();
51964             el.setSize(size.width, size.height);
51965             var touch = el.dom.offsetWidth;
51966             this.layout.layout();
51967             // ie requires a double layout on the first pass
51968             if(Roo.isIE && !this.initialized){
51969                 this.initialized = true;
51970                 this.layout.layout();
51971             }
51972         }
51973     },
51974     
51975     // activate all subpanels if not currently active..
51976     
51977     setActiveState : function(active){
51978         this.active = active;
51979         if(!active){
51980             this.fireEvent("deactivate", this);
51981             return;
51982         }
51983         
51984         this.fireEvent("activate", this);
51985         // not sure if this should happen before or after..
51986         if (!this.layout) {
51987             return; // should not happen..
51988         }
51989         var reg = false;
51990         for (var r in this.layout.regions) {
51991             reg = this.layout.getRegion(r);
51992             if (reg.getActivePanel()) {
51993                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51994                 reg.setActivePanel(reg.getActivePanel());
51995                 continue;
51996             }
51997             if (!reg.panels.length) {
51998                 continue;
51999             }
52000             reg.showPanel(reg.getPanel(0));
52001         }
52002         
52003         
52004         
52005         
52006     },
52007     
52008     /**
52009      * Returns the nested BorderLayout for this panel
52010      * @return {Roo.BorderLayout} 
52011      */
52012     getLayout : function(){
52013         return this.layout;
52014     },
52015     
52016      /**
52017      * Adds a xtype elements to the layout of the nested panel
52018      * <pre><code>
52019
52020 panel.addxtype({
52021        xtype : 'ContentPanel',
52022        region: 'west',
52023        items: [ .... ]
52024    }
52025 );
52026
52027 panel.addxtype({
52028         xtype : 'NestedLayoutPanel',
52029         region: 'west',
52030         layout: {
52031            center: { },
52032            west: { }   
52033         },
52034         items : [ ... list of content panels or nested layout panels.. ]
52035    }
52036 );
52037 </code></pre>
52038      * @param {Object} cfg Xtype definition of item to add.
52039      */
52040     addxtype : function(cfg) {
52041         return this.layout.addxtype(cfg);
52042     
52043     }
52044 });
52045
52046 Roo.ScrollPanel = function(el, config, content){
52047     config = config || {};
52048     config.fitToFrame = true;
52049     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52050     
52051     this.el.dom.style.overflow = "hidden";
52052     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52053     this.el.removeClass("x-layout-inactive-content");
52054     this.el.on("mousewheel", this.onWheel, this);
52055
52056     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52057     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52058     up.unselectable(); down.unselectable();
52059     up.on("click", this.scrollUp, this);
52060     down.on("click", this.scrollDown, this);
52061     up.addClassOnOver("x-scroller-btn-over");
52062     down.addClassOnOver("x-scroller-btn-over");
52063     up.addClassOnClick("x-scroller-btn-click");
52064     down.addClassOnClick("x-scroller-btn-click");
52065     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52066
52067     this.resizeEl = this.el;
52068     this.el = wrap; this.up = up; this.down = down;
52069 };
52070
52071 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52072     increment : 100,
52073     wheelIncrement : 5,
52074     scrollUp : function(){
52075         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52076     },
52077
52078     scrollDown : function(){
52079         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52080     },
52081
52082     afterScroll : function(){
52083         var el = this.resizeEl;
52084         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52085         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52086         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52087     },
52088
52089     setSize : function(){
52090         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52091         this.afterScroll();
52092     },
52093
52094     onWheel : function(e){
52095         var d = e.getWheelDelta();
52096         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52097         this.afterScroll();
52098         e.stopEvent();
52099     },
52100
52101     setContent : function(content, loadScripts){
52102         this.resizeEl.update(content, loadScripts);
52103     }
52104
52105 });
52106
52107
52108
52109
52110
52111
52112
52113
52114
52115 /**
52116  * @class Roo.TreePanel
52117  * @extends Roo.ContentPanel
52118  * @constructor
52119  * Create a new TreePanel. - defaults to fit/scoll contents.
52120  * @param {String/Object} config A string to set only the panel's title, or a config object
52121  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52122  */
52123 Roo.TreePanel = function(config){
52124     var el = config.el;
52125     var tree = config.tree;
52126     delete config.tree; 
52127     delete config.el; // hopefull!
52128     
52129     // wrapper for IE7 strict & safari scroll issue
52130     
52131     var treeEl = el.createChild();
52132     config.resizeEl = treeEl;
52133     
52134     
52135     
52136     Roo.TreePanel.superclass.constructor.call(this, el, config);
52137  
52138  
52139     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52140     //console.log(tree);
52141     this.on('activate', function()
52142     {
52143         if (this.tree.rendered) {
52144             return;
52145         }
52146         //console.log('render tree');
52147         this.tree.render();
52148     });
52149     // this should not be needed.. - it's actually the 'el' that resizes?
52150     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52151     
52152     //this.on('resize',  function (cp, w, h) {
52153     //        this.tree.innerCt.setWidth(w);
52154     //        this.tree.innerCt.setHeight(h);
52155     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52156     //});
52157
52158         
52159     
52160 };
52161
52162 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52163     fitToFrame : true,
52164     autoScroll : true
52165 });
52166
52167
52168
52169
52170
52171
52172
52173
52174
52175
52176
52177 /*
52178  * Based on:
52179  * Ext JS Library 1.1.1
52180  * Copyright(c) 2006-2007, Ext JS, LLC.
52181  *
52182  * Originally Released Under LGPL - original licence link has changed is not relivant.
52183  *
52184  * Fork - LGPL
52185  * <script type="text/javascript">
52186  */
52187  
52188
52189 /**
52190  * @class Roo.ReaderLayout
52191  * @extends Roo.BorderLayout
52192  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52193  * center region containing two nested regions (a top one for a list view and one for item preview below),
52194  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52195  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52196  * expedites the setup of the overall layout and regions for this common application style.
52197  * Example:
52198  <pre><code>
52199 var reader = new Roo.ReaderLayout();
52200 var CP = Roo.ContentPanel;  // shortcut for adding
52201
52202 reader.beginUpdate();
52203 reader.add("north", new CP("north", "North"));
52204 reader.add("west", new CP("west", {title: "West"}));
52205 reader.add("east", new CP("east", {title: "East"}));
52206
52207 reader.regions.listView.add(new CP("listView", "List"));
52208 reader.regions.preview.add(new CP("preview", "Preview"));
52209 reader.endUpdate();
52210 </code></pre>
52211 * @constructor
52212 * Create a new ReaderLayout
52213 * @param {Object} config Configuration options
52214 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52215 * document.body if omitted)
52216 */
52217 Roo.ReaderLayout = function(config, renderTo){
52218     var c = config || {size:{}};
52219     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52220         north: c.north !== false ? Roo.apply({
52221             split:false,
52222             initialSize: 32,
52223             titlebar: false
52224         }, c.north) : false,
52225         west: c.west !== false ? Roo.apply({
52226             split:true,
52227             initialSize: 200,
52228             minSize: 175,
52229             maxSize: 400,
52230             titlebar: true,
52231             collapsible: true,
52232             animate: true,
52233             margins:{left:5,right:0,bottom:5,top:5},
52234             cmargins:{left:5,right:5,bottom:5,top:5}
52235         }, c.west) : false,
52236         east: c.east !== false ? Roo.apply({
52237             split:true,
52238             initialSize: 200,
52239             minSize: 175,
52240             maxSize: 400,
52241             titlebar: true,
52242             collapsible: true,
52243             animate: true,
52244             margins:{left:0,right:5,bottom:5,top:5},
52245             cmargins:{left:5,right:5,bottom:5,top:5}
52246         }, c.east) : false,
52247         center: Roo.apply({
52248             tabPosition: 'top',
52249             autoScroll:false,
52250             closeOnTab: true,
52251             titlebar:false,
52252             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52253         }, c.center)
52254     });
52255
52256     this.el.addClass('x-reader');
52257
52258     this.beginUpdate();
52259
52260     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52261         south: c.preview !== false ? Roo.apply({
52262             split:true,
52263             initialSize: 200,
52264             minSize: 100,
52265             autoScroll:true,
52266             collapsible:true,
52267             titlebar: true,
52268             cmargins:{top:5,left:0, right:0, bottom:0}
52269         }, c.preview) : false,
52270         center: Roo.apply({
52271             autoScroll:false,
52272             titlebar:false,
52273             minHeight:200
52274         }, c.listView)
52275     });
52276     this.add('center', new Roo.NestedLayoutPanel(inner,
52277             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52278
52279     this.endUpdate();
52280
52281     this.regions.preview = inner.getRegion('south');
52282     this.regions.listView = inner.getRegion('center');
52283 };
52284
52285 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52286  * Based on:
52287  * Ext JS Library 1.1.1
52288  * Copyright(c) 2006-2007, Ext JS, LLC.
52289  *
52290  * Originally Released Under LGPL - original licence link has changed is not relivant.
52291  *
52292  * Fork - LGPL
52293  * <script type="text/javascript">
52294  */
52295  
52296 /**
52297  * @class Roo.grid.Grid
52298  * @extends Roo.util.Observable
52299  * This class represents the primary interface of a component based grid control.
52300  * <br><br>Usage:<pre><code>
52301  var grid = new Roo.grid.Grid("my-container-id", {
52302      ds: myDataStore,
52303      cm: myColModel,
52304      selModel: mySelectionModel,
52305      autoSizeColumns: true,
52306      monitorWindowResize: false,
52307      trackMouseOver: true
52308  });
52309  // set any options
52310  grid.render();
52311  * </code></pre>
52312  * <b>Common Problems:</b><br/>
52313  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52314  * element will correct this<br/>
52315  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52316  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52317  * are unpredictable.<br/>
52318  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52319  * grid to calculate dimensions/offsets.<br/>
52320   * @constructor
52321  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52322  * The container MUST have some type of size defined for the grid to fill. The container will be
52323  * automatically set to position relative if it isn't already.
52324  * @param {Object} config A config object that sets properties on this grid.
52325  */
52326 Roo.grid.Grid = function(container, config){
52327         // initialize the container
52328         this.container = Roo.get(container);
52329         this.container.update("");
52330         this.container.setStyle("overflow", "hidden");
52331     this.container.addClass('x-grid-container');
52332
52333     this.id = this.container.id;
52334
52335     Roo.apply(this, config);
52336     // check and correct shorthanded configs
52337     if(this.ds){
52338         this.dataSource = this.ds;
52339         delete this.ds;
52340     }
52341     if(this.cm){
52342         this.colModel = this.cm;
52343         delete this.cm;
52344     }
52345     if(this.sm){
52346         this.selModel = this.sm;
52347         delete this.sm;
52348     }
52349
52350     if (this.selModel) {
52351         this.selModel = Roo.factory(this.selModel, Roo.grid);
52352         this.sm = this.selModel;
52353         this.sm.xmodule = this.xmodule || false;
52354     }
52355     if (typeof(this.colModel.config) == 'undefined') {
52356         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52357         this.cm = this.colModel;
52358         this.cm.xmodule = this.xmodule || false;
52359     }
52360     if (this.dataSource) {
52361         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52362         this.ds = this.dataSource;
52363         this.ds.xmodule = this.xmodule || false;
52364          
52365     }
52366     
52367     
52368     
52369     if(this.width){
52370         this.container.setWidth(this.width);
52371     }
52372
52373     if(this.height){
52374         this.container.setHeight(this.height);
52375     }
52376     /** @private */
52377         this.addEvents({
52378         // raw events
52379         /**
52380          * @event click
52381          * The raw click event for the entire grid.
52382          * @param {Roo.EventObject} e
52383          */
52384         "click" : true,
52385         /**
52386          * @event dblclick
52387          * The raw dblclick event for the entire grid.
52388          * @param {Roo.EventObject} e
52389          */
52390         "dblclick" : true,
52391         /**
52392          * @event contextmenu
52393          * The raw contextmenu event for the entire grid.
52394          * @param {Roo.EventObject} e
52395          */
52396         "contextmenu" : true,
52397         /**
52398          * @event mousedown
52399          * The raw mousedown event for the entire grid.
52400          * @param {Roo.EventObject} e
52401          */
52402         "mousedown" : true,
52403         /**
52404          * @event mouseup
52405          * The raw mouseup event for the entire grid.
52406          * @param {Roo.EventObject} e
52407          */
52408         "mouseup" : true,
52409         /**
52410          * @event mouseover
52411          * The raw mouseover event for the entire grid.
52412          * @param {Roo.EventObject} e
52413          */
52414         "mouseover" : true,
52415         /**
52416          * @event mouseout
52417          * The raw mouseout event for the entire grid.
52418          * @param {Roo.EventObject} e
52419          */
52420         "mouseout" : true,
52421         /**
52422          * @event keypress
52423          * The raw keypress event for the entire grid.
52424          * @param {Roo.EventObject} e
52425          */
52426         "keypress" : true,
52427         /**
52428          * @event keydown
52429          * The raw keydown event for the entire grid.
52430          * @param {Roo.EventObject} e
52431          */
52432         "keydown" : true,
52433
52434         // custom events
52435
52436         /**
52437          * @event cellclick
52438          * Fires when a cell is clicked
52439          * @param {Grid} this
52440          * @param {Number} rowIndex
52441          * @param {Number} columnIndex
52442          * @param {Roo.EventObject} e
52443          */
52444         "cellclick" : true,
52445         /**
52446          * @event celldblclick
52447          * Fires when a cell is double clicked
52448          * @param {Grid} this
52449          * @param {Number} rowIndex
52450          * @param {Number} columnIndex
52451          * @param {Roo.EventObject} e
52452          */
52453         "celldblclick" : true,
52454         /**
52455          * @event rowclick
52456          * Fires when a row is clicked
52457          * @param {Grid} this
52458          * @param {Number} rowIndex
52459          * @param {Roo.EventObject} e
52460          */
52461         "rowclick" : true,
52462         /**
52463          * @event rowdblclick
52464          * Fires when a row is double clicked
52465          * @param {Grid} this
52466          * @param {Number} rowIndex
52467          * @param {Roo.EventObject} e
52468          */
52469         "rowdblclick" : true,
52470         /**
52471          * @event headerclick
52472          * Fires when a header is clicked
52473          * @param {Grid} this
52474          * @param {Number} columnIndex
52475          * @param {Roo.EventObject} e
52476          */
52477         "headerclick" : true,
52478         /**
52479          * @event headerdblclick
52480          * Fires when a header cell is double clicked
52481          * @param {Grid} this
52482          * @param {Number} columnIndex
52483          * @param {Roo.EventObject} e
52484          */
52485         "headerdblclick" : true,
52486         /**
52487          * @event rowcontextmenu
52488          * Fires when a row is right clicked
52489          * @param {Grid} this
52490          * @param {Number} rowIndex
52491          * @param {Roo.EventObject} e
52492          */
52493         "rowcontextmenu" : true,
52494         /**
52495          * @event cellcontextmenu
52496          * Fires when a cell is right clicked
52497          * @param {Grid} this
52498          * @param {Number} rowIndex
52499          * @param {Number} cellIndex
52500          * @param {Roo.EventObject} e
52501          */
52502          "cellcontextmenu" : true,
52503         /**
52504          * @event headercontextmenu
52505          * Fires when a header is right clicked
52506          * @param {Grid} this
52507          * @param {Number} columnIndex
52508          * @param {Roo.EventObject} e
52509          */
52510         "headercontextmenu" : true,
52511         /**
52512          * @event bodyscroll
52513          * Fires when the body element is scrolled
52514          * @param {Number} scrollLeft
52515          * @param {Number} scrollTop
52516          */
52517         "bodyscroll" : true,
52518         /**
52519          * @event columnresize
52520          * Fires when the user resizes a column
52521          * @param {Number} columnIndex
52522          * @param {Number} newSize
52523          */
52524         "columnresize" : true,
52525         /**
52526          * @event columnmove
52527          * Fires when the user moves a column
52528          * @param {Number} oldIndex
52529          * @param {Number} newIndex
52530          */
52531         "columnmove" : true,
52532         /**
52533          * @event startdrag
52534          * Fires when row(s) start being dragged
52535          * @param {Grid} this
52536          * @param {Roo.GridDD} dd The drag drop object
52537          * @param {event} e The raw browser event
52538          */
52539         "startdrag" : true,
52540         /**
52541          * @event enddrag
52542          * Fires when a drag operation is complete
52543          * @param {Grid} this
52544          * @param {Roo.GridDD} dd The drag drop object
52545          * @param {event} e The raw browser event
52546          */
52547         "enddrag" : true,
52548         /**
52549          * @event dragdrop
52550          * Fires when dragged row(s) are dropped on a valid DD target
52551          * @param {Grid} this
52552          * @param {Roo.GridDD} dd The drag drop object
52553          * @param {String} targetId The target drag drop object
52554          * @param {event} e The raw browser event
52555          */
52556         "dragdrop" : true,
52557         /**
52558          * @event dragover
52559          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52560          * @param {Grid} this
52561          * @param {Roo.GridDD} dd The drag drop object
52562          * @param {String} targetId The target drag drop object
52563          * @param {event} e The raw browser event
52564          */
52565         "dragover" : true,
52566         /**
52567          * @event dragenter
52568          *  Fires when the dragged row(s) first cross another DD target while being dragged
52569          * @param {Grid} this
52570          * @param {Roo.GridDD} dd The drag drop object
52571          * @param {String} targetId The target drag drop object
52572          * @param {event} e The raw browser event
52573          */
52574         "dragenter" : true,
52575         /**
52576          * @event dragout
52577          * Fires when the dragged row(s) leave another DD target while being dragged
52578          * @param {Grid} this
52579          * @param {Roo.GridDD} dd The drag drop object
52580          * @param {String} targetId The target drag drop object
52581          * @param {event} e The raw browser event
52582          */
52583         "dragout" : true,
52584         /**
52585          * @event rowclass
52586          * Fires when a row is rendered, so you can change add a style to it.
52587          * @param {GridView} gridview   The grid view
52588          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52589          */
52590         'rowclass' : true,
52591
52592         /**
52593          * @event render
52594          * Fires when the grid is rendered
52595          * @param {Grid} grid
52596          */
52597         'render' : true
52598     });
52599
52600     Roo.grid.Grid.superclass.constructor.call(this);
52601 };
52602 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52603     
52604     /**
52605      * @cfg {String} ddGroup - drag drop group.
52606      */
52607
52608     /**
52609      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52610      */
52611     minColumnWidth : 25,
52612
52613     /**
52614      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52615      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52616      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52617      */
52618     autoSizeColumns : false,
52619
52620     /**
52621      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52622      */
52623     autoSizeHeaders : true,
52624
52625     /**
52626      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52627      */
52628     monitorWindowResize : true,
52629
52630     /**
52631      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52632      * rows measured to get a columns size. Default is 0 (all rows).
52633      */
52634     maxRowsToMeasure : 0,
52635
52636     /**
52637      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52638      */
52639     trackMouseOver : true,
52640
52641     /**
52642     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52643     */
52644     
52645     /**
52646     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52647     */
52648     enableDragDrop : false,
52649     
52650     /**
52651     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52652     */
52653     enableColumnMove : true,
52654     
52655     /**
52656     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52657     */
52658     enableColumnHide : true,
52659     
52660     /**
52661     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52662     */
52663     enableRowHeightSync : false,
52664     
52665     /**
52666     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52667     */
52668     stripeRows : true,
52669     
52670     /**
52671     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52672     */
52673     autoHeight : false,
52674
52675     /**
52676      * @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.
52677      */
52678     autoExpandColumn : false,
52679
52680     /**
52681     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52682     * Default is 50.
52683     */
52684     autoExpandMin : 50,
52685
52686     /**
52687     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52688     */
52689     autoExpandMax : 1000,
52690
52691     /**
52692     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52693     */
52694     view : null,
52695
52696     /**
52697     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52698     */
52699     loadMask : false,
52700     /**
52701     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52702     */
52703     dropTarget: false,
52704     
52705    
52706     
52707     // private
52708     rendered : false,
52709
52710     /**
52711     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52712     * of a fixed width. Default is false.
52713     */
52714     /**
52715     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52716     */
52717     /**
52718      * Called once after all setup has been completed and the grid is ready to be rendered.
52719      * @return {Roo.grid.Grid} this
52720      */
52721     render : function()
52722     {
52723         var c = this.container;
52724         // try to detect autoHeight/width mode
52725         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52726             this.autoHeight = true;
52727         }
52728         var view = this.getView();
52729         view.init(this);
52730
52731         c.on("click", this.onClick, this);
52732         c.on("dblclick", this.onDblClick, this);
52733         c.on("contextmenu", this.onContextMenu, this);
52734         c.on("keydown", this.onKeyDown, this);
52735         if (Roo.isTouch) {
52736             c.on("touchstart", this.onTouchStart, this);
52737         }
52738
52739         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52740
52741         this.getSelectionModel().init(this);
52742
52743         view.render();
52744
52745         if(this.loadMask){
52746             this.loadMask = new Roo.LoadMask(this.container,
52747                     Roo.apply({store:this.dataSource}, this.loadMask));
52748         }
52749         
52750         
52751         if (this.toolbar && this.toolbar.xtype) {
52752             this.toolbar.container = this.getView().getHeaderPanel(true);
52753             this.toolbar = new Roo.Toolbar(this.toolbar);
52754         }
52755         if (this.footer && this.footer.xtype) {
52756             this.footer.dataSource = this.getDataSource();
52757             this.footer.container = this.getView().getFooterPanel(true);
52758             this.footer = Roo.factory(this.footer, Roo);
52759         }
52760         if (this.dropTarget && this.dropTarget.xtype) {
52761             delete this.dropTarget.xtype;
52762             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52763         }
52764         
52765         
52766         this.rendered = true;
52767         this.fireEvent('render', this);
52768         return this;
52769     },
52770
52771         /**
52772          * Reconfigures the grid to use a different Store and Column Model.
52773          * The View will be bound to the new objects and refreshed.
52774          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52775          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52776          */
52777     reconfigure : function(dataSource, colModel){
52778         if(this.loadMask){
52779             this.loadMask.destroy();
52780             this.loadMask = new Roo.LoadMask(this.container,
52781                     Roo.apply({store:dataSource}, this.loadMask));
52782         }
52783         this.view.bind(dataSource, colModel);
52784         this.dataSource = dataSource;
52785         this.colModel = colModel;
52786         this.view.refresh(true);
52787     },
52788
52789     // private
52790     onKeyDown : function(e){
52791         this.fireEvent("keydown", e);
52792     },
52793
52794     /**
52795      * Destroy this grid.
52796      * @param {Boolean} removeEl True to remove the element
52797      */
52798     destroy : function(removeEl, keepListeners){
52799         if(this.loadMask){
52800             this.loadMask.destroy();
52801         }
52802         var c = this.container;
52803         c.removeAllListeners();
52804         this.view.destroy();
52805         this.colModel.purgeListeners();
52806         if(!keepListeners){
52807             this.purgeListeners();
52808         }
52809         c.update("");
52810         if(removeEl === true){
52811             c.remove();
52812         }
52813     },
52814
52815     // private
52816     processEvent : function(name, e){
52817         // does this fire select???
52818         //Roo.log('grid:processEvent '  + name);
52819         
52820         if (name != 'touchstart' ) {
52821             this.fireEvent(name, e);    
52822         }
52823         
52824         var t = e.getTarget();
52825         var v = this.view;
52826         var header = v.findHeaderIndex(t);
52827         if(header !== false){
52828             var ename = name == 'touchstart' ? 'click' : name;
52829              
52830             this.fireEvent("header" + ename, this, header, e);
52831         }else{
52832             var row = v.findRowIndex(t);
52833             var cell = v.findCellIndex(t);
52834             if (name == 'touchstart') {
52835                 // first touch is always a click.
52836                 // hopefull this happens after selection is updated.?
52837                 name = false;
52838                 
52839                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52840                     var cs = this.selModel.getSelectedCell();
52841                     if (row == cs[0] && cell == cs[1]){
52842                         name = 'dblclick';
52843                     }
52844                 }
52845                 if (typeof(this.selModel.getSelections) != 'undefined') {
52846                     var cs = this.selModel.getSelections();
52847                     var ds = this.dataSource;
52848                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52849                         name = 'dblclick';
52850                     }
52851                 }
52852                 if (!name) {
52853                     return;
52854                 }
52855             }
52856             
52857             
52858             if(row !== false){
52859                 this.fireEvent("row" + name, this, row, e);
52860                 if(cell !== false){
52861                     this.fireEvent("cell" + name, this, row, cell, e);
52862                 }
52863             }
52864         }
52865     },
52866
52867     // private
52868     onClick : function(e){
52869         this.processEvent("click", e);
52870     },
52871    // private
52872     onTouchStart : function(e){
52873         this.processEvent("touchstart", e);
52874     },
52875
52876     // private
52877     onContextMenu : function(e, t){
52878         this.processEvent("contextmenu", e);
52879     },
52880
52881     // private
52882     onDblClick : function(e){
52883         this.processEvent("dblclick", e);
52884     },
52885
52886     // private
52887     walkCells : function(row, col, step, fn, scope){
52888         var cm = this.colModel, clen = cm.getColumnCount();
52889         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52890         if(step < 0){
52891             if(col < 0){
52892                 row--;
52893                 first = false;
52894             }
52895             while(row >= 0){
52896                 if(!first){
52897                     col = clen-1;
52898                 }
52899                 first = false;
52900                 while(col >= 0){
52901                     if(fn.call(scope || this, row, col, cm) === true){
52902                         return [row, col];
52903                     }
52904                     col--;
52905                 }
52906                 row--;
52907             }
52908         } else {
52909             if(col >= clen){
52910                 row++;
52911                 first = false;
52912             }
52913             while(row < rlen){
52914                 if(!first){
52915                     col = 0;
52916                 }
52917                 first = false;
52918                 while(col < clen){
52919                     if(fn.call(scope || this, row, col, cm) === true){
52920                         return [row, col];
52921                     }
52922                     col++;
52923                 }
52924                 row++;
52925             }
52926         }
52927         return null;
52928     },
52929
52930     // private
52931     getSelections : function(){
52932         return this.selModel.getSelections();
52933     },
52934
52935     /**
52936      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52937      * but if manual update is required this method will initiate it.
52938      */
52939     autoSize : function(){
52940         if(this.rendered){
52941             this.view.layout();
52942             if(this.view.adjustForScroll){
52943                 this.view.adjustForScroll();
52944             }
52945         }
52946     },
52947
52948     /**
52949      * Returns the grid's underlying element.
52950      * @return {Element} The element
52951      */
52952     getGridEl : function(){
52953         return this.container;
52954     },
52955
52956     // private for compatibility, overridden by editor grid
52957     stopEditing : function(){},
52958
52959     /**
52960      * Returns the grid's SelectionModel.
52961      * @return {SelectionModel}
52962      */
52963     getSelectionModel : function(){
52964         if(!this.selModel){
52965             this.selModel = new Roo.grid.RowSelectionModel();
52966         }
52967         return this.selModel;
52968     },
52969
52970     /**
52971      * Returns the grid's DataSource.
52972      * @return {DataSource}
52973      */
52974     getDataSource : function(){
52975         return this.dataSource;
52976     },
52977
52978     /**
52979      * Returns the grid's ColumnModel.
52980      * @return {ColumnModel}
52981      */
52982     getColumnModel : function(){
52983         return this.colModel;
52984     },
52985
52986     /**
52987      * Returns the grid's GridView object.
52988      * @return {GridView}
52989      */
52990     getView : function(){
52991         if(!this.view){
52992             this.view = new Roo.grid.GridView(this.viewConfig);
52993         }
52994         return this.view;
52995     },
52996     /**
52997      * Called to get grid's drag proxy text, by default returns this.ddText.
52998      * @return {String}
52999      */
53000     getDragDropText : function(){
53001         var count = this.selModel.getCount();
53002         return String.format(this.ddText, count, count == 1 ? '' : 's');
53003     }
53004 });
53005 /**
53006  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53007  * %0 is replaced with the number of selected rows.
53008  * @type String
53009  */
53010 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53011  * Based on:
53012  * Ext JS Library 1.1.1
53013  * Copyright(c) 2006-2007, Ext JS, LLC.
53014  *
53015  * Originally Released Under LGPL - original licence link has changed is not relivant.
53016  *
53017  * Fork - LGPL
53018  * <script type="text/javascript">
53019  */
53020  
53021 Roo.grid.AbstractGridView = function(){
53022         this.grid = null;
53023         
53024         this.events = {
53025             "beforerowremoved" : true,
53026             "beforerowsinserted" : true,
53027             "beforerefresh" : true,
53028             "rowremoved" : true,
53029             "rowsinserted" : true,
53030             "rowupdated" : true,
53031             "refresh" : true
53032         };
53033     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53034 };
53035
53036 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53037     rowClass : "x-grid-row",
53038     cellClass : "x-grid-cell",
53039     tdClass : "x-grid-td",
53040     hdClass : "x-grid-hd",
53041     splitClass : "x-grid-hd-split",
53042     
53043     init: function(grid){
53044         this.grid = grid;
53045                 var cid = this.grid.getGridEl().id;
53046         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53047         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53048         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53049         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53050         },
53051         
53052     getColumnRenderers : function(){
53053         var renderers = [];
53054         var cm = this.grid.colModel;
53055         var colCount = cm.getColumnCount();
53056         for(var i = 0; i < colCount; i++){
53057             renderers[i] = cm.getRenderer(i);
53058         }
53059         return renderers;
53060     },
53061     
53062     getColumnIds : function(){
53063         var ids = [];
53064         var cm = this.grid.colModel;
53065         var colCount = cm.getColumnCount();
53066         for(var i = 0; i < colCount; i++){
53067             ids[i] = cm.getColumnId(i);
53068         }
53069         return ids;
53070     },
53071     
53072     getDataIndexes : function(){
53073         if(!this.indexMap){
53074             this.indexMap = this.buildIndexMap();
53075         }
53076         return this.indexMap.colToData;
53077     },
53078     
53079     getColumnIndexByDataIndex : function(dataIndex){
53080         if(!this.indexMap){
53081             this.indexMap = this.buildIndexMap();
53082         }
53083         return this.indexMap.dataToCol[dataIndex];
53084     },
53085     
53086     /**
53087      * Set a css style for a column dynamically. 
53088      * @param {Number} colIndex The index of the column
53089      * @param {String} name The css property name
53090      * @param {String} value The css value
53091      */
53092     setCSSStyle : function(colIndex, name, value){
53093         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53094         Roo.util.CSS.updateRule(selector, name, value);
53095     },
53096     
53097     generateRules : function(cm){
53098         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53099         Roo.util.CSS.removeStyleSheet(rulesId);
53100         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53101             var cid = cm.getColumnId(i);
53102             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53103                          this.tdSelector, cid, " {\n}\n",
53104                          this.hdSelector, cid, " {\n}\n",
53105                          this.splitSelector, cid, " {\n}\n");
53106         }
53107         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53108     }
53109 });/*
53110  * Based on:
53111  * Ext JS Library 1.1.1
53112  * Copyright(c) 2006-2007, Ext JS, LLC.
53113  *
53114  * Originally Released Under LGPL - original licence link has changed is not relivant.
53115  *
53116  * Fork - LGPL
53117  * <script type="text/javascript">
53118  */
53119
53120 // private
53121 // This is a support class used internally by the Grid components
53122 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53123     this.grid = grid;
53124     this.view = grid.getView();
53125     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53126     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53127     if(hd2){
53128         this.setHandleElId(Roo.id(hd));
53129         this.setOuterHandleElId(Roo.id(hd2));
53130     }
53131     this.scroll = false;
53132 };
53133 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53134     maxDragWidth: 120,
53135     getDragData : function(e){
53136         var t = Roo.lib.Event.getTarget(e);
53137         var h = this.view.findHeaderCell(t);
53138         if(h){
53139             return {ddel: h.firstChild, header:h};
53140         }
53141         return false;
53142     },
53143
53144     onInitDrag : function(e){
53145         this.view.headersDisabled = true;
53146         var clone = this.dragData.ddel.cloneNode(true);
53147         clone.id = Roo.id();
53148         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53149         this.proxy.update(clone);
53150         return true;
53151     },
53152
53153     afterValidDrop : function(){
53154         var v = this.view;
53155         setTimeout(function(){
53156             v.headersDisabled = false;
53157         }, 50);
53158     },
53159
53160     afterInvalidDrop : function(){
53161         var v = this.view;
53162         setTimeout(function(){
53163             v.headersDisabled = false;
53164         }, 50);
53165     }
53166 });
53167 /*
53168  * Based on:
53169  * Ext JS Library 1.1.1
53170  * Copyright(c) 2006-2007, Ext JS, LLC.
53171  *
53172  * Originally Released Under LGPL - original licence link has changed is not relivant.
53173  *
53174  * Fork - LGPL
53175  * <script type="text/javascript">
53176  */
53177 // private
53178 // This is a support class used internally by the Grid components
53179 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53180     this.grid = grid;
53181     this.view = grid.getView();
53182     // split the proxies so they don't interfere with mouse events
53183     this.proxyTop = Roo.DomHelper.append(document.body, {
53184         cls:"col-move-top", html:"&#160;"
53185     }, true);
53186     this.proxyBottom = Roo.DomHelper.append(document.body, {
53187         cls:"col-move-bottom", html:"&#160;"
53188     }, true);
53189     this.proxyTop.hide = this.proxyBottom.hide = function(){
53190         this.setLeftTop(-100,-100);
53191         this.setStyle("visibility", "hidden");
53192     };
53193     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53194     // temporarily disabled
53195     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53196     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53197 };
53198 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53199     proxyOffsets : [-4, -9],
53200     fly: Roo.Element.fly,
53201
53202     getTargetFromEvent : function(e){
53203         var t = Roo.lib.Event.getTarget(e);
53204         var cindex = this.view.findCellIndex(t);
53205         if(cindex !== false){
53206             return this.view.getHeaderCell(cindex);
53207         }
53208         return null;
53209     },
53210
53211     nextVisible : function(h){
53212         var v = this.view, cm = this.grid.colModel;
53213         h = h.nextSibling;
53214         while(h){
53215             if(!cm.isHidden(v.getCellIndex(h))){
53216                 return h;
53217             }
53218             h = h.nextSibling;
53219         }
53220         return null;
53221     },
53222
53223     prevVisible : function(h){
53224         var v = this.view, cm = this.grid.colModel;
53225         h = h.prevSibling;
53226         while(h){
53227             if(!cm.isHidden(v.getCellIndex(h))){
53228                 return h;
53229             }
53230             h = h.prevSibling;
53231         }
53232         return null;
53233     },
53234
53235     positionIndicator : function(h, n, e){
53236         var x = Roo.lib.Event.getPageX(e);
53237         var r = Roo.lib.Dom.getRegion(n.firstChild);
53238         var px, pt, py = r.top + this.proxyOffsets[1];
53239         if((r.right - x) <= (r.right-r.left)/2){
53240             px = r.right+this.view.borderWidth;
53241             pt = "after";
53242         }else{
53243             px = r.left;
53244             pt = "before";
53245         }
53246         var oldIndex = this.view.getCellIndex(h);
53247         var newIndex = this.view.getCellIndex(n);
53248
53249         if(this.grid.colModel.isFixed(newIndex)){
53250             return false;
53251         }
53252
53253         var locked = this.grid.colModel.isLocked(newIndex);
53254
53255         if(pt == "after"){
53256             newIndex++;
53257         }
53258         if(oldIndex < newIndex){
53259             newIndex--;
53260         }
53261         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53262             return false;
53263         }
53264         px +=  this.proxyOffsets[0];
53265         this.proxyTop.setLeftTop(px, py);
53266         this.proxyTop.show();
53267         if(!this.bottomOffset){
53268             this.bottomOffset = this.view.mainHd.getHeight();
53269         }
53270         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53271         this.proxyBottom.show();
53272         return pt;
53273     },
53274
53275     onNodeEnter : function(n, dd, e, data){
53276         if(data.header != n){
53277             this.positionIndicator(data.header, n, e);
53278         }
53279     },
53280
53281     onNodeOver : function(n, dd, e, data){
53282         var result = false;
53283         if(data.header != n){
53284             result = this.positionIndicator(data.header, n, e);
53285         }
53286         if(!result){
53287             this.proxyTop.hide();
53288             this.proxyBottom.hide();
53289         }
53290         return result ? this.dropAllowed : this.dropNotAllowed;
53291     },
53292
53293     onNodeOut : function(n, dd, e, data){
53294         this.proxyTop.hide();
53295         this.proxyBottom.hide();
53296     },
53297
53298     onNodeDrop : function(n, dd, e, data){
53299         var h = data.header;
53300         if(h != n){
53301             var cm = this.grid.colModel;
53302             var x = Roo.lib.Event.getPageX(e);
53303             var r = Roo.lib.Dom.getRegion(n.firstChild);
53304             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53305             var oldIndex = this.view.getCellIndex(h);
53306             var newIndex = this.view.getCellIndex(n);
53307             var locked = cm.isLocked(newIndex);
53308             if(pt == "after"){
53309                 newIndex++;
53310             }
53311             if(oldIndex < newIndex){
53312                 newIndex--;
53313             }
53314             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53315                 return false;
53316             }
53317             cm.setLocked(oldIndex, locked, true);
53318             cm.moveColumn(oldIndex, newIndex);
53319             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53320             return true;
53321         }
53322         return false;
53323     }
53324 });
53325 /*
53326  * Based on:
53327  * Ext JS Library 1.1.1
53328  * Copyright(c) 2006-2007, Ext JS, LLC.
53329  *
53330  * Originally Released Under LGPL - original licence link has changed is not relivant.
53331  *
53332  * Fork - LGPL
53333  * <script type="text/javascript">
53334  */
53335   
53336 /**
53337  * @class Roo.grid.GridView
53338  * @extends Roo.util.Observable
53339  *
53340  * @constructor
53341  * @param {Object} config
53342  */
53343 Roo.grid.GridView = function(config){
53344     Roo.grid.GridView.superclass.constructor.call(this);
53345     this.el = null;
53346
53347     Roo.apply(this, config);
53348 };
53349
53350 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53351
53352     unselectable :  'unselectable="on"',
53353     unselectableCls :  'x-unselectable',
53354     
53355     
53356     rowClass : "x-grid-row",
53357
53358     cellClass : "x-grid-col",
53359
53360     tdClass : "x-grid-td",
53361
53362     hdClass : "x-grid-hd",
53363
53364     splitClass : "x-grid-split",
53365
53366     sortClasses : ["sort-asc", "sort-desc"],
53367
53368     enableMoveAnim : false,
53369
53370     hlColor: "C3DAF9",
53371
53372     dh : Roo.DomHelper,
53373
53374     fly : Roo.Element.fly,
53375
53376     css : Roo.util.CSS,
53377
53378     borderWidth: 1,
53379
53380     splitOffset: 3,
53381
53382     scrollIncrement : 22,
53383
53384     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53385
53386     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53387
53388     bind : function(ds, cm){
53389         if(this.ds){
53390             this.ds.un("load", this.onLoad, this);
53391             this.ds.un("datachanged", this.onDataChange, this);
53392             this.ds.un("add", this.onAdd, this);
53393             this.ds.un("remove", this.onRemove, this);
53394             this.ds.un("update", this.onUpdate, this);
53395             this.ds.un("clear", this.onClear, this);
53396         }
53397         if(ds){
53398             ds.on("load", this.onLoad, this);
53399             ds.on("datachanged", this.onDataChange, this);
53400             ds.on("add", this.onAdd, this);
53401             ds.on("remove", this.onRemove, this);
53402             ds.on("update", this.onUpdate, this);
53403             ds.on("clear", this.onClear, this);
53404         }
53405         this.ds = ds;
53406
53407         if(this.cm){
53408             this.cm.un("widthchange", this.onColWidthChange, this);
53409             this.cm.un("headerchange", this.onHeaderChange, this);
53410             this.cm.un("hiddenchange", this.onHiddenChange, this);
53411             this.cm.un("columnmoved", this.onColumnMove, this);
53412             this.cm.un("columnlockchange", this.onColumnLock, this);
53413         }
53414         if(cm){
53415             this.generateRules(cm);
53416             cm.on("widthchange", this.onColWidthChange, this);
53417             cm.on("headerchange", this.onHeaderChange, this);
53418             cm.on("hiddenchange", this.onHiddenChange, this);
53419             cm.on("columnmoved", this.onColumnMove, this);
53420             cm.on("columnlockchange", this.onColumnLock, this);
53421         }
53422         this.cm = cm;
53423     },
53424
53425     init: function(grid){
53426         Roo.grid.GridView.superclass.init.call(this, grid);
53427
53428         this.bind(grid.dataSource, grid.colModel);
53429
53430         grid.on("headerclick", this.handleHeaderClick, this);
53431
53432         if(grid.trackMouseOver){
53433             grid.on("mouseover", this.onRowOver, this);
53434             grid.on("mouseout", this.onRowOut, this);
53435         }
53436         grid.cancelTextSelection = function(){};
53437         this.gridId = grid.id;
53438
53439         var tpls = this.templates || {};
53440
53441         if(!tpls.master){
53442             tpls.master = new Roo.Template(
53443                '<div class="x-grid" hidefocus="true">',
53444                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53445                   '<div class="x-grid-topbar"></div>',
53446                   '<div class="x-grid-scroller"><div></div></div>',
53447                   '<div class="x-grid-locked">',
53448                       '<div class="x-grid-header">{lockedHeader}</div>',
53449                       '<div class="x-grid-body">{lockedBody}</div>',
53450                   "</div>",
53451                   '<div class="x-grid-viewport">',
53452                       '<div class="x-grid-header">{header}</div>',
53453                       '<div class="x-grid-body">{body}</div>',
53454                   "</div>",
53455                   '<div class="x-grid-bottombar"></div>',
53456                  
53457                   '<div class="x-grid-resize-proxy">&#160;</div>',
53458                "</div>"
53459             );
53460             tpls.master.disableformats = true;
53461         }
53462
53463         if(!tpls.header){
53464             tpls.header = new Roo.Template(
53465                '<table border="0" cellspacing="0" cellpadding="0">',
53466                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53467                "</table>{splits}"
53468             );
53469             tpls.header.disableformats = true;
53470         }
53471         tpls.header.compile();
53472
53473         if(!tpls.hcell){
53474             tpls.hcell = new Roo.Template(
53475                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53476                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53477                 "</div></td>"
53478              );
53479              tpls.hcell.disableFormats = true;
53480         }
53481         tpls.hcell.compile();
53482
53483         if(!tpls.hsplit){
53484             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53485                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53486             tpls.hsplit.disableFormats = true;
53487         }
53488         tpls.hsplit.compile();
53489
53490         if(!tpls.body){
53491             tpls.body = new Roo.Template(
53492                '<table border="0" cellspacing="0" cellpadding="0">',
53493                "<tbody>{rows}</tbody>",
53494                "</table>"
53495             );
53496             tpls.body.disableFormats = true;
53497         }
53498         tpls.body.compile();
53499
53500         if(!tpls.row){
53501             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53502             tpls.row.disableFormats = true;
53503         }
53504         tpls.row.compile();
53505
53506         if(!tpls.cell){
53507             tpls.cell = new Roo.Template(
53508                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53509                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53510                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53511                 "</td>"
53512             );
53513             tpls.cell.disableFormats = true;
53514         }
53515         tpls.cell.compile();
53516
53517         this.templates = tpls;
53518     },
53519
53520     // remap these for backwards compat
53521     onColWidthChange : function(){
53522         this.updateColumns.apply(this, arguments);
53523     },
53524     onHeaderChange : function(){
53525         this.updateHeaders.apply(this, arguments);
53526     }, 
53527     onHiddenChange : function(){
53528         this.handleHiddenChange.apply(this, arguments);
53529     },
53530     onColumnMove : function(){
53531         this.handleColumnMove.apply(this, arguments);
53532     },
53533     onColumnLock : function(){
53534         this.handleLockChange.apply(this, arguments);
53535     },
53536
53537     onDataChange : function(){
53538         this.refresh();
53539         this.updateHeaderSortState();
53540     },
53541
53542     onClear : function(){
53543         this.refresh();
53544     },
53545
53546     onUpdate : function(ds, record){
53547         this.refreshRow(record);
53548     },
53549
53550     refreshRow : function(record){
53551         var ds = this.ds, index;
53552         if(typeof record == 'number'){
53553             index = record;
53554             record = ds.getAt(index);
53555         }else{
53556             index = ds.indexOf(record);
53557         }
53558         this.insertRows(ds, index, index, true);
53559         this.onRemove(ds, record, index+1, true);
53560         this.syncRowHeights(index, index);
53561         this.layout();
53562         this.fireEvent("rowupdated", this, index, record);
53563     },
53564
53565     onAdd : function(ds, records, index){
53566         this.insertRows(ds, index, index + (records.length-1));
53567     },
53568
53569     onRemove : function(ds, record, index, isUpdate){
53570         if(isUpdate !== true){
53571             this.fireEvent("beforerowremoved", this, index, record);
53572         }
53573         var bt = this.getBodyTable(), lt = this.getLockedTable();
53574         if(bt.rows[index]){
53575             bt.firstChild.removeChild(bt.rows[index]);
53576         }
53577         if(lt.rows[index]){
53578             lt.firstChild.removeChild(lt.rows[index]);
53579         }
53580         if(isUpdate !== true){
53581             this.stripeRows(index);
53582             this.syncRowHeights(index, index);
53583             this.layout();
53584             this.fireEvent("rowremoved", this, index, record);
53585         }
53586     },
53587
53588     onLoad : function(){
53589         this.scrollToTop();
53590     },
53591
53592     /**
53593      * Scrolls the grid to the top
53594      */
53595     scrollToTop : function(){
53596         if(this.scroller){
53597             this.scroller.dom.scrollTop = 0;
53598             this.syncScroll();
53599         }
53600     },
53601
53602     /**
53603      * Gets a panel in the header of the grid that can be used for toolbars etc.
53604      * After modifying the contents of this panel a call to grid.autoSize() may be
53605      * required to register any changes in size.
53606      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53607      * @return Roo.Element
53608      */
53609     getHeaderPanel : function(doShow){
53610         if(doShow){
53611             this.headerPanel.show();
53612         }
53613         return this.headerPanel;
53614     },
53615
53616     /**
53617      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53618      * After modifying the contents of this panel a call to grid.autoSize() may be
53619      * required to register any changes in size.
53620      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53621      * @return Roo.Element
53622      */
53623     getFooterPanel : function(doShow){
53624         if(doShow){
53625             this.footerPanel.show();
53626         }
53627         return this.footerPanel;
53628     },
53629
53630     initElements : function(){
53631         var E = Roo.Element;
53632         var el = this.grid.getGridEl().dom.firstChild;
53633         var cs = el.childNodes;
53634
53635         this.el = new E(el);
53636         
53637          this.focusEl = new E(el.firstChild);
53638         this.focusEl.swallowEvent("click", true);
53639         
53640         this.headerPanel = new E(cs[1]);
53641         this.headerPanel.enableDisplayMode("block");
53642
53643         this.scroller = new E(cs[2]);
53644         this.scrollSizer = new E(this.scroller.dom.firstChild);
53645
53646         this.lockedWrap = new E(cs[3]);
53647         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53648         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53649
53650         this.mainWrap = new E(cs[4]);
53651         this.mainHd = new E(this.mainWrap.dom.firstChild);
53652         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53653
53654         this.footerPanel = new E(cs[5]);
53655         this.footerPanel.enableDisplayMode("block");
53656
53657         this.resizeProxy = new E(cs[6]);
53658
53659         this.headerSelector = String.format(
53660            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53661            this.lockedHd.id, this.mainHd.id
53662         );
53663
53664         this.splitterSelector = String.format(
53665            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53666            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53667         );
53668     },
53669     idToCssName : function(s)
53670     {
53671         return s.replace(/[^a-z0-9]+/ig, '-');
53672     },
53673
53674     getHeaderCell : function(index){
53675         return Roo.DomQuery.select(this.headerSelector)[index];
53676     },
53677
53678     getHeaderCellMeasure : function(index){
53679         return this.getHeaderCell(index).firstChild;
53680     },
53681
53682     getHeaderCellText : function(index){
53683         return this.getHeaderCell(index).firstChild.firstChild;
53684     },
53685
53686     getLockedTable : function(){
53687         return this.lockedBody.dom.firstChild;
53688     },
53689
53690     getBodyTable : function(){
53691         return this.mainBody.dom.firstChild;
53692     },
53693
53694     getLockedRow : function(index){
53695         return this.getLockedTable().rows[index];
53696     },
53697
53698     getRow : function(index){
53699         return this.getBodyTable().rows[index];
53700     },
53701
53702     getRowComposite : function(index){
53703         if(!this.rowEl){
53704             this.rowEl = new Roo.CompositeElementLite();
53705         }
53706         var els = [], lrow, mrow;
53707         if(lrow = this.getLockedRow(index)){
53708             els.push(lrow);
53709         }
53710         if(mrow = this.getRow(index)){
53711             els.push(mrow);
53712         }
53713         this.rowEl.elements = els;
53714         return this.rowEl;
53715     },
53716     /**
53717      * Gets the 'td' of the cell
53718      * 
53719      * @param {Integer} rowIndex row to select
53720      * @param {Integer} colIndex column to select
53721      * 
53722      * @return {Object} 
53723      */
53724     getCell : function(rowIndex, colIndex){
53725         var locked = this.cm.getLockedCount();
53726         var source;
53727         if(colIndex < locked){
53728             source = this.lockedBody.dom.firstChild;
53729         }else{
53730             source = this.mainBody.dom.firstChild;
53731             colIndex -= locked;
53732         }
53733         return source.rows[rowIndex].childNodes[colIndex];
53734     },
53735
53736     getCellText : function(rowIndex, colIndex){
53737         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53738     },
53739
53740     getCellBox : function(cell){
53741         var b = this.fly(cell).getBox();
53742         if(Roo.isOpera){ // opera fails to report the Y
53743             b.y = cell.offsetTop + this.mainBody.getY();
53744         }
53745         return b;
53746     },
53747
53748     getCellIndex : function(cell){
53749         var id = String(cell.className).match(this.cellRE);
53750         if(id){
53751             return parseInt(id[1], 10);
53752         }
53753         return 0;
53754     },
53755
53756     findHeaderIndex : function(n){
53757         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53758         return r ? this.getCellIndex(r) : false;
53759     },
53760
53761     findHeaderCell : function(n){
53762         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53763         return r ? r : false;
53764     },
53765
53766     findRowIndex : function(n){
53767         if(!n){
53768             return false;
53769         }
53770         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53771         return r ? r.rowIndex : false;
53772     },
53773
53774     findCellIndex : function(node){
53775         var stop = this.el.dom;
53776         while(node && node != stop){
53777             if(this.findRE.test(node.className)){
53778                 return this.getCellIndex(node);
53779             }
53780             node = node.parentNode;
53781         }
53782         return false;
53783     },
53784
53785     getColumnId : function(index){
53786         return this.cm.getColumnId(index);
53787     },
53788
53789     getSplitters : function()
53790     {
53791         if(this.splitterSelector){
53792            return Roo.DomQuery.select(this.splitterSelector);
53793         }else{
53794             return null;
53795       }
53796     },
53797
53798     getSplitter : function(index){
53799         return this.getSplitters()[index];
53800     },
53801
53802     onRowOver : function(e, t){
53803         var row;
53804         if((row = this.findRowIndex(t)) !== false){
53805             this.getRowComposite(row).addClass("x-grid-row-over");
53806         }
53807     },
53808
53809     onRowOut : function(e, t){
53810         var row;
53811         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53812             this.getRowComposite(row).removeClass("x-grid-row-over");
53813         }
53814     },
53815
53816     renderHeaders : function(){
53817         var cm = this.cm;
53818         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53819         var cb = [], lb = [], sb = [], lsb = [], p = {};
53820         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53821             p.cellId = "x-grid-hd-0-" + i;
53822             p.splitId = "x-grid-csplit-0-" + i;
53823             p.id = cm.getColumnId(i);
53824             p.title = cm.getColumnTooltip(i) || "";
53825             p.value = cm.getColumnHeader(i) || "";
53826             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53827             if(!cm.isLocked(i)){
53828                 cb[cb.length] = ct.apply(p);
53829                 sb[sb.length] = st.apply(p);
53830             }else{
53831                 lb[lb.length] = ct.apply(p);
53832                 lsb[lsb.length] = st.apply(p);
53833             }
53834         }
53835         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53836                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53837     },
53838
53839     updateHeaders : function(){
53840         var html = this.renderHeaders();
53841         this.lockedHd.update(html[0]);
53842         this.mainHd.update(html[1]);
53843     },
53844
53845     /**
53846      * Focuses the specified row.
53847      * @param {Number} row The row index
53848      */
53849     focusRow : function(row)
53850     {
53851         //Roo.log('GridView.focusRow');
53852         var x = this.scroller.dom.scrollLeft;
53853         this.focusCell(row, 0, false);
53854         this.scroller.dom.scrollLeft = x;
53855     },
53856
53857     /**
53858      * Focuses the specified cell.
53859      * @param {Number} row The row index
53860      * @param {Number} col The column index
53861      * @param {Boolean} hscroll false to disable horizontal scrolling
53862      */
53863     focusCell : function(row, col, hscroll)
53864     {
53865         //Roo.log('GridView.focusCell');
53866         var el = this.ensureVisible(row, col, hscroll);
53867         this.focusEl.alignTo(el, "tl-tl");
53868         if(Roo.isGecko){
53869             this.focusEl.focus();
53870         }else{
53871             this.focusEl.focus.defer(1, this.focusEl);
53872         }
53873     },
53874
53875     /**
53876      * Scrolls the specified cell into view
53877      * @param {Number} row The row index
53878      * @param {Number} col The column index
53879      * @param {Boolean} hscroll false to disable horizontal scrolling
53880      */
53881     ensureVisible : function(row, col, hscroll)
53882     {
53883         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53884         //return null; //disable for testing.
53885         if(typeof row != "number"){
53886             row = row.rowIndex;
53887         }
53888         if(row < 0 && row >= this.ds.getCount()){
53889             return  null;
53890         }
53891         col = (col !== undefined ? col : 0);
53892         var cm = this.grid.colModel;
53893         while(cm.isHidden(col)){
53894             col++;
53895         }
53896
53897         var el = this.getCell(row, col);
53898         if(!el){
53899             return null;
53900         }
53901         var c = this.scroller.dom;
53902
53903         var ctop = parseInt(el.offsetTop, 10);
53904         var cleft = parseInt(el.offsetLeft, 10);
53905         var cbot = ctop + el.offsetHeight;
53906         var cright = cleft + el.offsetWidth;
53907         
53908         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53909         var stop = parseInt(c.scrollTop, 10);
53910         var sleft = parseInt(c.scrollLeft, 10);
53911         var sbot = stop + ch;
53912         var sright = sleft + c.clientWidth;
53913         /*
53914         Roo.log('GridView.ensureVisible:' +
53915                 ' ctop:' + ctop +
53916                 ' c.clientHeight:' + c.clientHeight +
53917                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53918                 ' stop:' + stop +
53919                 ' cbot:' + cbot +
53920                 ' sbot:' + sbot +
53921                 ' ch:' + ch  
53922                 );
53923         */
53924         if(ctop < stop){
53925              c.scrollTop = ctop;
53926             //Roo.log("set scrolltop to ctop DISABLE?");
53927         }else if(cbot > sbot){
53928             //Roo.log("set scrolltop to cbot-ch");
53929             c.scrollTop = cbot-ch;
53930         }
53931         
53932         if(hscroll !== false){
53933             if(cleft < sleft){
53934                 c.scrollLeft = cleft;
53935             }else if(cright > sright){
53936                 c.scrollLeft = cright-c.clientWidth;
53937             }
53938         }
53939          
53940         return el;
53941     },
53942
53943     updateColumns : function(){
53944         this.grid.stopEditing();
53945         var cm = this.grid.colModel, colIds = this.getColumnIds();
53946         //var totalWidth = cm.getTotalWidth();
53947         var pos = 0;
53948         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53949             //if(cm.isHidden(i)) continue;
53950             var w = cm.getColumnWidth(i);
53951             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53952             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53953         }
53954         this.updateSplitters();
53955     },
53956
53957     generateRules : function(cm){
53958         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53959         Roo.util.CSS.removeStyleSheet(rulesId);
53960         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53961             var cid = cm.getColumnId(i);
53962             var align = '';
53963             if(cm.config[i].align){
53964                 align = 'text-align:'+cm.config[i].align+';';
53965             }
53966             var hidden = '';
53967             if(cm.isHidden(i)){
53968                 hidden = 'display:none;';
53969             }
53970             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53971             ruleBuf.push(
53972                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53973                     this.hdSelector, cid, " {\n", align, width, "}\n",
53974                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53975                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53976         }
53977         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53978     },
53979
53980     updateSplitters : function(){
53981         var cm = this.cm, s = this.getSplitters();
53982         if(s){ // splitters not created yet
53983             var pos = 0, locked = true;
53984             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53985                 if(cm.isHidden(i)) continue;
53986                 var w = cm.getColumnWidth(i); // make sure it's a number
53987                 if(!cm.isLocked(i) && locked){
53988                     pos = 0;
53989                     locked = false;
53990                 }
53991                 pos += w;
53992                 s[i].style.left = (pos-this.splitOffset) + "px";
53993             }
53994         }
53995     },
53996
53997     handleHiddenChange : function(colModel, colIndex, hidden){
53998         if(hidden){
53999             this.hideColumn(colIndex);
54000         }else{
54001             this.unhideColumn(colIndex);
54002         }
54003     },
54004
54005     hideColumn : function(colIndex){
54006         var cid = this.getColumnId(colIndex);
54007         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54008         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54009         if(Roo.isSafari){
54010             this.updateHeaders();
54011         }
54012         this.updateSplitters();
54013         this.layout();
54014     },
54015
54016     unhideColumn : function(colIndex){
54017         var cid = this.getColumnId(colIndex);
54018         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54019         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54020
54021         if(Roo.isSafari){
54022             this.updateHeaders();
54023         }
54024         this.updateSplitters();
54025         this.layout();
54026     },
54027
54028     insertRows : function(dm, firstRow, lastRow, isUpdate){
54029         if(firstRow == 0 && lastRow == dm.getCount()-1){
54030             this.refresh();
54031         }else{
54032             if(!isUpdate){
54033                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54034             }
54035             var s = this.getScrollState();
54036             var markup = this.renderRows(firstRow, lastRow);
54037             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54038             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54039             this.restoreScroll(s);
54040             if(!isUpdate){
54041                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54042                 this.syncRowHeights(firstRow, lastRow);
54043                 this.stripeRows(firstRow);
54044                 this.layout();
54045             }
54046         }
54047     },
54048
54049     bufferRows : function(markup, target, index){
54050         var before = null, trows = target.rows, tbody = target.tBodies[0];
54051         if(index < trows.length){
54052             before = trows[index];
54053         }
54054         var b = document.createElement("div");
54055         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54056         var rows = b.firstChild.rows;
54057         for(var i = 0, len = rows.length; i < len; i++){
54058             if(before){
54059                 tbody.insertBefore(rows[0], before);
54060             }else{
54061                 tbody.appendChild(rows[0]);
54062             }
54063         }
54064         b.innerHTML = "";
54065         b = null;
54066     },
54067
54068     deleteRows : function(dm, firstRow, lastRow){
54069         if(dm.getRowCount()<1){
54070             this.fireEvent("beforerefresh", this);
54071             this.mainBody.update("");
54072             this.lockedBody.update("");
54073             this.fireEvent("refresh", this);
54074         }else{
54075             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54076             var bt = this.getBodyTable();
54077             var tbody = bt.firstChild;
54078             var rows = bt.rows;
54079             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54080                 tbody.removeChild(rows[firstRow]);
54081             }
54082             this.stripeRows(firstRow);
54083             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54084         }
54085     },
54086
54087     updateRows : function(dataSource, firstRow, lastRow){
54088         var s = this.getScrollState();
54089         this.refresh();
54090         this.restoreScroll(s);
54091     },
54092
54093     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54094         if(!noRefresh){
54095            this.refresh();
54096         }
54097         this.updateHeaderSortState();
54098     },
54099
54100     getScrollState : function(){
54101         
54102         var sb = this.scroller.dom;
54103         return {left: sb.scrollLeft, top: sb.scrollTop};
54104     },
54105
54106     stripeRows : function(startRow){
54107         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54108             return;
54109         }
54110         startRow = startRow || 0;
54111         var rows = this.getBodyTable().rows;
54112         var lrows = this.getLockedTable().rows;
54113         var cls = ' x-grid-row-alt ';
54114         for(var i = startRow, len = rows.length; i < len; i++){
54115             var row = rows[i], lrow = lrows[i];
54116             var isAlt = ((i+1) % 2 == 0);
54117             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54118             if(isAlt == hasAlt){
54119                 continue;
54120             }
54121             if(isAlt){
54122                 row.className += " x-grid-row-alt";
54123             }else{
54124                 row.className = row.className.replace("x-grid-row-alt", "");
54125             }
54126             if(lrow){
54127                 lrow.className = row.className;
54128             }
54129         }
54130     },
54131
54132     restoreScroll : function(state){
54133         //Roo.log('GridView.restoreScroll');
54134         var sb = this.scroller.dom;
54135         sb.scrollLeft = state.left;
54136         sb.scrollTop = state.top;
54137         this.syncScroll();
54138     },
54139
54140     syncScroll : function(){
54141         //Roo.log('GridView.syncScroll');
54142         var sb = this.scroller.dom;
54143         var sh = this.mainHd.dom;
54144         var bs = this.mainBody.dom;
54145         var lv = this.lockedBody.dom;
54146         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54147         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54148     },
54149
54150     handleScroll : function(e){
54151         this.syncScroll();
54152         var sb = this.scroller.dom;
54153         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54154         e.stopEvent();
54155     },
54156
54157     handleWheel : function(e){
54158         var d = e.getWheelDelta();
54159         this.scroller.dom.scrollTop -= d*22;
54160         // set this here to prevent jumpy scrolling on large tables
54161         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54162         e.stopEvent();
54163     },
54164
54165     renderRows : function(startRow, endRow){
54166         // pull in all the crap needed to render rows
54167         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54168         var colCount = cm.getColumnCount();
54169
54170         if(ds.getCount() < 1){
54171             return ["", ""];
54172         }
54173
54174         // build a map for all the columns
54175         var cs = [];
54176         for(var i = 0; i < colCount; i++){
54177             var name = cm.getDataIndex(i);
54178             cs[i] = {
54179                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54180                 renderer : cm.getRenderer(i),
54181                 id : cm.getColumnId(i),
54182                 locked : cm.isLocked(i)
54183             };
54184         }
54185
54186         startRow = startRow || 0;
54187         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54188
54189         // records to render
54190         var rs = ds.getRange(startRow, endRow);
54191
54192         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54193     },
54194
54195     // As much as I hate to duplicate code, this was branched because FireFox really hates
54196     // [].join("") on strings. The performance difference was substantial enough to
54197     // branch this function
54198     doRender : Roo.isGecko ?
54199             function(cs, rs, ds, startRow, colCount, stripe){
54200                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54201                 // buffers
54202                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54203                 
54204                 var hasListener = this.grid.hasListener('rowclass');
54205                 var rowcfg = {};
54206                 for(var j = 0, len = rs.length; j < len; j++){
54207                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54208                     for(var i = 0; i < colCount; i++){
54209                         c = cs[i];
54210                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54211                         p.id = c.id;
54212                         p.css = p.attr = "";
54213                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54214                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54215                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54216                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54217                         }
54218                         var markup = ct.apply(p);
54219                         if(!c.locked){
54220                             cb+= markup;
54221                         }else{
54222                             lcb+= markup;
54223                         }
54224                     }
54225                     var alt = [];
54226                     if(stripe && ((rowIndex+1) % 2 == 0)){
54227                         alt.push("x-grid-row-alt")
54228                     }
54229                     if(r.dirty){
54230                         alt.push(  " x-grid-dirty-row");
54231                     }
54232                     rp.cells = lcb;
54233                     if(this.getRowClass){
54234                         alt.push(this.getRowClass(r, rowIndex));
54235                     }
54236                     if (hasListener) {
54237                         rowcfg = {
54238                              
54239                             record: r,
54240                             rowIndex : rowIndex,
54241                             rowClass : ''
54242                         }
54243                         this.grid.fireEvent('rowclass', this, rowcfg);
54244                         alt.push(rowcfg.rowClass);
54245                     }
54246                     rp.alt = alt.join(" ");
54247                     lbuf+= rt.apply(rp);
54248                     rp.cells = cb;
54249                     buf+=  rt.apply(rp);
54250                 }
54251                 return [lbuf, buf];
54252             } :
54253             function(cs, rs, ds, startRow, colCount, stripe){
54254                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54255                 // buffers
54256                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54257                 var hasListener = this.grid.hasListener('rowclass');
54258  
54259                 var rowcfg = {};
54260                 for(var j = 0, len = rs.length; j < len; j++){
54261                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54262                     for(var i = 0; i < colCount; i++){
54263                         c = cs[i];
54264                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54265                         p.id = c.id;
54266                         p.css = p.attr = "";
54267                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54268                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54269                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54270                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54271                         }
54272                         
54273                         var markup = ct.apply(p);
54274                         if(!c.locked){
54275                             cb[cb.length] = markup;
54276                         }else{
54277                             lcb[lcb.length] = markup;
54278                         }
54279                     }
54280                     var alt = [];
54281                     if(stripe && ((rowIndex+1) % 2 == 0)){
54282                         alt.push( "x-grid-row-alt");
54283                     }
54284                     if(r.dirty){
54285                         alt.push(" x-grid-dirty-row");
54286                     }
54287                     rp.cells = lcb;
54288                     if(this.getRowClass){
54289                         alt.push( this.getRowClass(r, rowIndex));
54290                     }
54291                     if (hasListener) {
54292                         rowcfg = {
54293                              
54294                             record: r,
54295                             rowIndex : rowIndex,
54296                             rowClass : ''
54297                         }
54298                         this.grid.fireEvent('rowclass', this, rowcfg);
54299                         alt.push(rowcfg.rowClass);
54300                     }
54301                     rp.alt = alt.join(" ");
54302                     rp.cells = lcb.join("");
54303                     lbuf[lbuf.length] = rt.apply(rp);
54304                     rp.cells = cb.join("");
54305                     buf[buf.length] =  rt.apply(rp);
54306                 }
54307                 return [lbuf.join(""), buf.join("")];
54308             },
54309
54310     renderBody : function(){
54311         var markup = this.renderRows();
54312         var bt = this.templates.body;
54313         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54314     },
54315
54316     /**
54317      * Refreshes the grid
54318      * @param {Boolean} headersToo
54319      */
54320     refresh : function(headersToo){
54321         this.fireEvent("beforerefresh", this);
54322         this.grid.stopEditing();
54323         var result = this.renderBody();
54324         this.lockedBody.update(result[0]);
54325         this.mainBody.update(result[1]);
54326         if(headersToo === true){
54327             this.updateHeaders();
54328             this.updateColumns();
54329             this.updateSplitters();
54330             this.updateHeaderSortState();
54331         }
54332         this.syncRowHeights();
54333         this.layout();
54334         this.fireEvent("refresh", this);
54335     },
54336
54337     handleColumnMove : function(cm, oldIndex, newIndex){
54338         this.indexMap = null;
54339         var s = this.getScrollState();
54340         this.refresh(true);
54341         this.restoreScroll(s);
54342         this.afterMove(newIndex);
54343     },
54344
54345     afterMove : function(colIndex){
54346         if(this.enableMoveAnim && Roo.enableFx){
54347             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54348         }
54349         // if multisort - fix sortOrder, and reload..
54350         if (this.grid.dataSource.multiSort) {
54351             // the we can call sort again..
54352             var dm = this.grid.dataSource;
54353             var cm = this.grid.colModel;
54354             var so = [];
54355             for(var i = 0; i < cm.config.length; i++ ) {
54356                 
54357                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54358                     continue; // dont' bother, it's not in sort list or being set.
54359                 }
54360                 
54361                 so.push(cm.config[i].dataIndex);
54362             };
54363             dm.sortOrder = so;
54364             dm.load(dm.lastOptions);
54365             
54366             
54367         }
54368         
54369     },
54370
54371     updateCell : function(dm, rowIndex, dataIndex){
54372         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54373         if(typeof colIndex == "undefined"){ // not present in grid
54374             return;
54375         }
54376         var cm = this.grid.colModel;
54377         var cell = this.getCell(rowIndex, colIndex);
54378         var cellText = this.getCellText(rowIndex, colIndex);
54379
54380         var p = {
54381             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54382             id : cm.getColumnId(colIndex),
54383             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54384         };
54385         var renderer = cm.getRenderer(colIndex);
54386         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54387         if(typeof val == "undefined" || val === "") val = "&#160;";
54388         cellText.innerHTML = val;
54389         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54390         this.syncRowHeights(rowIndex, rowIndex);
54391     },
54392
54393     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54394         var maxWidth = 0;
54395         if(this.grid.autoSizeHeaders){
54396             var h = this.getHeaderCellMeasure(colIndex);
54397             maxWidth = Math.max(maxWidth, h.scrollWidth);
54398         }
54399         var tb, index;
54400         if(this.cm.isLocked(colIndex)){
54401             tb = this.getLockedTable();
54402             index = colIndex;
54403         }else{
54404             tb = this.getBodyTable();
54405             index = colIndex - this.cm.getLockedCount();
54406         }
54407         if(tb && tb.rows){
54408             var rows = tb.rows;
54409             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54410             for(var i = 0; i < stopIndex; i++){
54411                 var cell = rows[i].childNodes[index].firstChild;
54412                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54413             }
54414         }
54415         return maxWidth + /*margin for error in IE*/ 5;
54416     },
54417     /**
54418      * Autofit a column to its content.
54419      * @param {Number} colIndex
54420      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54421      */
54422      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54423          if(this.cm.isHidden(colIndex)){
54424              return; // can't calc a hidden column
54425          }
54426         if(forceMinSize){
54427             var cid = this.cm.getColumnId(colIndex);
54428             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54429            if(this.grid.autoSizeHeaders){
54430                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54431            }
54432         }
54433         var newWidth = this.calcColumnWidth(colIndex);
54434         this.cm.setColumnWidth(colIndex,
54435             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54436         if(!suppressEvent){
54437             this.grid.fireEvent("columnresize", colIndex, newWidth);
54438         }
54439     },
54440
54441     /**
54442      * Autofits all columns to their content and then expands to fit any extra space in the grid
54443      */
54444      autoSizeColumns : function(){
54445         var cm = this.grid.colModel;
54446         var colCount = cm.getColumnCount();
54447         for(var i = 0; i < colCount; i++){
54448             this.autoSizeColumn(i, true, true);
54449         }
54450         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54451             this.fitColumns();
54452         }else{
54453             this.updateColumns();
54454             this.layout();
54455         }
54456     },
54457
54458     /**
54459      * Autofits all columns to the grid's width proportionate with their current size
54460      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54461      */
54462     fitColumns : function(reserveScrollSpace){
54463         var cm = this.grid.colModel;
54464         var colCount = cm.getColumnCount();
54465         var cols = [];
54466         var width = 0;
54467         var i, w;
54468         for (i = 0; i < colCount; i++){
54469             if(!cm.isHidden(i) && !cm.isFixed(i)){
54470                 w = cm.getColumnWidth(i);
54471                 cols.push(i);
54472                 cols.push(w);
54473                 width += w;
54474             }
54475         }
54476         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54477         if(reserveScrollSpace){
54478             avail -= 17;
54479         }
54480         var frac = (avail - cm.getTotalWidth())/width;
54481         while (cols.length){
54482             w = cols.pop();
54483             i = cols.pop();
54484             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54485         }
54486         this.updateColumns();
54487         this.layout();
54488     },
54489
54490     onRowSelect : function(rowIndex){
54491         var row = this.getRowComposite(rowIndex);
54492         row.addClass("x-grid-row-selected");
54493     },
54494
54495     onRowDeselect : function(rowIndex){
54496         var row = this.getRowComposite(rowIndex);
54497         row.removeClass("x-grid-row-selected");
54498     },
54499
54500     onCellSelect : function(row, col){
54501         var cell = this.getCell(row, col);
54502         if(cell){
54503             Roo.fly(cell).addClass("x-grid-cell-selected");
54504         }
54505     },
54506
54507     onCellDeselect : function(row, col){
54508         var cell = this.getCell(row, col);
54509         if(cell){
54510             Roo.fly(cell).removeClass("x-grid-cell-selected");
54511         }
54512     },
54513
54514     updateHeaderSortState : function(){
54515         
54516         // sort state can be single { field: xxx, direction : yyy}
54517         // or   { xxx=>ASC , yyy : DESC ..... }
54518         
54519         var mstate = {};
54520         if (!this.ds.multiSort) { 
54521             var state = this.ds.getSortState();
54522             if(!state){
54523                 return;
54524             }
54525             mstate[state.field] = state.direction;
54526             // FIXME... - this is not used here.. but might be elsewhere..
54527             this.sortState = state;
54528             
54529         } else {
54530             mstate = this.ds.sortToggle;
54531         }
54532         //remove existing sort classes..
54533         
54534         var sc = this.sortClasses;
54535         var hds = this.el.select(this.headerSelector).removeClass(sc);
54536         
54537         for(var f in mstate) {
54538         
54539             var sortColumn = this.cm.findColumnIndex(f);
54540             
54541             if(sortColumn != -1){
54542                 var sortDir = mstate[f];        
54543                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54544             }
54545         }
54546         
54547          
54548         
54549     },
54550
54551
54552     handleHeaderClick : function(g, index,e){
54553         
54554         Roo.log("header click");
54555         
54556         if (Roo.isTouch) {
54557             // touch events on header are handled by context
54558             this.handleHdCtx(g,index,e);
54559             return;
54560         }
54561         
54562         
54563         if(this.headersDisabled){
54564             return;
54565         }
54566         var dm = g.dataSource, cm = g.colModel;
54567         if(!cm.isSortable(index)){
54568             return;
54569         }
54570         g.stopEditing();
54571         
54572         if (dm.multiSort) {
54573             // update the sortOrder
54574             var so = [];
54575             for(var i = 0; i < cm.config.length; i++ ) {
54576                 
54577                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54578                     continue; // dont' bother, it's not in sort list or being set.
54579                 }
54580                 
54581                 so.push(cm.config[i].dataIndex);
54582             };
54583             dm.sortOrder = so;
54584         }
54585         
54586         
54587         dm.sort(cm.getDataIndex(index));
54588     },
54589
54590
54591     destroy : function(){
54592         if(this.colMenu){
54593             this.colMenu.removeAll();
54594             Roo.menu.MenuMgr.unregister(this.colMenu);
54595             this.colMenu.getEl().remove();
54596             delete this.colMenu;
54597         }
54598         if(this.hmenu){
54599             this.hmenu.removeAll();
54600             Roo.menu.MenuMgr.unregister(this.hmenu);
54601             this.hmenu.getEl().remove();
54602             delete this.hmenu;
54603         }
54604         if(this.grid.enableColumnMove){
54605             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54606             if(dds){
54607                 for(var dd in dds){
54608                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54609                         var elid = dds[dd].dragElId;
54610                         dds[dd].unreg();
54611                         Roo.get(elid).remove();
54612                     } else if(dds[dd].config.isTarget){
54613                         dds[dd].proxyTop.remove();
54614                         dds[dd].proxyBottom.remove();
54615                         dds[dd].unreg();
54616                     }
54617                     if(Roo.dd.DDM.locationCache[dd]){
54618                         delete Roo.dd.DDM.locationCache[dd];
54619                     }
54620                 }
54621                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54622             }
54623         }
54624         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54625         this.bind(null, null);
54626         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54627     },
54628
54629     handleLockChange : function(){
54630         this.refresh(true);
54631     },
54632
54633     onDenyColumnLock : function(){
54634
54635     },
54636
54637     onDenyColumnHide : function(){
54638
54639     },
54640
54641     handleHdMenuClick : function(item){
54642         var index = this.hdCtxIndex;
54643         var cm = this.cm, ds = this.ds;
54644         switch(item.id){
54645             case "asc":
54646                 ds.sort(cm.getDataIndex(index), "ASC");
54647                 break;
54648             case "desc":
54649                 ds.sort(cm.getDataIndex(index), "DESC");
54650                 break;
54651             case "lock":
54652                 var lc = cm.getLockedCount();
54653                 if(cm.getColumnCount(true) <= lc+1){
54654                     this.onDenyColumnLock();
54655                     return;
54656                 }
54657                 if(lc != index){
54658                     cm.setLocked(index, true, true);
54659                     cm.moveColumn(index, lc);
54660                     this.grid.fireEvent("columnmove", index, lc);
54661                 }else{
54662                     cm.setLocked(index, true);
54663                 }
54664             break;
54665             case "unlock":
54666                 var lc = cm.getLockedCount();
54667                 if((lc-1) != index){
54668                     cm.setLocked(index, false, true);
54669                     cm.moveColumn(index, lc-1);
54670                     this.grid.fireEvent("columnmove", index, lc-1);
54671                 }else{
54672                     cm.setLocked(index, false);
54673                 }
54674             break;
54675             case 'wider': // used to expand cols on touch..
54676             case 'narrow':
54677                 var cw = cm.getColumnWidth(index);
54678                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54679                 cw = Math.max(0, cw);
54680                 cw = Math.min(cw,4000);
54681                 cm.setColumnWidth(index, cw);
54682                 break;
54683                 
54684             default:
54685                 index = cm.getIndexById(item.id.substr(4));
54686                 if(index != -1){
54687                     if(item.checked && cm.getColumnCount(true) <= 1){
54688                         this.onDenyColumnHide();
54689                         return false;
54690                     }
54691                     cm.setHidden(index, item.checked);
54692                 }
54693         }
54694         return true;
54695     },
54696
54697     beforeColMenuShow : function(){
54698         var cm = this.cm,  colCount = cm.getColumnCount();
54699         this.colMenu.removeAll();
54700         for(var i = 0; i < colCount; i++){
54701             this.colMenu.add(new Roo.menu.CheckItem({
54702                 id: "col-"+cm.getColumnId(i),
54703                 text: cm.getColumnHeader(i),
54704                 checked: !cm.isHidden(i),
54705                 hideOnClick:false
54706             }));
54707         }
54708     },
54709
54710     handleHdCtx : function(g, index, e){
54711         e.stopEvent();
54712         var hd = this.getHeaderCell(index);
54713         this.hdCtxIndex = index;
54714         var ms = this.hmenu.items, cm = this.cm;
54715         ms.get("asc").setDisabled(!cm.isSortable(index));
54716         ms.get("desc").setDisabled(!cm.isSortable(index));
54717         if(this.grid.enableColLock !== false){
54718             ms.get("lock").setDisabled(cm.isLocked(index));
54719             ms.get("unlock").setDisabled(!cm.isLocked(index));
54720         }
54721         this.hmenu.show(hd, "tl-bl");
54722     },
54723
54724     handleHdOver : function(e){
54725         var hd = this.findHeaderCell(e.getTarget());
54726         if(hd && !this.headersDisabled){
54727             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54728                this.fly(hd).addClass("x-grid-hd-over");
54729             }
54730         }
54731     },
54732
54733     handleHdOut : function(e){
54734         var hd = this.findHeaderCell(e.getTarget());
54735         if(hd){
54736             this.fly(hd).removeClass("x-grid-hd-over");
54737         }
54738     },
54739
54740     handleSplitDblClick : function(e, t){
54741         var i = this.getCellIndex(t);
54742         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54743             this.autoSizeColumn(i, true);
54744             this.layout();
54745         }
54746     },
54747
54748     render : function(){
54749
54750         var cm = this.cm;
54751         var colCount = cm.getColumnCount();
54752
54753         if(this.grid.monitorWindowResize === true){
54754             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54755         }
54756         var header = this.renderHeaders();
54757         var body = this.templates.body.apply({rows:""});
54758         var html = this.templates.master.apply({
54759             lockedBody: body,
54760             body: body,
54761             lockedHeader: header[0],
54762             header: header[1]
54763         });
54764
54765         //this.updateColumns();
54766
54767         this.grid.getGridEl().dom.innerHTML = html;
54768
54769         this.initElements();
54770         
54771         // a kludge to fix the random scolling effect in webkit
54772         this.el.on("scroll", function() {
54773             this.el.dom.scrollTop=0; // hopefully not recursive..
54774         },this);
54775
54776         this.scroller.on("scroll", this.handleScroll, this);
54777         this.lockedBody.on("mousewheel", this.handleWheel, this);
54778         this.mainBody.on("mousewheel", this.handleWheel, this);
54779
54780         this.mainHd.on("mouseover", this.handleHdOver, this);
54781         this.mainHd.on("mouseout", this.handleHdOut, this);
54782         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54783                 {delegate: "."+this.splitClass});
54784
54785         this.lockedHd.on("mouseover", this.handleHdOver, this);
54786         this.lockedHd.on("mouseout", this.handleHdOut, this);
54787         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54788                 {delegate: "."+this.splitClass});
54789
54790         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54791             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54792         }
54793
54794         this.updateSplitters();
54795
54796         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54797             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54798             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54799         }
54800
54801         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54802             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54803             this.hmenu.add(
54804                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54805                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54806             );
54807             if(this.grid.enableColLock !== false){
54808                 this.hmenu.add('-',
54809                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54810                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54811                 );
54812             }
54813             if (Roo.isTouch) {
54814                  this.hmenu.add('-',
54815                     {id:"wider", text: this.columnsWiderText},
54816                     {id:"narrow", text: this.columnsNarrowText }
54817                 );
54818                 
54819                  
54820             }
54821             
54822             if(this.grid.enableColumnHide !== false){
54823
54824                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54825                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54826                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54827
54828                 this.hmenu.add('-',
54829                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54830                 );
54831             }
54832             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54833
54834             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54835         }
54836
54837         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54838             this.dd = new Roo.grid.GridDragZone(this.grid, {
54839                 ddGroup : this.grid.ddGroup || 'GridDD'
54840             });
54841             
54842         }
54843
54844         /*
54845         for(var i = 0; i < colCount; i++){
54846             if(cm.isHidden(i)){
54847                 this.hideColumn(i);
54848             }
54849             if(cm.config[i].align){
54850                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54851                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54852             }
54853         }*/
54854         
54855         this.updateHeaderSortState();
54856
54857         this.beforeInitialResize();
54858         this.layout(true);
54859
54860         // two part rendering gives faster view to the user
54861         this.renderPhase2.defer(1, this);
54862     },
54863
54864     renderPhase2 : function(){
54865         // render the rows now
54866         this.refresh();
54867         if(this.grid.autoSizeColumns){
54868             this.autoSizeColumns();
54869         }
54870     },
54871
54872     beforeInitialResize : function(){
54873
54874     },
54875
54876     onColumnSplitterMoved : function(i, w){
54877         this.userResized = true;
54878         var cm = this.grid.colModel;
54879         cm.setColumnWidth(i, w, true);
54880         var cid = cm.getColumnId(i);
54881         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54882         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54883         this.updateSplitters();
54884         this.layout();
54885         this.grid.fireEvent("columnresize", i, w);
54886     },
54887
54888     syncRowHeights : function(startIndex, endIndex){
54889         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54890             startIndex = startIndex || 0;
54891             var mrows = this.getBodyTable().rows;
54892             var lrows = this.getLockedTable().rows;
54893             var len = mrows.length-1;
54894             endIndex = Math.min(endIndex || len, len);
54895             for(var i = startIndex; i <= endIndex; i++){
54896                 var m = mrows[i], l = lrows[i];
54897                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54898                 m.style.height = l.style.height = h + "px";
54899             }
54900         }
54901     },
54902
54903     layout : function(initialRender, is2ndPass){
54904         var g = this.grid;
54905         var auto = g.autoHeight;
54906         var scrollOffset = 16;
54907         var c = g.getGridEl(), cm = this.cm,
54908                 expandCol = g.autoExpandColumn,
54909                 gv = this;
54910         //c.beginMeasure();
54911
54912         if(!c.dom.offsetWidth){ // display:none?
54913             if(initialRender){
54914                 this.lockedWrap.show();
54915                 this.mainWrap.show();
54916             }
54917             return;
54918         }
54919
54920         var hasLock = this.cm.isLocked(0);
54921
54922         var tbh = this.headerPanel.getHeight();
54923         var bbh = this.footerPanel.getHeight();
54924
54925         if(auto){
54926             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54927             var newHeight = ch + c.getBorderWidth("tb");
54928             if(g.maxHeight){
54929                 newHeight = Math.min(g.maxHeight, newHeight);
54930             }
54931             c.setHeight(newHeight);
54932         }
54933
54934         if(g.autoWidth){
54935             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54936         }
54937
54938         var s = this.scroller;
54939
54940         var csize = c.getSize(true);
54941
54942         this.el.setSize(csize.width, csize.height);
54943
54944         this.headerPanel.setWidth(csize.width);
54945         this.footerPanel.setWidth(csize.width);
54946
54947         var hdHeight = this.mainHd.getHeight();
54948         var vw = csize.width;
54949         var vh = csize.height - (tbh + bbh);
54950
54951         s.setSize(vw, vh);
54952
54953         var bt = this.getBodyTable();
54954         var ltWidth = hasLock ?
54955                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54956
54957         var scrollHeight = bt.offsetHeight;
54958         var scrollWidth = ltWidth + bt.offsetWidth;
54959         var vscroll = false, hscroll = false;
54960
54961         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54962
54963         var lw = this.lockedWrap, mw = this.mainWrap;
54964         var lb = this.lockedBody, mb = this.mainBody;
54965
54966         setTimeout(function(){
54967             var t = s.dom.offsetTop;
54968             var w = s.dom.clientWidth,
54969                 h = s.dom.clientHeight;
54970
54971             lw.setTop(t);
54972             lw.setSize(ltWidth, h);
54973
54974             mw.setLeftTop(ltWidth, t);
54975             mw.setSize(w-ltWidth, h);
54976
54977             lb.setHeight(h-hdHeight);
54978             mb.setHeight(h-hdHeight);
54979
54980             if(is2ndPass !== true && !gv.userResized && expandCol){
54981                 // high speed resize without full column calculation
54982                 
54983                 var ci = cm.getIndexById(expandCol);
54984                 if (ci < 0) {
54985                     ci = cm.findColumnIndex(expandCol);
54986                 }
54987                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54988                 var expandId = cm.getColumnId(ci);
54989                 var  tw = cm.getTotalWidth(false);
54990                 var currentWidth = cm.getColumnWidth(ci);
54991                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54992                 if(currentWidth != cw){
54993                     cm.setColumnWidth(ci, cw, true);
54994                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54995                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54996                     gv.updateSplitters();
54997                     gv.layout(false, true);
54998                 }
54999             }
55000
55001             if(initialRender){
55002                 lw.show();
55003                 mw.show();
55004             }
55005             //c.endMeasure();
55006         }, 10);
55007     },
55008
55009     onWindowResize : function(){
55010         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55011             return;
55012         }
55013         this.layout();
55014     },
55015
55016     appendFooter : function(parentEl){
55017         return null;
55018     },
55019
55020     sortAscText : "Sort Ascending",
55021     sortDescText : "Sort Descending",
55022     lockText : "Lock Column",
55023     unlockText : "Unlock Column",
55024     columnsText : "Columns",
55025  
55026     columnsWiderText : "Wider",
55027     columnsNarrowText : "Thinner"
55028 });
55029
55030
55031 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55032     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55033     this.proxy.el.addClass('x-grid3-col-dd');
55034 };
55035
55036 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55037     handleMouseDown : function(e){
55038
55039     },
55040
55041     callHandleMouseDown : function(e){
55042         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55043     }
55044 });
55045 /*
55046  * Based on:
55047  * Ext JS Library 1.1.1
55048  * Copyright(c) 2006-2007, Ext JS, LLC.
55049  *
55050  * Originally Released Under LGPL - original licence link has changed is not relivant.
55051  *
55052  * Fork - LGPL
55053  * <script type="text/javascript">
55054  */
55055  
55056 // private
55057 // This is a support class used internally by the Grid components
55058 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55059     this.grid = grid;
55060     this.view = grid.getView();
55061     this.proxy = this.view.resizeProxy;
55062     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55063         "gridSplitters" + this.grid.getGridEl().id, {
55064         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55065     });
55066     this.setHandleElId(Roo.id(hd));
55067     this.setOuterHandleElId(Roo.id(hd2));
55068     this.scroll = false;
55069 };
55070 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55071     fly: Roo.Element.fly,
55072
55073     b4StartDrag : function(x, y){
55074         this.view.headersDisabled = true;
55075         this.proxy.setHeight(this.view.mainWrap.getHeight());
55076         var w = this.cm.getColumnWidth(this.cellIndex);
55077         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55078         this.resetConstraints();
55079         this.setXConstraint(minw, 1000);
55080         this.setYConstraint(0, 0);
55081         this.minX = x - minw;
55082         this.maxX = x + 1000;
55083         this.startPos = x;
55084         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55085     },
55086
55087
55088     handleMouseDown : function(e){
55089         ev = Roo.EventObject.setEvent(e);
55090         var t = this.fly(ev.getTarget());
55091         if(t.hasClass("x-grid-split")){
55092             this.cellIndex = this.view.getCellIndex(t.dom);
55093             this.split = t.dom;
55094             this.cm = this.grid.colModel;
55095             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55096                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55097             }
55098         }
55099     },
55100
55101     endDrag : function(e){
55102         this.view.headersDisabled = false;
55103         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55104         var diff = endX - this.startPos;
55105         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55106     },
55107
55108     autoOffset : function(){
55109         this.setDelta(0,0);
55110     }
55111 });/*
55112  * Based on:
55113  * Ext JS Library 1.1.1
55114  * Copyright(c) 2006-2007, Ext JS, LLC.
55115  *
55116  * Originally Released Under LGPL - original licence link has changed is not relivant.
55117  *
55118  * Fork - LGPL
55119  * <script type="text/javascript">
55120  */
55121  
55122 // private
55123 // This is a support class used internally by the Grid components
55124 Roo.grid.GridDragZone = function(grid, config){
55125     this.view = grid.getView();
55126     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55127     if(this.view.lockedBody){
55128         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55129         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55130     }
55131     this.scroll = false;
55132     this.grid = grid;
55133     this.ddel = document.createElement('div');
55134     this.ddel.className = 'x-grid-dd-wrap';
55135 };
55136
55137 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55138     ddGroup : "GridDD",
55139
55140     getDragData : function(e){
55141         var t = Roo.lib.Event.getTarget(e);
55142         var rowIndex = this.view.findRowIndex(t);
55143         var sm = this.grid.selModel;
55144             
55145         //Roo.log(rowIndex);
55146         
55147         if (sm.getSelectedCell) {
55148             // cell selection..
55149             if (!sm.getSelectedCell()) {
55150                 return false;
55151             }
55152             if (rowIndex != sm.getSelectedCell()[0]) {
55153                 return false;
55154             }
55155         
55156         }
55157         
55158         if(rowIndex !== false){
55159             
55160             // if editorgrid.. 
55161             
55162             
55163             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55164                
55165             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55166               //  
55167             //}
55168             if (e.hasModifier()){
55169                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55170             }
55171             
55172             Roo.log("getDragData");
55173             
55174             return {
55175                 grid: this.grid,
55176                 ddel: this.ddel,
55177                 rowIndex: rowIndex,
55178                 selections:sm.getSelections ? sm.getSelections() : (
55179                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55180                 )
55181             };
55182         }
55183         return false;
55184     },
55185
55186     onInitDrag : function(e){
55187         var data = this.dragData;
55188         this.ddel.innerHTML = this.grid.getDragDropText();
55189         this.proxy.update(this.ddel);
55190         // fire start drag?
55191     },
55192
55193     afterRepair : function(){
55194         this.dragging = false;
55195     },
55196
55197     getRepairXY : function(e, data){
55198         return false;
55199     },
55200
55201     onEndDrag : function(data, e){
55202         // fire end drag?
55203     },
55204
55205     onValidDrop : function(dd, e, id){
55206         // fire drag drop?
55207         this.hideProxy();
55208     },
55209
55210     beforeInvalidDrop : function(e, id){
55211
55212     }
55213 });/*
55214  * Based on:
55215  * Ext JS Library 1.1.1
55216  * Copyright(c) 2006-2007, Ext JS, LLC.
55217  *
55218  * Originally Released Under LGPL - original licence link has changed is not relivant.
55219  *
55220  * Fork - LGPL
55221  * <script type="text/javascript">
55222  */
55223  
55224
55225 /**
55226  * @class Roo.grid.ColumnModel
55227  * @extends Roo.util.Observable
55228  * This is the default implementation of a ColumnModel used by the Grid. It defines
55229  * the columns in the grid.
55230  * <br>Usage:<br>
55231  <pre><code>
55232  var colModel = new Roo.grid.ColumnModel([
55233         {header: "Ticker", width: 60, sortable: true, locked: true},
55234         {header: "Company Name", width: 150, sortable: true},
55235         {header: "Market Cap.", width: 100, sortable: true},
55236         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55237         {header: "Employees", width: 100, sortable: true, resizable: false}
55238  ]);
55239  </code></pre>
55240  * <p>
55241  
55242  * The config options listed for this class are options which may appear in each
55243  * individual column definition.
55244  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55245  * @constructor
55246  * @param {Object} config An Array of column config objects. See this class's
55247  * config objects for details.
55248 */
55249 Roo.grid.ColumnModel = function(config){
55250         /**
55251      * The config passed into the constructor
55252      */
55253     this.config = config;
55254     this.lookup = {};
55255
55256     // if no id, create one
55257     // if the column does not have a dataIndex mapping,
55258     // map it to the order it is in the config
55259     for(var i = 0, len = config.length; i < len; i++){
55260         var c = config[i];
55261         if(typeof c.dataIndex == "undefined"){
55262             c.dataIndex = i;
55263         }
55264         if(typeof c.renderer == "string"){
55265             c.renderer = Roo.util.Format[c.renderer];
55266         }
55267         if(typeof c.id == "undefined"){
55268             c.id = Roo.id();
55269         }
55270         if(c.editor && c.editor.xtype){
55271             c.editor  = Roo.factory(c.editor, Roo.grid);
55272         }
55273         if(c.editor && c.editor.isFormField){
55274             c.editor = new Roo.grid.GridEditor(c.editor);
55275         }
55276         this.lookup[c.id] = c;
55277     }
55278
55279     /**
55280      * The width of columns which have no width specified (defaults to 100)
55281      * @type Number
55282      */
55283     this.defaultWidth = 100;
55284
55285     /**
55286      * Default sortable of columns which have no sortable specified (defaults to false)
55287      * @type Boolean
55288      */
55289     this.defaultSortable = false;
55290
55291     this.addEvents({
55292         /**
55293              * @event widthchange
55294              * Fires when the width of a column changes.
55295              * @param {ColumnModel} this
55296              * @param {Number} columnIndex The column index
55297              * @param {Number} newWidth The new width
55298              */
55299             "widthchange": true,
55300         /**
55301              * @event headerchange
55302              * Fires when the text of a header changes.
55303              * @param {ColumnModel} this
55304              * @param {Number} columnIndex The column index
55305              * @param {Number} newText The new header text
55306              */
55307             "headerchange": true,
55308         /**
55309              * @event hiddenchange
55310              * Fires when a column is hidden or "unhidden".
55311              * @param {ColumnModel} this
55312              * @param {Number} columnIndex The column index
55313              * @param {Boolean} hidden true if hidden, false otherwise
55314              */
55315             "hiddenchange": true,
55316             /**
55317          * @event columnmoved
55318          * Fires when a column is moved.
55319          * @param {ColumnModel} this
55320          * @param {Number} oldIndex
55321          * @param {Number} newIndex
55322          */
55323         "columnmoved" : true,
55324         /**
55325          * @event columlockchange
55326          * Fires when a column's locked state is changed
55327          * @param {ColumnModel} this
55328          * @param {Number} colIndex
55329          * @param {Boolean} locked true if locked
55330          */
55331         "columnlockchange" : true
55332     });
55333     Roo.grid.ColumnModel.superclass.constructor.call(this);
55334 };
55335 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55336     /**
55337      * @cfg {String} header The header text to display in the Grid view.
55338      */
55339     /**
55340      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55341      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55342      * specified, the column's index is used as an index into the Record's data Array.
55343      */
55344     /**
55345      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55346      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55347      */
55348     /**
55349      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55350      * Defaults to the value of the {@link #defaultSortable} property.
55351      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55352      */
55353     /**
55354      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55355      */
55356     /**
55357      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55358      */
55359     /**
55360      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55361      */
55362     /**
55363      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55364      */
55365     /**
55366      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55367      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55368      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55369      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55370      */
55371        /**
55372      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55373      */
55374     /**
55375      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55376      */
55377     /**
55378      * @cfg {String} cursor (Optional)
55379      */
55380     /**
55381      * @cfg {String} tooltip (Optional)
55382      */
55383     /**
55384      * Returns the id of the column at the specified index.
55385      * @param {Number} index The column index
55386      * @return {String} the id
55387      */
55388     getColumnId : function(index){
55389         return this.config[index].id;
55390     },
55391
55392     /**
55393      * Returns the column for a specified id.
55394      * @param {String} id The column id
55395      * @return {Object} the column
55396      */
55397     getColumnById : function(id){
55398         return this.lookup[id];
55399     },
55400
55401     
55402     /**
55403      * Returns the column for a specified dataIndex.
55404      * @param {String} dataIndex The column dataIndex
55405      * @return {Object|Boolean} the column or false if not found
55406      */
55407     getColumnByDataIndex: function(dataIndex){
55408         var index = this.findColumnIndex(dataIndex);
55409         return index > -1 ? this.config[index] : false;
55410     },
55411     
55412     /**
55413      * Returns the index for a specified column id.
55414      * @param {String} id The column id
55415      * @return {Number} the index, or -1 if not found
55416      */
55417     getIndexById : function(id){
55418         for(var i = 0, len = this.config.length; i < len; i++){
55419             if(this.config[i].id == id){
55420                 return i;
55421             }
55422         }
55423         return -1;
55424     },
55425     
55426     /**
55427      * Returns the index for a specified column dataIndex.
55428      * @param {String} dataIndex The column dataIndex
55429      * @return {Number} the index, or -1 if not found
55430      */
55431     
55432     findColumnIndex : function(dataIndex){
55433         for(var i = 0, len = this.config.length; i < len; i++){
55434             if(this.config[i].dataIndex == dataIndex){
55435                 return i;
55436             }
55437         }
55438         return -1;
55439     },
55440     
55441     
55442     moveColumn : function(oldIndex, newIndex){
55443         var c = this.config[oldIndex];
55444         this.config.splice(oldIndex, 1);
55445         this.config.splice(newIndex, 0, c);
55446         this.dataMap = null;
55447         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55448     },
55449
55450     isLocked : function(colIndex){
55451         return this.config[colIndex].locked === true;
55452     },
55453
55454     setLocked : function(colIndex, value, suppressEvent){
55455         if(this.isLocked(colIndex) == value){
55456             return;
55457         }
55458         this.config[colIndex].locked = value;
55459         if(!suppressEvent){
55460             this.fireEvent("columnlockchange", this, colIndex, value);
55461         }
55462     },
55463
55464     getTotalLockedWidth : function(){
55465         var totalWidth = 0;
55466         for(var i = 0; i < this.config.length; i++){
55467             if(this.isLocked(i) && !this.isHidden(i)){
55468                 this.totalWidth += this.getColumnWidth(i);
55469             }
55470         }
55471         return totalWidth;
55472     },
55473
55474     getLockedCount : function(){
55475         for(var i = 0, len = this.config.length; i < len; i++){
55476             if(!this.isLocked(i)){
55477                 return i;
55478             }
55479         }
55480     },
55481
55482     /**
55483      * Returns the number of columns.
55484      * @return {Number}
55485      */
55486     getColumnCount : function(visibleOnly){
55487         if(visibleOnly === true){
55488             var c = 0;
55489             for(var i = 0, len = this.config.length; i < len; i++){
55490                 if(!this.isHidden(i)){
55491                     c++;
55492                 }
55493             }
55494             return c;
55495         }
55496         return this.config.length;
55497     },
55498
55499     /**
55500      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55501      * @param {Function} fn
55502      * @param {Object} scope (optional)
55503      * @return {Array} result
55504      */
55505     getColumnsBy : function(fn, scope){
55506         var r = [];
55507         for(var i = 0, len = this.config.length; i < len; i++){
55508             var c = this.config[i];
55509             if(fn.call(scope||this, c, i) === true){
55510                 r[r.length] = c;
55511             }
55512         }
55513         return r;
55514     },
55515
55516     /**
55517      * Returns true if the specified column is sortable.
55518      * @param {Number} col The column index
55519      * @return {Boolean}
55520      */
55521     isSortable : function(col){
55522         if(typeof this.config[col].sortable == "undefined"){
55523             return this.defaultSortable;
55524         }
55525         return this.config[col].sortable;
55526     },
55527
55528     /**
55529      * Returns the rendering (formatting) function defined for the column.
55530      * @param {Number} col The column index.
55531      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55532      */
55533     getRenderer : function(col){
55534         if(!this.config[col].renderer){
55535             return Roo.grid.ColumnModel.defaultRenderer;
55536         }
55537         return this.config[col].renderer;
55538     },
55539
55540     /**
55541      * Sets the rendering (formatting) function for a column.
55542      * @param {Number} col The column index
55543      * @param {Function} fn The function to use to process the cell's raw data
55544      * to return HTML markup for the grid view. The render function is called with
55545      * the following parameters:<ul>
55546      * <li>Data value.</li>
55547      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55548      * <li>css A CSS style string to apply to the table cell.</li>
55549      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55550      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55551      * <li>Row index</li>
55552      * <li>Column index</li>
55553      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55554      */
55555     setRenderer : function(col, fn){
55556         this.config[col].renderer = fn;
55557     },
55558
55559     /**
55560      * Returns the width for the specified column.
55561      * @param {Number} col The column index
55562      * @return {Number}
55563      */
55564     getColumnWidth : function(col){
55565         return this.config[col].width * 1 || this.defaultWidth;
55566     },
55567
55568     /**
55569      * Sets the width for a column.
55570      * @param {Number} col The column index
55571      * @param {Number} width The new width
55572      */
55573     setColumnWidth : function(col, width, suppressEvent){
55574         this.config[col].width = width;
55575         this.totalWidth = null;
55576         if(!suppressEvent){
55577              this.fireEvent("widthchange", this, col, width);
55578         }
55579     },
55580
55581     /**
55582      * Returns the total width of all columns.
55583      * @param {Boolean} includeHidden True to include hidden column widths
55584      * @return {Number}
55585      */
55586     getTotalWidth : function(includeHidden){
55587         if(!this.totalWidth){
55588             this.totalWidth = 0;
55589             for(var i = 0, len = this.config.length; i < len; i++){
55590                 if(includeHidden || !this.isHidden(i)){
55591                     this.totalWidth += this.getColumnWidth(i);
55592                 }
55593             }
55594         }
55595         return this.totalWidth;
55596     },
55597
55598     /**
55599      * Returns the header for the specified column.
55600      * @param {Number} col The column index
55601      * @return {String}
55602      */
55603     getColumnHeader : function(col){
55604         return this.config[col].header;
55605     },
55606
55607     /**
55608      * Sets the header for a column.
55609      * @param {Number} col The column index
55610      * @param {String} header The new header
55611      */
55612     setColumnHeader : function(col, header){
55613         this.config[col].header = header;
55614         this.fireEvent("headerchange", this, col, header);
55615     },
55616
55617     /**
55618      * Returns the tooltip for the specified column.
55619      * @param {Number} col The column index
55620      * @return {String}
55621      */
55622     getColumnTooltip : function(col){
55623             return this.config[col].tooltip;
55624     },
55625     /**
55626      * Sets the tooltip for a column.
55627      * @param {Number} col The column index
55628      * @param {String} tooltip The new tooltip
55629      */
55630     setColumnTooltip : function(col, tooltip){
55631             this.config[col].tooltip = tooltip;
55632     },
55633
55634     /**
55635      * Returns the dataIndex for the specified column.
55636      * @param {Number} col The column index
55637      * @return {Number}
55638      */
55639     getDataIndex : function(col){
55640         return this.config[col].dataIndex;
55641     },
55642
55643     /**
55644      * Sets the dataIndex for a column.
55645      * @param {Number} col The column index
55646      * @param {Number} dataIndex The new dataIndex
55647      */
55648     setDataIndex : function(col, dataIndex){
55649         this.config[col].dataIndex = dataIndex;
55650     },
55651
55652     
55653     
55654     /**
55655      * Returns true if the cell is editable.
55656      * @param {Number} colIndex The column index
55657      * @param {Number} rowIndex The row index
55658      * @return {Boolean}
55659      */
55660     isCellEditable : function(colIndex, rowIndex){
55661         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55662     },
55663
55664     /**
55665      * Returns the editor defined for the cell/column.
55666      * return false or null to disable editing.
55667      * @param {Number} colIndex The column index
55668      * @param {Number} rowIndex The row index
55669      * @return {Object}
55670      */
55671     getCellEditor : function(colIndex, rowIndex){
55672         return this.config[colIndex].editor;
55673     },
55674
55675     /**
55676      * Sets if a column is editable.
55677      * @param {Number} col The column index
55678      * @param {Boolean} editable True if the column is editable
55679      */
55680     setEditable : function(col, editable){
55681         this.config[col].editable = editable;
55682     },
55683
55684
55685     /**
55686      * Returns true if the column is hidden.
55687      * @param {Number} colIndex The column index
55688      * @return {Boolean}
55689      */
55690     isHidden : function(colIndex){
55691         return this.config[colIndex].hidden;
55692     },
55693
55694
55695     /**
55696      * Returns true if the column width cannot be changed
55697      */
55698     isFixed : function(colIndex){
55699         return this.config[colIndex].fixed;
55700     },
55701
55702     /**
55703      * Returns true if the column can be resized
55704      * @return {Boolean}
55705      */
55706     isResizable : function(colIndex){
55707         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55708     },
55709     /**
55710      * Sets if a column is hidden.
55711      * @param {Number} colIndex The column index
55712      * @param {Boolean} hidden True if the column is hidden
55713      */
55714     setHidden : function(colIndex, hidden){
55715         this.config[colIndex].hidden = hidden;
55716         this.totalWidth = null;
55717         this.fireEvent("hiddenchange", this, colIndex, hidden);
55718     },
55719
55720     /**
55721      * Sets the editor for a column.
55722      * @param {Number} col The column index
55723      * @param {Object} editor The editor object
55724      */
55725     setEditor : function(col, editor){
55726         this.config[col].editor = editor;
55727     }
55728 });
55729
55730 Roo.grid.ColumnModel.defaultRenderer = function(value){
55731         if(typeof value == "string" && value.length < 1){
55732             return "&#160;";
55733         }
55734         return value;
55735 };
55736
55737 // Alias for backwards compatibility
55738 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55739 /*
55740  * Based on:
55741  * Ext JS Library 1.1.1
55742  * Copyright(c) 2006-2007, Ext JS, LLC.
55743  *
55744  * Originally Released Under LGPL - original licence link has changed is not relivant.
55745  *
55746  * Fork - LGPL
55747  * <script type="text/javascript">
55748  */
55749
55750 /**
55751  * @class Roo.grid.AbstractSelectionModel
55752  * @extends Roo.util.Observable
55753  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55754  * implemented by descendant classes.  This class should not be directly instantiated.
55755  * @constructor
55756  */
55757 Roo.grid.AbstractSelectionModel = function(){
55758     this.locked = false;
55759     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55760 };
55761
55762 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55763     /** @ignore Called by the grid automatically. Do not call directly. */
55764     init : function(grid){
55765         this.grid = grid;
55766         this.initEvents();
55767     },
55768
55769     /**
55770      * Locks the selections.
55771      */
55772     lock : function(){
55773         this.locked = true;
55774     },
55775
55776     /**
55777      * Unlocks the selections.
55778      */
55779     unlock : function(){
55780         this.locked = false;
55781     },
55782
55783     /**
55784      * Returns true if the selections are locked.
55785      * @return {Boolean}
55786      */
55787     isLocked : function(){
55788         return this.locked;
55789     }
55790 });/*
55791  * Based on:
55792  * Ext JS Library 1.1.1
55793  * Copyright(c) 2006-2007, Ext JS, LLC.
55794  *
55795  * Originally Released Under LGPL - original licence link has changed is not relivant.
55796  *
55797  * Fork - LGPL
55798  * <script type="text/javascript">
55799  */
55800 /**
55801  * @extends Roo.grid.AbstractSelectionModel
55802  * @class Roo.grid.RowSelectionModel
55803  * The default SelectionModel used by {@link Roo.grid.Grid}.
55804  * It supports multiple selections and keyboard selection/navigation. 
55805  * @constructor
55806  * @param {Object} config
55807  */
55808 Roo.grid.RowSelectionModel = function(config){
55809     Roo.apply(this, config);
55810     this.selections = new Roo.util.MixedCollection(false, function(o){
55811         return o.id;
55812     });
55813
55814     this.last = false;
55815     this.lastActive = false;
55816
55817     this.addEvents({
55818         /**
55819              * @event selectionchange
55820              * Fires when the selection changes
55821              * @param {SelectionModel} this
55822              */
55823             "selectionchange" : true,
55824         /**
55825              * @event afterselectionchange
55826              * Fires after the selection changes (eg. by key press or clicking)
55827              * @param {SelectionModel} this
55828              */
55829             "afterselectionchange" : true,
55830         /**
55831              * @event beforerowselect
55832              * Fires when a row is selected being selected, return false to cancel.
55833              * @param {SelectionModel} this
55834              * @param {Number} rowIndex The selected index
55835              * @param {Boolean} keepExisting False if other selections will be cleared
55836              */
55837             "beforerowselect" : true,
55838         /**
55839              * @event rowselect
55840              * Fires when a row is selected.
55841              * @param {SelectionModel} this
55842              * @param {Number} rowIndex The selected index
55843              * @param {Roo.data.Record} r The record
55844              */
55845             "rowselect" : true,
55846         /**
55847              * @event rowdeselect
55848              * Fires when a row is deselected.
55849              * @param {SelectionModel} this
55850              * @param {Number} rowIndex The selected index
55851              */
55852         "rowdeselect" : true
55853     });
55854     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55855     this.locked = false;
55856 };
55857
55858 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55859     /**
55860      * @cfg {Boolean} singleSelect
55861      * True to allow selection of only one row at a time (defaults to false)
55862      */
55863     singleSelect : false,
55864
55865     // private
55866     initEvents : function(){
55867
55868         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55869             this.grid.on("mousedown", this.handleMouseDown, this);
55870         }else{ // allow click to work like normal
55871             this.grid.on("rowclick", this.handleDragableRowClick, this);
55872         }
55873
55874         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55875             "up" : function(e){
55876                 if(!e.shiftKey){
55877                     this.selectPrevious(e.shiftKey);
55878                 }else if(this.last !== false && this.lastActive !== false){
55879                     var last = this.last;
55880                     this.selectRange(this.last,  this.lastActive-1);
55881                     this.grid.getView().focusRow(this.lastActive);
55882                     if(last !== false){
55883                         this.last = last;
55884                     }
55885                 }else{
55886                     this.selectFirstRow();
55887                 }
55888                 this.fireEvent("afterselectionchange", this);
55889             },
55890             "down" : function(e){
55891                 if(!e.shiftKey){
55892                     this.selectNext(e.shiftKey);
55893                 }else if(this.last !== false && this.lastActive !== false){
55894                     var last = this.last;
55895                     this.selectRange(this.last,  this.lastActive+1);
55896                     this.grid.getView().focusRow(this.lastActive);
55897                     if(last !== false){
55898                         this.last = last;
55899                     }
55900                 }else{
55901                     this.selectFirstRow();
55902                 }
55903                 this.fireEvent("afterselectionchange", this);
55904             },
55905             scope: this
55906         });
55907
55908         var view = this.grid.view;
55909         view.on("refresh", this.onRefresh, this);
55910         view.on("rowupdated", this.onRowUpdated, this);
55911         view.on("rowremoved", this.onRemove, this);
55912     },
55913
55914     // private
55915     onRefresh : function(){
55916         var ds = this.grid.dataSource, i, v = this.grid.view;
55917         var s = this.selections;
55918         s.each(function(r){
55919             if((i = ds.indexOfId(r.id)) != -1){
55920                 v.onRowSelect(i);
55921             }else{
55922                 s.remove(r);
55923             }
55924         });
55925     },
55926
55927     // private
55928     onRemove : function(v, index, r){
55929         this.selections.remove(r);
55930     },
55931
55932     // private
55933     onRowUpdated : function(v, index, r){
55934         if(this.isSelected(r)){
55935             v.onRowSelect(index);
55936         }
55937     },
55938
55939     /**
55940      * Select records.
55941      * @param {Array} records The records to select
55942      * @param {Boolean} keepExisting (optional) True to keep existing selections
55943      */
55944     selectRecords : function(records, keepExisting){
55945         if(!keepExisting){
55946             this.clearSelections();
55947         }
55948         var ds = this.grid.dataSource;
55949         for(var i = 0, len = records.length; i < len; i++){
55950             this.selectRow(ds.indexOf(records[i]), true);
55951         }
55952     },
55953
55954     /**
55955      * Gets the number of selected rows.
55956      * @return {Number}
55957      */
55958     getCount : function(){
55959         return this.selections.length;
55960     },
55961
55962     /**
55963      * Selects the first row in the grid.
55964      */
55965     selectFirstRow : function(){
55966         this.selectRow(0);
55967     },
55968
55969     /**
55970      * Select the last row.
55971      * @param {Boolean} keepExisting (optional) True to keep existing selections
55972      */
55973     selectLastRow : function(keepExisting){
55974         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55975     },
55976
55977     /**
55978      * Selects the row immediately following the last selected row.
55979      * @param {Boolean} keepExisting (optional) True to keep existing selections
55980      */
55981     selectNext : function(keepExisting){
55982         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55983             this.selectRow(this.last+1, keepExisting);
55984             this.grid.getView().focusRow(this.last);
55985         }
55986     },
55987
55988     /**
55989      * Selects the row that precedes the last selected row.
55990      * @param {Boolean} keepExisting (optional) True to keep existing selections
55991      */
55992     selectPrevious : function(keepExisting){
55993         if(this.last){
55994             this.selectRow(this.last-1, keepExisting);
55995             this.grid.getView().focusRow(this.last);
55996         }
55997     },
55998
55999     /**
56000      * Returns the selected records
56001      * @return {Array} Array of selected records
56002      */
56003     getSelections : function(){
56004         return [].concat(this.selections.items);
56005     },
56006
56007     /**
56008      * Returns the first selected record.
56009      * @return {Record}
56010      */
56011     getSelected : function(){
56012         return this.selections.itemAt(0);
56013     },
56014
56015
56016     /**
56017      * Clears all selections.
56018      */
56019     clearSelections : function(fast){
56020         if(this.locked) return;
56021         if(fast !== true){
56022             var ds = this.grid.dataSource;
56023             var s = this.selections;
56024             s.each(function(r){
56025                 this.deselectRow(ds.indexOfId(r.id));
56026             }, this);
56027             s.clear();
56028         }else{
56029             this.selections.clear();
56030         }
56031         this.last = false;
56032     },
56033
56034
56035     /**
56036      * Selects all rows.
56037      */
56038     selectAll : function(){
56039         if(this.locked) return;
56040         this.selections.clear();
56041         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56042             this.selectRow(i, true);
56043         }
56044     },
56045
56046     /**
56047      * Returns True if there is a selection.
56048      * @return {Boolean}
56049      */
56050     hasSelection : function(){
56051         return this.selections.length > 0;
56052     },
56053
56054     /**
56055      * Returns True if the specified row is selected.
56056      * @param {Number/Record} record The record or index of the record to check
56057      * @return {Boolean}
56058      */
56059     isSelected : function(index){
56060         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56061         return (r && this.selections.key(r.id) ? true : false);
56062     },
56063
56064     /**
56065      * Returns True if the specified record id is selected.
56066      * @param {String} id The id of record to check
56067      * @return {Boolean}
56068      */
56069     isIdSelected : function(id){
56070         return (this.selections.key(id) ? true : false);
56071     },
56072
56073     // private
56074     handleMouseDown : function(e, t){
56075         var view = this.grid.getView(), rowIndex;
56076         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56077             return;
56078         };
56079         if(e.shiftKey && this.last !== false){
56080             var last = this.last;
56081             this.selectRange(last, rowIndex, e.ctrlKey);
56082             this.last = last; // reset the last
56083             view.focusRow(rowIndex);
56084         }else{
56085             var isSelected = this.isSelected(rowIndex);
56086             if(e.button !== 0 && isSelected){
56087                 view.focusRow(rowIndex);
56088             }else if(e.ctrlKey && isSelected){
56089                 this.deselectRow(rowIndex);
56090             }else if(!isSelected){
56091                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56092                 view.focusRow(rowIndex);
56093             }
56094         }
56095         this.fireEvent("afterselectionchange", this);
56096     },
56097     // private
56098     handleDragableRowClick :  function(grid, rowIndex, e) 
56099     {
56100         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56101             this.selectRow(rowIndex, false);
56102             grid.view.focusRow(rowIndex);
56103              this.fireEvent("afterselectionchange", this);
56104         }
56105     },
56106     
56107     /**
56108      * Selects multiple rows.
56109      * @param {Array} rows Array of the indexes of the row to select
56110      * @param {Boolean} keepExisting (optional) True to keep existing selections
56111      */
56112     selectRows : function(rows, keepExisting){
56113         if(!keepExisting){
56114             this.clearSelections();
56115         }
56116         for(var i = 0, len = rows.length; i < len; i++){
56117             this.selectRow(rows[i], true);
56118         }
56119     },
56120
56121     /**
56122      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56123      * @param {Number} startRow The index of the first row in the range
56124      * @param {Number} endRow The index of the last row in the range
56125      * @param {Boolean} keepExisting (optional) True to retain existing selections
56126      */
56127     selectRange : function(startRow, endRow, keepExisting){
56128         if(this.locked) return;
56129         if(!keepExisting){
56130             this.clearSelections();
56131         }
56132         if(startRow <= endRow){
56133             for(var i = startRow; i <= endRow; i++){
56134                 this.selectRow(i, true);
56135             }
56136         }else{
56137             for(var i = startRow; i >= endRow; i--){
56138                 this.selectRow(i, true);
56139             }
56140         }
56141     },
56142
56143     /**
56144      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56145      * @param {Number} startRow The index of the first row in the range
56146      * @param {Number} endRow The index of the last row in the range
56147      */
56148     deselectRange : function(startRow, endRow, preventViewNotify){
56149         if(this.locked) return;
56150         for(var i = startRow; i <= endRow; i++){
56151             this.deselectRow(i, preventViewNotify);
56152         }
56153     },
56154
56155     /**
56156      * Selects a row.
56157      * @param {Number} row The index of the row to select
56158      * @param {Boolean} keepExisting (optional) True to keep existing selections
56159      */
56160     selectRow : function(index, keepExisting, preventViewNotify){
56161         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56162         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56163             if(!keepExisting || this.singleSelect){
56164                 this.clearSelections();
56165             }
56166             var r = this.grid.dataSource.getAt(index);
56167             this.selections.add(r);
56168             this.last = this.lastActive = index;
56169             if(!preventViewNotify){
56170                 this.grid.getView().onRowSelect(index);
56171             }
56172             this.fireEvent("rowselect", this, index, r);
56173             this.fireEvent("selectionchange", this);
56174         }
56175     },
56176
56177     /**
56178      * Deselects a row.
56179      * @param {Number} row The index of the row to deselect
56180      */
56181     deselectRow : function(index, preventViewNotify){
56182         if(this.locked) return;
56183         if(this.last == index){
56184             this.last = false;
56185         }
56186         if(this.lastActive == index){
56187             this.lastActive = false;
56188         }
56189         var r = this.grid.dataSource.getAt(index);
56190         this.selections.remove(r);
56191         if(!preventViewNotify){
56192             this.grid.getView().onRowDeselect(index);
56193         }
56194         this.fireEvent("rowdeselect", this, index);
56195         this.fireEvent("selectionchange", this);
56196     },
56197
56198     // private
56199     restoreLast : function(){
56200         if(this._last){
56201             this.last = this._last;
56202         }
56203     },
56204
56205     // private
56206     acceptsNav : function(row, col, cm){
56207         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56208     },
56209
56210     // private
56211     onEditorKey : function(field, e){
56212         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56213         if(k == e.TAB){
56214             e.stopEvent();
56215             ed.completeEdit();
56216             if(e.shiftKey){
56217                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56218             }else{
56219                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56220             }
56221         }else if(k == e.ENTER && !e.ctrlKey){
56222             e.stopEvent();
56223             ed.completeEdit();
56224             if(e.shiftKey){
56225                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56226             }else{
56227                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56228             }
56229         }else if(k == e.ESC){
56230             ed.cancelEdit();
56231         }
56232         if(newCell){
56233             g.startEditing(newCell[0], newCell[1]);
56234         }
56235     }
56236 });/*
56237  * Based on:
56238  * Ext JS Library 1.1.1
56239  * Copyright(c) 2006-2007, Ext JS, LLC.
56240  *
56241  * Originally Released Under LGPL - original licence link has changed is not relivant.
56242  *
56243  * Fork - LGPL
56244  * <script type="text/javascript">
56245  */
56246 /**
56247  * @class Roo.grid.CellSelectionModel
56248  * @extends Roo.grid.AbstractSelectionModel
56249  * This class provides the basic implementation for cell selection in a grid.
56250  * @constructor
56251  * @param {Object} config The object containing the configuration of this model.
56252  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56253  */
56254 Roo.grid.CellSelectionModel = function(config){
56255     Roo.apply(this, config);
56256
56257     this.selection = null;
56258
56259     this.addEvents({
56260         /**
56261              * @event beforerowselect
56262              * Fires before a cell is selected.
56263              * @param {SelectionModel} this
56264              * @param {Number} rowIndex The selected row index
56265              * @param {Number} colIndex The selected cell index
56266              */
56267             "beforecellselect" : true,
56268         /**
56269              * @event cellselect
56270              * Fires when a cell is selected.
56271              * @param {SelectionModel} this
56272              * @param {Number} rowIndex The selected row index
56273              * @param {Number} colIndex The selected cell index
56274              */
56275             "cellselect" : true,
56276         /**
56277              * @event selectionchange
56278              * Fires when the active selection changes.
56279              * @param {SelectionModel} this
56280              * @param {Object} selection null for no selection or an object (o) with two properties
56281                 <ul>
56282                 <li>o.record: the record object for the row the selection is in</li>
56283                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56284                 </ul>
56285              */
56286             "selectionchange" : true,
56287         /**
56288              * @event tabend
56289              * Fires when the tab (or enter) was pressed on the last editable cell
56290              * You can use this to trigger add new row.
56291              * @param {SelectionModel} this
56292              */
56293             "tabend" : true,
56294          /**
56295              * @event beforeeditnext
56296              * Fires before the next editable sell is made active
56297              * You can use this to skip to another cell or fire the tabend
56298              *    if you set cell to false
56299              * @param {Object} eventdata object : { cell : [ row, col ] } 
56300              */
56301             "beforeeditnext" : true
56302     });
56303     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56304 };
56305
56306 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56307     
56308     enter_is_tab: false,
56309
56310     /** @ignore */
56311     initEvents : function(){
56312         this.grid.on("mousedown", this.handleMouseDown, this);
56313         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56314         var view = this.grid.view;
56315         view.on("refresh", this.onViewChange, this);
56316         view.on("rowupdated", this.onRowUpdated, this);
56317         view.on("beforerowremoved", this.clearSelections, this);
56318         view.on("beforerowsinserted", this.clearSelections, this);
56319         if(this.grid.isEditor){
56320             this.grid.on("beforeedit", this.beforeEdit,  this);
56321         }
56322     },
56323
56324         //private
56325     beforeEdit : function(e){
56326         this.select(e.row, e.column, false, true, e.record);
56327     },
56328
56329         //private
56330     onRowUpdated : function(v, index, r){
56331         if(this.selection && this.selection.record == r){
56332             v.onCellSelect(index, this.selection.cell[1]);
56333         }
56334     },
56335
56336         //private
56337     onViewChange : function(){
56338         this.clearSelections(true);
56339     },
56340
56341         /**
56342          * Returns the currently selected cell,.
56343          * @return {Array} The selected cell (row, column) or null if none selected.
56344          */
56345     getSelectedCell : function(){
56346         return this.selection ? this.selection.cell : null;
56347     },
56348
56349     /**
56350      * Clears all selections.
56351      * @param {Boolean} true to prevent the gridview from being notified about the change.
56352      */
56353     clearSelections : function(preventNotify){
56354         var s = this.selection;
56355         if(s){
56356             if(preventNotify !== true){
56357                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56358             }
56359             this.selection = null;
56360             this.fireEvent("selectionchange", this, null);
56361         }
56362     },
56363
56364     /**
56365      * Returns true if there is a selection.
56366      * @return {Boolean}
56367      */
56368     hasSelection : function(){
56369         return this.selection ? true : false;
56370     },
56371
56372     /** @ignore */
56373     handleMouseDown : function(e, t){
56374         var v = this.grid.getView();
56375         if(this.isLocked()){
56376             return;
56377         };
56378         var row = v.findRowIndex(t);
56379         var cell = v.findCellIndex(t);
56380         if(row !== false && cell !== false){
56381             this.select(row, cell);
56382         }
56383     },
56384
56385     /**
56386      * Selects a cell.
56387      * @param {Number} rowIndex
56388      * @param {Number} collIndex
56389      */
56390     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56391         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56392             this.clearSelections();
56393             r = r || this.grid.dataSource.getAt(rowIndex);
56394             this.selection = {
56395                 record : r,
56396                 cell : [rowIndex, colIndex]
56397             };
56398             if(!preventViewNotify){
56399                 var v = this.grid.getView();
56400                 v.onCellSelect(rowIndex, colIndex);
56401                 if(preventFocus !== true){
56402                     v.focusCell(rowIndex, colIndex);
56403                 }
56404             }
56405             this.fireEvent("cellselect", this, rowIndex, colIndex);
56406             this.fireEvent("selectionchange", this, this.selection);
56407         }
56408     },
56409
56410         //private
56411     isSelectable : function(rowIndex, colIndex, cm){
56412         return !cm.isHidden(colIndex);
56413     },
56414
56415     /** @ignore */
56416     handleKeyDown : function(e){
56417         //Roo.log('Cell Sel Model handleKeyDown');
56418         if(!e.isNavKeyPress()){
56419             return;
56420         }
56421         var g = this.grid, s = this.selection;
56422         if(!s){
56423             e.stopEvent();
56424             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56425             if(cell){
56426                 this.select(cell[0], cell[1]);
56427             }
56428             return;
56429         }
56430         var sm = this;
56431         var walk = function(row, col, step){
56432             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56433         };
56434         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56435         var newCell;
56436
56437       
56438
56439         switch(k){
56440             case e.TAB:
56441                 // handled by onEditorKey
56442                 if (g.isEditor && g.editing) {
56443                     return;
56444                 }
56445                 if(e.shiftKey) {
56446                     newCell = walk(r, c-1, -1);
56447                 } else {
56448                     newCell = walk(r, c+1, 1);
56449                 }
56450                 break;
56451             
56452             case e.DOWN:
56453                newCell = walk(r+1, c, 1);
56454                 break;
56455             
56456             case e.UP:
56457                 newCell = walk(r-1, c, -1);
56458                 break;
56459             
56460             case e.RIGHT:
56461                 newCell = walk(r, c+1, 1);
56462                 break;
56463             
56464             case e.LEFT:
56465                 newCell = walk(r, c-1, -1);
56466                 break;
56467             
56468             case e.ENTER:
56469                 
56470                 if(g.isEditor && !g.editing){
56471                    g.startEditing(r, c);
56472                    e.stopEvent();
56473                    return;
56474                 }
56475                 
56476                 
56477              break;
56478         };
56479         if(newCell){
56480             this.select(newCell[0], newCell[1]);
56481             e.stopEvent();
56482             
56483         }
56484     },
56485
56486     acceptsNav : function(row, col, cm){
56487         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56488     },
56489     /**
56490      * Selects a cell.
56491      * @param {Number} field (not used) - as it's normally used as a listener
56492      * @param {Number} e - event - fake it by using
56493      *
56494      * var e = Roo.EventObjectImpl.prototype;
56495      * e.keyCode = e.TAB
56496      *
56497      * 
56498      */
56499     onEditorKey : function(field, e){
56500         
56501         var k = e.getKey(),
56502             newCell,
56503             g = this.grid,
56504             ed = g.activeEditor,
56505             forward = false;
56506         ///Roo.log('onEditorKey' + k);
56507         
56508         
56509         if (this.enter_is_tab && k == e.ENTER) {
56510             k = e.TAB;
56511         }
56512         
56513         if(k == e.TAB){
56514             if(e.shiftKey){
56515                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56516             }else{
56517                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56518                 forward = true;
56519             }
56520             
56521             e.stopEvent();
56522             
56523         } else if(k == e.ENTER &&  !e.ctrlKey){
56524             ed.completeEdit();
56525             e.stopEvent();
56526             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56527         
56528                 } else if(k == e.ESC){
56529             ed.cancelEdit();
56530         }
56531                 
56532         if (newCell) {
56533             var ecall = { cell : newCell, forward : forward };
56534             this.fireEvent('beforeeditnext', ecall );
56535             newCell = ecall.cell;
56536                         forward = ecall.forward;
56537         }
56538                 
56539         if(newCell){
56540             //Roo.log('next cell after edit');
56541             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56542         } else if (forward) {
56543             // tabbed past last
56544             this.fireEvent.defer(100, this, ['tabend',this]);
56545         }
56546     }
56547 });/*
56548  * Based on:
56549  * Ext JS Library 1.1.1
56550  * Copyright(c) 2006-2007, Ext JS, LLC.
56551  *
56552  * Originally Released Under LGPL - original licence link has changed is not relivant.
56553  *
56554  * Fork - LGPL
56555  * <script type="text/javascript">
56556  */
56557  
56558 /**
56559  * @class Roo.grid.EditorGrid
56560  * @extends Roo.grid.Grid
56561  * Class for creating and editable grid.
56562  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56563  * The container MUST have some type of size defined for the grid to fill. The container will be 
56564  * automatically set to position relative if it isn't already.
56565  * @param {Object} dataSource The data model to bind to
56566  * @param {Object} colModel The column model with info about this grid's columns
56567  */
56568 Roo.grid.EditorGrid = function(container, config){
56569     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56570     this.getGridEl().addClass("xedit-grid");
56571
56572     if(!this.selModel){
56573         this.selModel = new Roo.grid.CellSelectionModel();
56574     }
56575
56576     this.activeEditor = null;
56577
56578         this.addEvents({
56579             /**
56580              * @event beforeedit
56581              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56582              * <ul style="padding:5px;padding-left:16px;">
56583              * <li>grid - This grid</li>
56584              * <li>record - The record being edited</li>
56585              * <li>field - The field name being edited</li>
56586              * <li>value - The value for the field being edited.</li>
56587              * <li>row - The grid row index</li>
56588              * <li>column - The grid column index</li>
56589              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56590              * </ul>
56591              * @param {Object} e An edit event (see above for description)
56592              */
56593             "beforeedit" : true,
56594             /**
56595              * @event afteredit
56596              * Fires after a cell is edited. <br />
56597              * <ul style="padding:5px;padding-left:16px;">
56598              * <li>grid - This grid</li>
56599              * <li>record - The record being edited</li>
56600              * <li>field - The field name being edited</li>
56601              * <li>value - The value being set</li>
56602              * <li>originalValue - The original value for the field, before the edit.</li>
56603              * <li>row - The grid row index</li>
56604              * <li>column - The grid column index</li>
56605              * </ul>
56606              * @param {Object} e An edit event (see above for description)
56607              */
56608             "afteredit" : true,
56609             /**
56610              * @event validateedit
56611              * Fires after a cell is edited, but before the value is set in the record. 
56612          * You can use this to modify the value being set in the field, Return false
56613              * to cancel the change. The edit event object has the following properties <br />
56614              * <ul style="padding:5px;padding-left:16px;">
56615          * <li>editor - This editor</li>
56616              * <li>grid - This grid</li>
56617              * <li>record - The record being edited</li>
56618              * <li>field - The field name being edited</li>
56619              * <li>value - The value being set</li>
56620              * <li>originalValue - The original value for the field, before the edit.</li>
56621              * <li>row - The grid row index</li>
56622              * <li>column - The grid column index</li>
56623              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56624              * </ul>
56625              * @param {Object} e An edit event (see above for description)
56626              */
56627             "validateedit" : true
56628         });
56629     this.on("bodyscroll", this.stopEditing,  this);
56630     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56631 };
56632
56633 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56634     /**
56635      * @cfg {Number} clicksToEdit
56636      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56637      */
56638     clicksToEdit: 2,
56639
56640     // private
56641     isEditor : true,
56642     // private
56643     trackMouseOver: false, // causes very odd FF errors
56644
56645     onCellDblClick : function(g, row, col){
56646         this.startEditing(row, col);
56647     },
56648
56649     onEditComplete : function(ed, value, startValue){
56650         this.editing = false;
56651         this.activeEditor = null;
56652         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56653         var r = ed.record;
56654         var field = this.colModel.getDataIndex(ed.col);
56655         var e = {
56656             grid: this,
56657             record: r,
56658             field: field,
56659             originalValue: startValue,
56660             value: value,
56661             row: ed.row,
56662             column: ed.col,
56663             cancel:false,
56664             editor: ed
56665         };
56666         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56667         cell.show();
56668           
56669         if(String(value) !== String(startValue)){
56670             
56671             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56672                 r.set(field, e.value);
56673                 // if we are dealing with a combo box..
56674                 // then we also set the 'name' colum to be the displayField
56675                 if (ed.field.displayField && ed.field.name) {
56676                     r.set(ed.field.name, ed.field.el.dom.value);
56677                 }
56678                 
56679                 delete e.cancel; //?? why!!!
56680                 this.fireEvent("afteredit", e);
56681             }
56682         } else {
56683             this.fireEvent("afteredit", e); // always fire it!
56684         }
56685         this.view.focusCell(ed.row, ed.col);
56686     },
56687
56688     /**
56689      * Starts editing the specified for the specified row/column
56690      * @param {Number} rowIndex
56691      * @param {Number} colIndex
56692      */
56693     startEditing : function(row, col){
56694         this.stopEditing();
56695         if(this.colModel.isCellEditable(col, row)){
56696             this.view.ensureVisible(row, col, true);
56697           
56698             var r = this.dataSource.getAt(row);
56699             var field = this.colModel.getDataIndex(col);
56700             var cell = Roo.get(this.view.getCell(row,col));
56701             var e = {
56702                 grid: this,
56703                 record: r,
56704                 field: field,
56705                 value: r.data[field],
56706                 row: row,
56707                 column: col,
56708                 cancel:false 
56709             };
56710             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56711                 this.editing = true;
56712                 var ed = this.colModel.getCellEditor(col, row);
56713                 
56714                 if (!ed) {
56715                     return;
56716                 }
56717                 if(!ed.rendered){
56718                     ed.render(ed.parentEl || document.body);
56719                 }
56720                 ed.field.reset();
56721                
56722                 cell.hide();
56723                 
56724                 (function(){ // complex but required for focus issues in safari, ie and opera
56725                     ed.row = row;
56726                     ed.col = col;
56727                     ed.record = r;
56728                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56729                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56730                     this.activeEditor = ed;
56731                     var v = r.data[field];
56732                     ed.startEdit(this.view.getCell(row, col), v);
56733                     // combo's with 'displayField and name set
56734                     if (ed.field.displayField && ed.field.name) {
56735                         ed.field.el.dom.value = r.data[ed.field.name];
56736                     }
56737                     
56738                     
56739                 }).defer(50, this);
56740             }
56741         }
56742     },
56743         
56744     /**
56745      * Stops any active editing
56746      */
56747     stopEditing : function(){
56748         if(this.activeEditor){
56749             this.activeEditor.completeEdit();
56750         }
56751         this.activeEditor = null;
56752     },
56753         
56754          /**
56755      * Called to get grid's drag proxy text, by default returns this.ddText.
56756      * @return {String}
56757      */
56758     getDragDropText : function(){
56759         var count = this.selModel.getSelectedCell() ? 1 : 0;
56760         return String.format(this.ddText, count, count == 1 ? '' : 's');
56761     }
56762         
56763 });/*
56764  * Based on:
56765  * Ext JS Library 1.1.1
56766  * Copyright(c) 2006-2007, Ext JS, LLC.
56767  *
56768  * Originally Released Under LGPL - original licence link has changed is not relivant.
56769  *
56770  * Fork - LGPL
56771  * <script type="text/javascript">
56772  */
56773
56774 // private - not really -- you end up using it !
56775 // This is a support class used internally by the Grid components
56776
56777 /**
56778  * @class Roo.grid.GridEditor
56779  * @extends Roo.Editor
56780  * Class for creating and editable grid elements.
56781  * @param {Object} config any settings (must include field)
56782  */
56783 Roo.grid.GridEditor = function(field, config){
56784     if (!config && field.field) {
56785         config = field;
56786         field = Roo.factory(config.field, Roo.form);
56787     }
56788     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56789     field.monitorTab = false;
56790 };
56791
56792 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56793     
56794     /**
56795      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56796      */
56797     
56798     alignment: "tl-tl",
56799     autoSize: "width",
56800     hideEl : false,
56801     cls: "x-small-editor x-grid-editor",
56802     shim:false,
56803     shadow:"frame"
56804 });/*
56805  * Based on:
56806  * Ext JS Library 1.1.1
56807  * Copyright(c) 2006-2007, Ext JS, LLC.
56808  *
56809  * Originally Released Under LGPL - original licence link has changed is not relivant.
56810  *
56811  * Fork - LGPL
56812  * <script type="text/javascript">
56813  */
56814   
56815
56816   
56817 Roo.grid.PropertyRecord = Roo.data.Record.create([
56818     {name:'name',type:'string'},  'value'
56819 ]);
56820
56821
56822 Roo.grid.PropertyStore = function(grid, source){
56823     this.grid = grid;
56824     this.store = new Roo.data.Store({
56825         recordType : Roo.grid.PropertyRecord
56826     });
56827     this.store.on('update', this.onUpdate,  this);
56828     if(source){
56829         this.setSource(source);
56830     }
56831     Roo.grid.PropertyStore.superclass.constructor.call(this);
56832 };
56833
56834
56835
56836 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56837     setSource : function(o){
56838         this.source = o;
56839         this.store.removeAll();
56840         var data = [];
56841         for(var k in o){
56842             if(this.isEditableValue(o[k])){
56843                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56844             }
56845         }
56846         this.store.loadRecords({records: data}, {}, true);
56847     },
56848
56849     onUpdate : function(ds, record, type){
56850         if(type == Roo.data.Record.EDIT){
56851             var v = record.data['value'];
56852             var oldValue = record.modified['value'];
56853             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56854                 this.source[record.id] = v;
56855                 record.commit();
56856                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56857             }else{
56858                 record.reject();
56859             }
56860         }
56861     },
56862
56863     getProperty : function(row){
56864        return this.store.getAt(row);
56865     },
56866
56867     isEditableValue: function(val){
56868         if(val && val instanceof Date){
56869             return true;
56870         }else if(typeof val == 'object' || typeof val == 'function'){
56871             return false;
56872         }
56873         return true;
56874     },
56875
56876     setValue : function(prop, value){
56877         this.source[prop] = value;
56878         this.store.getById(prop).set('value', value);
56879     },
56880
56881     getSource : function(){
56882         return this.source;
56883     }
56884 });
56885
56886 Roo.grid.PropertyColumnModel = function(grid, store){
56887     this.grid = grid;
56888     var g = Roo.grid;
56889     g.PropertyColumnModel.superclass.constructor.call(this, [
56890         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56891         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56892     ]);
56893     this.store = store;
56894     this.bselect = Roo.DomHelper.append(document.body, {
56895         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56896             {tag: 'option', value: 'true', html: 'true'},
56897             {tag: 'option', value: 'false', html: 'false'}
56898         ]
56899     });
56900     Roo.id(this.bselect);
56901     var f = Roo.form;
56902     this.editors = {
56903         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56904         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56905         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56906         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56907         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56908     };
56909     this.renderCellDelegate = this.renderCell.createDelegate(this);
56910     this.renderPropDelegate = this.renderProp.createDelegate(this);
56911 };
56912
56913 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56914     
56915     
56916     nameText : 'Name',
56917     valueText : 'Value',
56918     
56919     dateFormat : 'm/j/Y',
56920     
56921     
56922     renderDate : function(dateVal){
56923         return dateVal.dateFormat(this.dateFormat);
56924     },
56925
56926     renderBool : function(bVal){
56927         return bVal ? 'true' : 'false';
56928     },
56929
56930     isCellEditable : function(colIndex, rowIndex){
56931         return colIndex == 1;
56932     },
56933
56934     getRenderer : function(col){
56935         return col == 1 ?
56936             this.renderCellDelegate : this.renderPropDelegate;
56937     },
56938
56939     renderProp : function(v){
56940         return this.getPropertyName(v);
56941     },
56942
56943     renderCell : function(val){
56944         var rv = val;
56945         if(val instanceof Date){
56946             rv = this.renderDate(val);
56947         }else if(typeof val == 'boolean'){
56948             rv = this.renderBool(val);
56949         }
56950         return Roo.util.Format.htmlEncode(rv);
56951     },
56952
56953     getPropertyName : function(name){
56954         var pn = this.grid.propertyNames;
56955         return pn && pn[name] ? pn[name] : name;
56956     },
56957
56958     getCellEditor : function(colIndex, rowIndex){
56959         var p = this.store.getProperty(rowIndex);
56960         var n = p.data['name'], val = p.data['value'];
56961         
56962         if(typeof(this.grid.customEditors[n]) == 'string'){
56963             return this.editors[this.grid.customEditors[n]];
56964         }
56965         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56966             return this.grid.customEditors[n];
56967         }
56968         if(val instanceof Date){
56969             return this.editors['date'];
56970         }else if(typeof val == 'number'){
56971             return this.editors['number'];
56972         }else if(typeof val == 'boolean'){
56973             return this.editors['boolean'];
56974         }else{
56975             return this.editors['string'];
56976         }
56977     }
56978 });
56979
56980 /**
56981  * @class Roo.grid.PropertyGrid
56982  * @extends Roo.grid.EditorGrid
56983  * This class represents the  interface of a component based property grid control.
56984  * <br><br>Usage:<pre><code>
56985  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56986       
56987  });
56988  // set any options
56989  grid.render();
56990  * </code></pre>
56991   
56992  * @constructor
56993  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56994  * The container MUST have some type of size defined for the grid to fill. The container will be
56995  * automatically set to position relative if it isn't already.
56996  * @param {Object} config A config object that sets properties on this grid.
56997  */
56998 Roo.grid.PropertyGrid = function(container, config){
56999     config = config || {};
57000     var store = new Roo.grid.PropertyStore(this);
57001     this.store = store;
57002     var cm = new Roo.grid.PropertyColumnModel(this, store);
57003     store.store.sort('name', 'ASC');
57004     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57005         ds: store.store,
57006         cm: cm,
57007         enableColLock:false,
57008         enableColumnMove:false,
57009         stripeRows:false,
57010         trackMouseOver: false,
57011         clicksToEdit:1
57012     }, config));
57013     this.getGridEl().addClass('x-props-grid');
57014     this.lastEditRow = null;
57015     this.on('columnresize', this.onColumnResize, this);
57016     this.addEvents({
57017          /**
57018              * @event beforepropertychange
57019              * Fires before a property changes (return false to stop?)
57020              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57021              * @param {String} id Record Id
57022              * @param {String} newval New Value
57023          * @param {String} oldval Old Value
57024              */
57025         "beforepropertychange": true,
57026         /**
57027              * @event propertychange
57028              * Fires after a property changes
57029              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57030              * @param {String} id Record Id
57031              * @param {String} newval New Value
57032          * @param {String} oldval Old Value
57033              */
57034         "propertychange": true
57035     });
57036     this.customEditors = this.customEditors || {};
57037 };
57038 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57039     
57040      /**
57041      * @cfg {Object} customEditors map of colnames=> custom editors.
57042      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57043      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57044      * false disables editing of the field.
57045          */
57046     
57047       /**
57048      * @cfg {Object} propertyNames map of property Names to their displayed value
57049          */
57050     
57051     render : function(){
57052         Roo.grid.PropertyGrid.superclass.render.call(this);
57053         this.autoSize.defer(100, this);
57054     },
57055
57056     autoSize : function(){
57057         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57058         if(this.view){
57059             this.view.fitColumns();
57060         }
57061     },
57062
57063     onColumnResize : function(){
57064         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57065         this.autoSize();
57066     },
57067     /**
57068      * Sets the data for the Grid
57069      * accepts a Key => Value object of all the elements avaiable.
57070      * @param {Object} data  to appear in grid.
57071      */
57072     setSource : function(source){
57073         this.store.setSource(source);
57074         //this.autoSize();
57075     },
57076     /**
57077      * Gets all the data from the grid.
57078      * @return {Object} data  data stored in grid
57079      */
57080     getSource : function(){
57081         return this.store.getSource();
57082     }
57083 });/*
57084   
57085  * Licence LGPL
57086  
57087  */
57088  
57089 /**
57090  * @class Roo.grid.Calendar
57091  * @extends Roo.util.Grid
57092  * This class extends the Grid to provide a calendar widget
57093  * <br><br>Usage:<pre><code>
57094  var grid = new Roo.grid.Calendar("my-container-id", {
57095      ds: myDataStore,
57096      cm: myColModel,
57097      selModel: mySelectionModel,
57098      autoSizeColumns: true,
57099      monitorWindowResize: false,
57100      trackMouseOver: true
57101      eventstore : real data store..
57102  });
57103  // set any options
57104  grid.render();
57105   
57106   * @constructor
57107  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57108  * The container MUST have some type of size defined for the grid to fill. The container will be
57109  * automatically set to position relative if it isn't already.
57110  * @param {Object} config A config object that sets properties on this grid.
57111  */
57112 Roo.grid.Calendar = function(container, config){
57113         // initialize the container
57114         this.container = Roo.get(container);
57115         this.container.update("");
57116         this.container.setStyle("overflow", "hidden");
57117     this.container.addClass('x-grid-container');
57118
57119     this.id = this.container.id;
57120
57121     Roo.apply(this, config);
57122     // check and correct shorthanded configs
57123     
57124     var rows = [];
57125     var d =1;
57126     for (var r = 0;r < 6;r++) {
57127         
57128         rows[r]=[];
57129         for (var c =0;c < 7;c++) {
57130             rows[r][c]= '';
57131         }
57132     }
57133     if (this.eventStore) {
57134         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57135         this.eventStore.on('load',this.onLoad, this);
57136         this.eventStore.on('beforeload',this.clearEvents, this);
57137          
57138     }
57139     
57140     this.dataSource = new Roo.data.Store({
57141             proxy: new Roo.data.MemoryProxy(rows),
57142             reader: new Roo.data.ArrayReader({}, [
57143                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57144     });
57145
57146     this.dataSource.load();
57147     this.ds = this.dataSource;
57148     this.ds.xmodule = this.xmodule || false;
57149     
57150     
57151     var cellRender = function(v,x,r)
57152     {
57153         return String.format(
57154             '<div class="fc-day  fc-widget-content"><div>' +
57155                 '<div class="fc-event-container"></div>' +
57156                 '<div class="fc-day-number">{0}</div>'+
57157                 
57158                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57159             '</div></div>', v);
57160     
57161     }
57162     
57163     
57164     this.colModel = new Roo.grid.ColumnModel( [
57165         {
57166             xtype: 'ColumnModel',
57167             xns: Roo.grid,
57168             dataIndex : 'weekday0',
57169             header : 'Sunday',
57170             renderer : cellRender
57171         },
57172         {
57173             xtype: 'ColumnModel',
57174             xns: Roo.grid,
57175             dataIndex : 'weekday1',
57176             header : 'Monday',
57177             renderer : cellRender
57178         },
57179         {
57180             xtype: 'ColumnModel',
57181             xns: Roo.grid,
57182             dataIndex : 'weekday2',
57183             header : 'Tuesday',
57184             renderer : cellRender
57185         },
57186         {
57187             xtype: 'ColumnModel',
57188             xns: Roo.grid,
57189             dataIndex : 'weekday3',
57190             header : 'Wednesday',
57191             renderer : cellRender
57192         },
57193         {
57194             xtype: 'ColumnModel',
57195             xns: Roo.grid,
57196             dataIndex : 'weekday4',
57197             header : 'Thursday',
57198             renderer : cellRender
57199         },
57200         {
57201             xtype: 'ColumnModel',
57202             xns: Roo.grid,
57203             dataIndex : 'weekday5',
57204             header : 'Friday',
57205             renderer : cellRender
57206         },
57207         {
57208             xtype: 'ColumnModel',
57209             xns: Roo.grid,
57210             dataIndex : 'weekday6',
57211             header : 'Saturday',
57212             renderer : cellRender
57213         }
57214     ]);
57215     this.cm = this.colModel;
57216     this.cm.xmodule = this.xmodule || false;
57217  
57218         
57219           
57220     //this.selModel = new Roo.grid.CellSelectionModel();
57221     //this.sm = this.selModel;
57222     //this.selModel.init(this);
57223     
57224     
57225     if(this.width){
57226         this.container.setWidth(this.width);
57227     }
57228
57229     if(this.height){
57230         this.container.setHeight(this.height);
57231     }
57232     /** @private */
57233         this.addEvents({
57234         // raw events
57235         /**
57236          * @event click
57237          * The raw click event for the entire grid.
57238          * @param {Roo.EventObject} e
57239          */
57240         "click" : true,
57241         /**
57242          * @event dblclick
57243          * The raw dblclick event for the entire grid.
57244          * @param {Roo.EventObject} e
57245          */
57246         "dblclick" : true,
57247         /**
57248          * @event contextmenu
57249          * The raw contextmenu event for the entire grid.
57250          * @param {Roo.EventObject} e
57251          */
57252         "contextmenu" : true,
57253         /**
57254          * @event mousedown
57255          * The raw mousedown event for the entire grid.
57256          * @param {Roo.EventObject} e
57257          */
57258         "mousedown" : true,
57259         /**
57260          * @event mouseup
57261          * The raw mouseup event for the entire grid.
57262          * @param {Roo.EventObject} e
57263          */
57264         "mouseup" : true,
57265         /**
57266          * @event mouseover
57267          * The raw mouseover event for the entire grid.
57268          * @param {Roo.EventObject} e
57269          */
57270         "mouseover" : true,
57271         /**
57272          * @event mouseout
57273          * The raw mouseout event for the entire grid.
57274          * @param {Roo.EventObject} e
57275          */
57276         "mouseout" : true,
57277         /**
57278          * @event keypress
57279          * The raw keypress event for the entire grid.
57280          * @param {Roo.EventObject} e
57281          */
57282         "keypress" : true,
57283         /**
57284          * @event keydown
57285          * The raw keydown event for the entire grid.
57286          * @param {Roo.EventObject} e
57287          */
57288         "keydown" : true,
57289
57290         // custom events
57291
57292         /**
57293          * @event cellclick
57294          * Fires when a cell is clicked
57295          * @param {Grid} this
57296          * @param {Number} rowIndex
57297          * @param {Number} columnIndex
57298          * @param {Roo.EventObject} e
57299          */
57300         "cellclick" : true,
57301         /**
57302          * @event celldblclick
57303          * Fires when a cell is double clicked
57304          * @param {Grid} this
57305          * @param {Number} rowIndex
57306          * @param {Number} columnIndex
57307          * @param {Roo.EventObject} e
57308          */
57309         "celldblclick" : true,
57310         /**
57311          * @event rowclick
57312          * Fires when a row is clicked
57313          * @param {Grid} this
57314          * @param {Number} rowIndex
57315          * @param {Roo.EventObject} e
57316          */
57317         "rowclick" : true,
57318         /**
57319          * @event rowdblclick
57320          * Fires when a row is double clicked
57321          * @param {Grid} this
57322          * @param {Number} rowIndex
57323          * @param {Roo.EventObject} e
57324          */
57325         "rowdblclick" : true,
57326         /**
57327          * @event headerclick
57328          * Fires when a header is clicked
57329          * @param {Grid} this
57330          * @param {Number} columnIndex
57331          * @param {Roo.EventObject} e
57332          */
57333         "headerclick" : true,
57334         /**
57335          * @event headerdblclick
57336          * Fires when a header cell is double clicked
57337          * @param {Grid} this
57338          * @param {Number} columnIndex
57339          * @param {Roo.EventObject} e
57340          */
57341         "headerdblclick" : true,
57342         /**
57343          * @event rowcontextmenu
57344          * Fires when a row is right clicked
57345          * @param {Grid} this
57346          * @param {Number} rowIndex
57347          * @param {Roo.EventObject} e
57348          */
57349         "rowcontextmenu" : true,
57350         /**
57351          * @event cellcontextmenu
57352          * Fires when a cell is right clicked
57353          * @param {Grid} this
57354          * @param {Number} rowIndex
57355          * @param {Number} cellIndex
57356          * @param {Roo.EventObject} e
57357          */
57358          "cellcontextmenu" : true,
57359         /**
57360          * @event headercontextmenu
57361          * Fires when a header is right clicked
57362          * @param {Grid} this
57363          * @param {Number} columnIndex
57364          * @param {Roo.EventObject} e
57365          */
57366         "headercontextmenu" : true,
57367         /**
57368          * @event bodyscroll
57369          * Fires when the body element is scrolled
57370          * @param {Number} scrollLeft
57371          * @param {Number} scrollTop
57372          */
57373         "bodyscroll" : true,
57374         /**
57375          * @event columnresize
57376          * Fires when the user resizes a column
57377          * @param {Number} columnIndex
57378          * @param {Number} newSize
57379          */
57380         "columnresize" : true,
57381         /**
57382          * @event columnmove
57383          * Fires when the user moves a column
57384          * @param {Number} oldIndex
57385          * @param {Number} newIndex
57386          */
57387         "columnmove" : true,
57388         /**
57389          * @event startdrag
57390          * Fires when row(s) start being dragged
57391          * @param {Grid} this
57392          * @param {Roo.GridDD} dd The drag drop object
57393          * @param {event} e The raw browser event
57394          */
57395         "startdrag" : true,
57396         /**
57397          * @event enddrag
57398          * Fires when a drag operation is complete
57399          * @param {Grid} this
57400          * @param {Roo.GridDD} dd The drag drop object
57401          * @param {event} e The raw browser event
57402          */
57403         "enddrag" : true,
57404         /**
57405          * @event dragdrop
57406          * Fires when dragged row(s) are dropped on a valid DD target
57407          * @param {Grid} this
57408          * @param {Roo.GridDD} dd The drag drop object
57409          * @param {String} targetId The target drag drop object
57410          * @param {event} e The raw browser event
57411          */
57412         "dragdrop" : true,
57413         /**
57414          * @event dragover
57415          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57416          * @param {Grid} this
57417          * @param {Roo.GridDD} dd The drag drop object
57418          * @param {String} targetId The target drag drop object
57419          * @param {event} e The raw browser event
57420          */
57421         "dragover" : true,
57422         /**
57423          * @event dragenter
57424          *  Fires when the dragged row(s) first cross another DD target while being dragged
57425          * @param {Grid} this
57426          * @param {Roo.GridDD} dd The drag drop object
57427          * @param {String} targetId The target drag drop object
57428          * @param {event} e The raw browser event
57429          */
57430         "dragenter" : true,
57431         /**
57432          * @event dragout
57433          * Fires when the dragged row(s) leave another DD target while being dragged
57434          * @param {Grid} this
57435          * @param {Roo.GridDD} dd The drag drop object
57436          * @param {String} targetId The target drag drop object
57437          * @param {event} e The raw browser event
57438          */
57439         "dragout" : true,
57440         /**
57441          * @event rowclass
57442          * Fires when a row is rendered, so you can change add a style to it.
57443          * @param {GridView} gridview   The grid view
57444          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57445          */
57446         'rowclass' : true,
57447
57448         /**
57449          * @event render
57450          * Fires when the grid is rendered
57451          * @param {Grid} grid
57452          */
57453         'render' : true,
57454             /**
57455              * @event select
57456              * Fires when a date is selected
57457              * @param {DatePicker} this
57458              * @param {Date} date The selected date
57459              */
57460         'select': true,
57461         /**
57462              * @event monthchange
57463              * Fires when the displayed month changes 
57464              * @param {DatePicker} this
57465              * @param {Date} date The selected month
57466              */
57467         'monthchange': true,
57468         /**
57469              * @event evententer
57470              * Fires when mouse over an event
57471              * @param {Calendar} this
57472              * @param {event} Event
57473              */
57474         'evententer': true,
57475         /**
57476              * @event eventleave
57477              * Fires when the mouse leaves an
57478              * @param {Calendar} this
57479              * @param {event}
57480              */
57481         'eventleave': true,
57482         /**
57483              * @event eventclick
57484              * Fires when the mouse click an
57485              * @param {Calendar} this
57486              * @param {event}
57487              */
57488         'eventclick': true,
57489         /**
57490              * @event eventrender
57491              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57492              * @param {Calendar} this
57493              * @param {data} data to be modified
57494              */
57495         'eventrender': true
57496         
57497     });
57498
57499     Roo.grid.Grid.superclass.constructor.call(this);
57500     this.on('render', function() {
57501         this.view.el.addClass('x-grid-cal'); 
57502         
57503         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57504
57505     },this);
57506     
57507     if (!Roo.grid.Calendar.style) {
57508         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57509             
57510             
57511             '.x-grid-cal .x-grid-col' :  {
57512                 height: 'auto !important',
57513                 'vertical-align': 'top'
57514             },
57515             '.x-grid-cal  .fc-event-hori' : {
57516                 height: '14px'
57517             }
57518              
57519             
57520         }, Roo.id());
57521     }
57522
57523     
57524     
57525 };
57526 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57527     /**
57528      * @cfg {Store} eventStore The store that loads events.
57529      */
57530     eventStore : 25,
57531
57532      
57533     activeDate : false,
57534     startDay : 0,
57535     autoWidth : true,
57536     monitorWindowResize : false,
57537
57538     
57539     resizeColumns : function() {
57540         var col = (this.view.el.getWidth() / 7) - 3;
57541         // loop through cols, and setWidth
57542         for(var i =0 ; i < 7 ; i++){
57543             this.cm.setColumnWidth(i, col);
57544         }
57545     },
57546      setDate :function(date) {
57547         
57548         Roo.log('setDate?');
57549         
57550         this.resizeColumns();
57551         var vd = this.activeDate;
57552         this.activeDate = date;
57553 //        if(vd && this.el){
57554 //            var t = date.getTime();
57555 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57556 //                Roo.log('using add remove');
57557 //                
57558 //                this.fireEvent('monthchange', this, date);
57559 //                
57560 //                this.cells.removeClass("fc-state-highlight");
57561 //                this.cells.each(function(c){
57562 //                   if(c.dateValue == t){
57563 //                       c.addClass("fc-state-highlight");
57564 //                       setTimeout(function(){
57565 //                            try{c.dom.firstChild.focus();}catch(e){}
57566 //                       }, 50);
57567 //                       return false;
57568 //                   }
57569 //                   return true;
57570 //                });
57571 //                return;
57572 //            }
57573 //        }
57574         
57575         var days = date.getDaysInMonth();
57576         
57577         var firstOfMonth = date.getFirstDateOfMonth();
57578         var startingPos = firstOfMonth.getDay()-this.startDay;
57579         
57580         if(startingPos < this.startDay){
57581             startingPos += 7;
57582         }
57583         
57584         var pm = date.add(Date.MONTH, -1);
57585         var prevStart = pm.getDaysInMonth()-startingPos;
57586 //        
57587         
57588         
57589         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57590         
57591         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57592         //this.cells.addClassOnOver('fc-state-hover');
57593         
57594         var cells = this.cells.elements;
57595         var textEls = this.textNodes;
57596         
57597         //Roo.each(cells, function(cell){
57598         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57599         //});
57600         
57601         days += startingPos;
57602
57603         // convert everything to numbers so it's fast
57604         var day = 86400000;
57605         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57606         //Roo.log(d);
57607         //Roo.log(pm);
57608         //Roo.log(prevStart);
57609         
57610         var today = new Date().clearTime().getTime();
57611         var sel = date.clearTime().getTime();
57612         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57613         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57614         var ddMatch = this.disabledDatesRE;
57615         var ddText = this.disabledDatesText;
57616         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57617         var ddaysText = this.disabledDaysText;
57618         var format = this.format;
57619         
57620         var setCellClass = function(cal, cell){
57621             
57622             //Roo.log('set Cell Class');
57623             cell.title = "";
57624             var t = d.getTime();
57625             
57626             //Roo.log(d);
57627             
57628             
57629             cell.dateValue = t;
57630             if(t == today){
57631                 cell.className += " fc-today";
57632                 cell.className += " fc-state-highlight";
57633                 cell.title = cal.todayText;
57634             }
57635             if(t == sel){
57636                 // disable highlight in other month..
57637                 cell.className += " fc-state-highlight";
57638                 
57639             }
57640             // disabling
57641             if(t < min) {
57642                 //cell.className = " fc-state-disabled";
57643                 cell.title = cal.minText;
57644                 return;
57645             }
57646             if(t > max) {
57647                 //cell.className = " fc-state-disabled";
57648                 cell.title = cal.maxText;
57649                 return;
57650             }
57651             if(ddays){
57652                 if(ddays.indexOf(d.getDay()) != -1){
57653                     // cell.title = ddaysText;
57654                    // cell.className = " fc-state-disabled";
57655                 }
57656             }
57657             if(ddMatch && format){
57658                 var fvalue = d.dateFormat(format);
57659                 if(ddMatch.test(fvalue)){
57660                     cell.title = ddText.replace("%0", fvalue);
57661                    cell.className = " fc-state-disabled";
57662                 }
57663             }
57664             
57665             if (!cell.initialClassName) {
57666                 cell.initialClassName = cell.dom.className;
57667             }
57668             
57669             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57670         };
57671
57672         var i = 0;
57673         
57674         for(; i < startingPos; i++) {
57675             cells[i].dayName =  (++prevStart);
57676             Roo.log(textEls[i]);
57677             d.setDate(d.getDate()+1);
57678             
57679             //cells[i].className = "fc-past fc-other-month";
57680             setCellClass(this, cells[i]);
57681         }
57682         
57683         var intDay = 0;
57684         
57685         for(; i < days; i++){
57686             intDay = i - startingPos + 1;
57687             cells[i].dayName =  (intDay);
57688             d.setDate(d.getDate()+1);
57689             
57690             cells[i].className = ''; // "x-date-active";
57691             setCellClass(this, cells[i]);
57692         }
57693         var extraDays = 0;
57694         
57695         for(; i < 42; i++) {
57696             //textEls[i].innerHTML = (++extraDays);
57697             
57698             d.setDate(d.getDate()+1);
57699             cells[i].dayName = (++extraDays);
57700             cells[i].className = "fc-future fc-other-month";
57701             setCellClass(this, cells[i]);
57702         }
57703         
57704         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57705         
57706         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57707         
57708         // this will cause all the cells to mis
57709         var rows= [];
57710         var i =0;
57711         for (var r = 0;r < 6;r++) {
57712             for (var c =0;c < 7;c++) {
57713                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57714             }    
57715         }
57716         
57717         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57718         for(i=0;i<cells.length;i++) {
57719             
57720             this.cells.elements[i].dayName = cells[i].dayName ;
57721             this.cells.elements[i].className = cells[i].className;
57722             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57723             this.cells.elements[i].title = cells[i].title ;
57724             this.cells.elements[i].dateValue = cells[i].dateValue ;
57725         }
57726         
57727         
57728         
57729         
57730         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57731         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57732         
57733         ////if(totalRows != 6){
57734             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57735            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57736        // }
57737         
57738         this.fireEvent('monthchange', this, date);
57739         
57740         
57741     },
57742  /**
57743      * Returns the grid's SelectionModel.
57744      * @return {SelectionModel}
57745      */
57746     getSelectionModel : function(){
57747         if(!this.selModel){
57748             this.selModel = new Roo.grid.CellSelectionModel();
57749         }
57750         return this.selModel;
57751     },
57752
57753     load: function() {
57754         this.eventStore.load()
57755         
57756         
57757         
57758     },
57759     
57760     findCell : function(dt) {
57761         dt = dt.clearTime().getTime();
57762         var ret = false;
57763         this.cells.each(function(c){
57764             //Roo.log("check " +c.dateValue + '?=' + dt);
57765             if(c.dateValue == dt){
57766                 ret = c;
57767                 return false;
57768             }
57769             return true;
57770         });
57771         
57772         return ret;
57773     },
57774     
57775     findCells : function(rec) {
57776         var s = rec.data.start_dt.clone().clearTime().getTime();
57777        // Roo.log(s);
57778         var e= rec.data.end_dt.clone().clearTime().getTime();
57779        // Roo.log(e);
57780         var ret = [];
57781         this.cells.each(function(c){
57782              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57783             
57784             if(c.dateValue > e){
57785                 return ;
57786             }
57787             if(c.dateValue < s){
57788                 return ;
57789             }
57790             ret.push(c);
57791         });
57792         
57793         return ret;    
57794     },
57795     
57796     findBestRow: function(cells)
57797     {
57798         var ret = 0;
57799         
57800         for (var i =0 ; i < cells.length;i++) {
57801             ret  = Math.max(cells[i].rows || 0,ret);
57802         }
57803         return ret;
57804         
57805     },
57806     
57807     
57808     addItem : function(rec)
57809     {
57810         // look for vertical location slot in
57811         var cells = this.findCells(rec);
57812         
57813         rec.row = this.findBestRow(cells);
57814         
57815         // work out the location.
57816         
57817         var crow = false;
57818         var rows = [];
57819         for(var i =0; i < cells.length; i++) {
57820             if (!crow) {
57821                 crow = {
57822                     start : cells[i],
57823                     end :  cells[i]
57824                 };
57825                 continue;
57826             }
57827             if (crow.start.getY() == cells[i].getY()) {
57828                 // on same row.
57829                 crow.end = cells[i];
57830                 continue;
57831             }
57832             // different row.
57833             rows.push(crow);
57834             crow = {
57835                 start: cells[i],
57836                 end : cells[i]
57837             };
57838             
57839         }
57840         
57841         rows.push(crow);
57842         rec.els = [];
57843         rec.rows = rows;
57844         rec.cells = cells;
57845         for (var i = 0; i < cells.length;i++) {
57846             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57847             
57848         }
57849         
57850         
57851     },
57852     
57853     clearEvents: function() {
57854         
57855         if (!this.eventStore.getCount()) {
57856             return;
57857         }
57858         // reset number of rows in cells.
57859         Roo.each(this.cells.elements, function(c){
57860             c.rows = 0;
57861         });
57862         
57863         this.eventStore.each(function(e) {
57864             this.clearEvent(e);
57865         },this);
57866         
57867     },
57868     
57869     clearEvent : function(ev)
57870     {
57871         if (ev.els) {
57872             Roo.each(ev.els, function(el) {
57873                 el.un('mouseenter' ,this.onEventEnter, this);
57874                 el.un('mouseleave' ,this.onEventLeave, this);
57875                 el.remove();
57876             },this);
57877             ev.els = [];
57878         }
57879     },
57880     
57881     
57882     renderEvent : function(ev,ctr) {
57883         if (!ctr) {
57884              ctr = this.view.el.select('.fc-event-container',true).first();
57885         }
57886         
57887          
57888         this.clearEvent(ev);
57889             //code
57890        
57891         
57892         
57893         ev.els = [];
57894         var cells = ev.cells;
57895         var rows = ev.rows;
57896         this.fireEvent('eventrender', this, ev);
57897         
57898         for(var i =0; i < rows.length; i++) {
57899             
57900             cls = '';
57901             if (i == 0) {
57902                 cls += ' fc-event-start';
57903             }
57904             if ((i+1) == rows.length) {
57905                 cls += ' fc-event-end';
57906             }
57907             
57908             //Roo.log(ev.data);
57909             // how many rows should it span..
57910             var cg = this.eventTmpl.append(ctr,Roo.apply({
57911                 fccls : cls
57912                 
57913             }, ev.data) , true);
57914             
57915             
57916             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57917             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57918             cg.on('click', this.onEventClick, this, ev);
57919             
57920             ev.els.push(cg);
57921             
57922             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57923             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57924             //Roo.log(cg);
57925              
57926             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57927             cg.setWidth(ebox.right - sbox.x -2);
57928         }
57929     },
57930     
57931     renderEvents: function()
57932     {   
57933         // first make sure there is enough space..
57934         
57935         if (!this.eventTmpl) {
57936             this.eventTmpl = new Roo.Template(
57937                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57938                     '<div class="fc-event-inner">' +
57939                         '<span class="fc-event-time">{time}</span>' +
57940                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57941                     '</div>' +
57942                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57943                 '</div>'
57944             );
57945                 
57946         }
57947                
57948         
57949         
57950         this.cells.each(function(c) {
57951             //Roo.log(c.select('.fc-day-content div',true).first());
57952             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57953         });
57954         
57955         var ctr = this.view.el.select('.fc-event-container',true).first();
57956         
57957         var cls;
57958         this.eventStore.each(function(ev){
57959             
57960             this.renderEvent(ev);
57961              
57962              
57963         }, this);
57964         this.view.layout();
57965         
57966     },
57967     
57968     onEventEnter: function (e, el,event,d) {
57969         this.fireEvent('evententer', this, el, event);
57970     },
57971     
57972     onEventLeave: function (e, el,event,d) {
57973         this.fireEvent('eventleave', this, el, event);
57974     },
57975     
57976     onEventClick: function (e, el,event,d) {
57977         this.fireEvent('eventclick', this, el, event);
57978     },
57979     
57980     onMonthChange: function () {
57981         this.store.load();
57982     },
57983     
57984     onLoad: function () {
57985         
57986         //Roo.log('calendar onload');
57987 //         
57988         if(this.eventStore.getCount() > 0){
57989             
57990            
57991             
57992             this.eventStore.each(function(d){
57993                 
57994                 
57995                 // FIXME..
57996                 var add =   d.data;
57997                 if (typeof(add.end_dt) == 'undefined')  {
57998                     Roo.log("Missing End time in calendar data: ");
57999                     Roo.log(d);
58000                     return;
58001                 }
58002                 if (typeof(add.start_dt) == 'undefined')  {
58003                     Roo.log("Missing Start time in calendar data: ");
58004                     Roo.log(d);
58005                     return;
58006                 }
58007                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58008                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58009                 add.id = add.id || d.id;
58010                 add.title = add.title || '??';
58011                 
58012                 this.addItem(d);
58013                 
58014              
58015             },this);
58016         }
58017         
58018         this.renderEvents();
58019     }
58020     
58021
58022 });
58023 /*
58024  grid : {
58025                 xtype: 'Grid',
58026                 xns: Roo.grid,
58027                 listeners : {
58028                     render : function ()
58029                     {
58030                         _this.grid = this;
58031                         
58032                         if (!this.view.el.hasClass('course-timesheet')) {
58033                             this.view.el.addClass('course-timesheet');
58034                         }
58035                         if (this.tsStyle) {
58036                             this.ds.load({});
58037                             return; 
58038                         }
58039                         Roo.log('width');
58040                         Roo.log(_this.grid.view.el.getWidth());
58041                         
58042                         
58043                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58044                             '.course-timesheet .x-grid-row' : {
58045                                 height: '80px'
58046                             },
58047                             '.x-grid-row td' : {
58048                                 'vertical-align' : 0
58049                             },
58050                             '.course-edit-link' : {
58051                                 'color' : 'blue',
58052                                 'text-overflow' : 'ellipsis',
58053                                 'overflow' : 'hidden',
58054                                 'white-space' : 'nowrap',
58055                                 'cursor' : 'pointer'
58056                             },
58057                             '.sub-link' : {
58058                                 'color' : 'green'
58059                             },
58060                             '.de-act-sup-link' : {
58061                                 'color' : 'purple',
58062                                 'text-decoration' : 'line-through'
58063                             },
58064                             '.de-act-link' : {
58065                                 'color' : 'red',
58066                                 'text-decoration' : 'line-through'
58067                             },
58068                             '.course-timesheet .course-highlight' : {
58069                                 'border-top-style': 'dashed !important',
58070                                 'border-bottom-bottom': 'dashed !important'
58071                             },
58072                             '.course-timesheet .course-item' : {
58073                                 'font-family'   : 'tahoma, arial, helvetica',
58074                                 'font-size'     : '11px',
58075                                 'overflow'      : 'hidden',
58076                                 'padding-left'  : '10px',
58077                                 'padding-right' : '10px',
58078                                 'padding-top' : '10px' 
58079                             }
58080                             
58081                         }, Roo.id());
58082                                 this.ds.load({});
58083                     }
58084                 },
58085                 autoWidth : true,
58086                 monitorWindowResize : false,
58087                 cellrenderer : function(v,x,r)
58088                 {
58089                     return v;
58090                 },
58091                 sm : {
58092                     xtype: 'CellSelectionModel',
58093                     xns: Roo.grid
58094                 },
58095                 dataSource : {
58096                     xtype: 'Store',
58097                     xns: Roo.data,
58098                     listeners : {
58099                         beforeload : function (_self, options)
58100                         {
58101                             options.params = options.params || {};
58102                             options.params._month = _this.monthField.getValue();
58103                             options.params.limit = 9999;
58104                             options.params['sort'] = 'when_dt';    
58105                             options.params['dir'] = 'ASC';    
58106                             this.proxy.loadResponse = this.loadResponse;
58107                             Roo.log("load?");
58108                             //this.addColumns();
58109                         },
58110                         load : function (_self, records, options)
58111                         {
58112                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58113                                 // if you click on the translation.. you can edit it...
58114                                 var el = Roo.get(this);
58115                                 var id = el.dom.getAttribute('data-id');
58116                                 var d = el.dom.getAttribute('data-date');
58117                                 var t = el.dom.getAttribute('data-time');
58118                                 //var id = this.child('span').dom.textContent;
58119                                 
58120                                 //Roo.log(this);
58121                                 Pman.Dialog.CourseCalendar.show({
58122                                     id : id,
58123                                     when_d : d,
58124                                     when_t : t,
58125                                     productitem_active : id ? 1 : 0
58126                                 }, function() {
58127                                     _this.grid.ds.load({});
58128                                 });
58129                            
58130                            });
58131                            
58132                            _this.panel.fireEvent('resize', [ '', '' ]);
58133                         }
58134                     },
58135                     loadResponse : function(o, success, response){
58136                             // this is overridden on before load..
58137                             
58138                             Roo.log("our code?");       
58139                             //Roo.log(success);
58140                             //Roo.log(response)
58141                             delete this.activeRequest;
58142                             if(!success){
58143                                 this.fireEvent("loadexception", this, o, response);
58144                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58145                                 return;
58146                             }
58147                             var result;
58148                             try {
58149                                 result = o.reader.read(response);
58150                             }catch(e){
58151                                 Roo.log("load exception?");
58152                                 this.fireEvent("loadexception", this, o, response, e);
58153                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58154                                 return;
58155                             }
58156                             Roo.log("ready...");        
58157                             // loop through result.records;
58158                             // and set this.tdate[date] = [] << array of records..
58159                             _this.tdata  = {};
58160                             Roo.each(result.records, function(r){
58161                                 //Roo.log(r.data);
58162                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58163                                     _this.tdata[r.data.when_dt.format('j')] = [];
58164                                 }
58165                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58166                             });
58167                             
58168                             //Roo.log(_this.tdata);
58169                             
58170                             result.records = [];
58171                             result.totalRecords = 6;
58172                     
58173                             // let's generate some duumy records for the rows.
58174                             //var st = _this.dateField.getValue();
58175                             
58176                             // work out monday..
58177                             //st = st.add(Date.DAY, -1 * st.format('w'));
58178                             
58179                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58180                             
58181                             var firstOfMonth = date.getFirstDayOfMonth();
58182                             var days = date.getDaysInMonth();
58183                             var d = 1;
58184                             var firstAdded = false;
58185                             for (var i = 0; i < result.totalRecords ; i++) {
58186                                 //var d= st.add(Date.DAY, i);
58187                                 var row = {};
58188                                 var added = 0;
58189                                 for(var w = 0 ; w < 7 ; w++){
58190                                     if(!firstAdded && firstOfMonth != w){
58191                                         continue;
58192                                     }
58193                                     if(d > days){
58194                                         continue;
58195                                     }
58196                                     firstAdded = true;
58197                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58198                                     row['weekday'+w] = String.format(
58199                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58200                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58201                                                     d,
58202                                                     date.format('Y-m-')+dd
58203                                                 );
58204                                     added++;
58205                                     if(typeof(_this.tdata[d]) != 'undefined'){
58206                                         Roo.each(_this.tdata[d], function(r){
58207                                             var is_sub = '';
58208                                             var deactive = '';
58209                                             var id = r.id;
58210                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58211                                             if(r.parent_id*1>0){
58212                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58213                                                 id = r.parent_id;
58214                                             }
58215                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58216                                                 deactive = 'de-act-link';
58217                                             }
58218                                             
58219                                             row['weekday'+w] += String.format(
58220                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58221                                                     id, //0
58222                                                     r.product_id_name, //1
58223                                                     r.when_dt.format('h:ia'), //2
58224                                                     is_sub, //3
58225                                                     deactive, //4
58226                                                     desc // 5
58227                                             );
58228                                         });
58229                                     }
58230                                     d++;
58231                                 }
58232                                 
58233                                 // only do this if something added..
58234                                 if(added > 0){ 
58235                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58236                                 }
58237                                 
58238                                 
58239                                 // push it twice. (second one with an hour..
58240                                 
58241                             }
58242                             //Roo.log(result);
58243                             this.fireEvent("load", this, o, o.request.arg);
58244                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58245                         },
58246                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58247                     proxy : {
58248                         xtype: 'HttpProxy',
58249                         xns: Roo.data,
58250                         method : 'GET',
58251                         url : baseURL + '/Roo/Shop_course.php'
58252                     },
58253                     reader : {
58254                         xtype: 'JsonReader',
58255                         xns: Roo.data,
58256                         id : 'id',
58257                         fields : [
58258                             {
58259                                 'name': 'id',
58260                                 'type': 'int'
58261                             },
58262                             {
58263                                 'name': 'when_dt',
58264                                 'type': 'string'
58265                             },
58266                             {
58267                                 'name': 'end_dt',
58268                                 'type': 'string'
58269                             },
58270                             {
58271                                 'name': 'parent_id',
58272                                 'type': 'int'
58273                             },
58274                             {
58275                                 'name': 'product_id',
58276                                 'type': 'int'
58277                             },
58278                             {
58279                                 'name': 'productitem_id',
58280                                 'type': 'int'
58281                             },
58282                             {
58283                                 'name': 'guid',
58284                                 'type': 'int'
58285                             }
58286                         ]
58287                     }
58288                 },
58289                 toolbar : {
58290                     xtype: 'Toolbar',
58291                     xns: Roo,
58292                     items : [
58293                         {
58294                             xtype: 'Button',
58295                             xns: Roo.Toolbar,
58296                             listeners : {
58297                                 click : function (_self, e)
58298                                 {
58299                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58300                                     sd.setMonth(sd.getMonth()-1);
58301                                     _this.monthField.setValue(sd.format('Y-m-d'));
58302                                     _this.grid.ds.load({});
58303                                 }
58304                             },
58305                             text : "Back"
58306                         },
58307                         {
58308                             xtype: 'Separator',
58309                             xns: Roo.Toolbar
58310                         },
58311                         {
58312                             xtype: 'MonthField',
58313                             xns: Roo.form,
58314                             listeners : {
58315                                 render : function (_self)
58316                                 {
58317                                     _this.monthField = _self;
58318                                    // _this.monthField.set  today
58319                                 },
58320                                 select : function (combo, date)
58321                                 {
58322                                     _this.grid.ds.load({});
58323                                 }
58324                             },
58325                             value : (function() { return new Date(); })()
58326                         },
58327                         {
58328                             xtype: 'Separator',
58329                             xns: Roo.Toolbar
58330                         },
58331                         {
58332                             xtype: 'TextItem',
58333                             xns: Roo.Toolbar,
58334                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58335                         },
58336                         {
58337                             xtype: 'Fill',
58338                             xns: Roo.Toolbar
58339                         },
58340                         {
58341                             xtype: 'Button',
58342                             xns: Roo.Toolbar,
58343                             listeners : {
58344                                 click : function (_self, e)
58345                                 {
58346                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58347                                     sd.setMonth(sd.getMonth()+1);
58348                                     _this.monthField.setValue(sd.format('Y-m-d'));
58349                                     _this.grid.ds.load({});
58350                                 }
58351                             },
58352                             text : "Next"
58353                         }
58354                     ]
58355                 },
58356                  
58357             }
58358         };
58359         
58360         *//*
58361  * Based on:
58362  * Ext JS Library 1.1.1
58363  * Copyright(c) 2006-2007, Ext JS, LLC.
58364  *
58365  * Originally Released Under LGPL - original licence link has changed is not relivant.
58366  *
58367  * Fork - LGPL
58368  * <script type="text/javascript">
58369  */
58370  
58371 /**
58372  * @class Roo.LoadMask
58373  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58374  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58375  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58376  * element's UpdateManager load indicator and will be destroyed after the initial load.
58377  * @constructor
58378  * Create a new LoadMask
58379  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58380  * @param {Object} config The config object
58381  */
58382 Roo.LoadMask = function(el, config){
58383     this.el = Roo.get(el);
58384     Roo.apply(this, config);
58385     if(this.store){
58386         this.store.on('beforeload', this.onBeforeLoad, this);
58387         this.store.on('load', this.onLoad, this);
58388         this.store.on('loadexception', this.onLoadException, this);
58389         this.removeMask = false;
58390     }else{
58391         var um = this.el.getUpdateManager();
58392         um.showLoadIndicator = false; // disable the default indicator
58393         um.on('beforeupdate', this.onBeforeLoad, this);
58394         um.on('update', this.onLoad, this);
58395         um.on('failure', this.onLoad, this);
58396         this.removeMask = true;
58397     }
58398 };
58399
58400 Roo.LoadMask.prototype = {
58401     /**
58402      * @cfg {Boolean} removeMask
58403      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58404      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58405      */
58406     /**
58407      * @cfg {String} msg
58408      * The text to display in a centered loading message box (defaults to 'Loading...')
58409      */
58410     msg : 'Loading...',
58411     /**
58412      * @cfg {String} msgCls
58413      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58414      */
58415     msgCls : 'x-mask-loading',
58416
58417     /**
58418      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58419      * @type Boolean
58420      */
58421     disabled: false,
58422
58423     /**
58424      * Disables the mask to prevent it from being displayed
58425      */
58426     disable : function(){
58427        this.disabled = true;
58428     },
58429
58430     /**
58431      * Enables the mask so that it can be displayed
58432      */
58433     enable : function(){
58434         this.disabled = false;
58435     },
58436     
58437     onLoadException : function()
58438     {
58439         Roo.log(arguments);
58440         
58441         if (typeof(arguments[3]) != 'undefined') {
58442             Roo.MessageBox.alert("Error loading",arguments[3]);
58443         } 
58444         /*
58445         try {
58446             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58447                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58448             }   
58449         } catch(e) {
58450             
58451         }
58452         */
58453     
58454         
58455         
58456         this.el.unmask(this.removeMask);
58457     },
58458     // private
58459     onLoad : function()
58460     {
58461         this.el.unmask(this.removeMask);
58462     },
58463
58464     // private
58465     onBeforeLoad : function(){
58466         if(!this.disabled){
58467             this.el.mask(this.msg, this.msgCls);
58468         }
58469     },
58470
58471     // private
58472     destroy : function(){
58473         if(this.store){
58474             this.store.un('beforeload', this.onBeforeLoad, this);
58475             this.store.un('load', this.onLoad, this);
58476             this.store.un('loadexception', this.onLoadException, this);
58477         }else{
58478             var um = this.el.getUpdateManager();
58479             um.un('beforeupdate', this.onBeforeLoad, this);
58480             um.un('update', this.onLoad, this);
58481             um.un('failure', this.onLoad, this);
58482         }
58483     }
58484 };/*
58485  * Based on:
58486  * Ext JS Library 1.1.1
58487  * Copyright(c) 2006-2007, Ext JS, LLC.
58488  *
58489  * Originally Released Under LGPL - original licence link has changed is not relivant.
58490  *
58491  * Fork - LGPL
58492  * <script type="text/javascript">
58493  */
58494
58495
58496 /**
58497  * @class Roo.XTemplate
58498  * @extends Roo.Template
58499  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58500 <pre><code>
58501 var t = new Roo.XTemplate(
58502         '&lt;select name="{name}"&gt;',
58503                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58504         '&lt;/select&gt;'
58505 );
58506  
58507 // then append, applying the master template values
58508  </code></pre>
58509  *
58510  * Supported features:
58511  *
58512  *  Tags:
58513
58514 <pre><code>
58515       {a_variable} - output encoded.
58516       {a_variable.format:("Y-m-d")} - call a method on the variable
58517       {a_variable:raw} - unencoded output
58518       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58519       {a_variable:this.method_on_template(...)} - call a method on the template object.
58520  
58521 </code></pre>
58522  *  The tpl tag:
58523 <pre><code>
58524         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58525         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58526         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58527         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58528   
58529         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58530         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58531 </code></pre>
58532  *      
58533  */
58534 Roo.XTemplate = function()
58535 {
58536     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58537     if (this.html) {
58538         this.compile();
58539     }
58540 };
58541
58542
58543 Roo.extend(Roo.XTemplate, Roo.Template, {
58544
58545     /**
58546      * The various sub templates
58547      */
58548     tpls : false,
58549     /**
58550      *
58551      * basic tag replacing syntax
58552      * WORD:WORD()
58553      *
58554      * // you can fake an object call by doing this
58555      *  x.t:(test,tesT) 
58556      * 
58557      */
58558     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58559
58560     /**
58561      * compile the template
58562      *
58563      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58564      *
58565      */
58566     compile: function()
58567     {
58568         var s = this.html;
58569      
58570         s = ['<tpl>', s, '</tpl>'].join('');
58571     
58572         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58573             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58574             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58575             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58576             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58577             m,
58578             id     = 0,
58579             tpls   = [];
58580     
58581         while(true == !!(m = s.match(re))){
58582             var forMatch   = m[0].match(nameRe),
58583                 ifMatch   = m[0].match(ifRe),
58584                 execMatch   = m[0].match(execRe),
58585                 namedMatch   = m[0].match(namedRe),
58586                 
58587                 exp  = null, 
58588                 fn   = null,
58589                 exec = null,
58590                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58591                 
58592             if (ifMatch) {
58593                 // if - puts fn into test..
58594                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58595                 if(exp){
58596                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58597                 }
58598             }
58599             
58600             if (execMatch) {
58601                 // exec - calls a function... returns empty if true is  returned.
58602                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58603                 if(exp){
58604                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58605                 }
58606             }
58607             
58608             
58609             if (name) {
58610                 // for = 
58611                 switch(name){
58612                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58613                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58614                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58615                 }
58616             }
58617             var uid = namedMatch ? namedMatch[1] : id;
58618             
58619             
58620             tpls.push({
58621                 id:     namedMatch ? namedMatch[1] : id,
58622                 target: name,
58623                 exec:   exec,
58624                 test:   fn,
58625                 body:   m[1] || ''
58626             });
58627             if (namedMatch) {
58628                 s = s.replace(m[0], '');
58629             } else { 
58630                 s = s.replace(m[0], '{xtpl'+ id + '}');
58631             }
58632             ++id;
58633         }
58634         this.tpls = [];
58635         for(var i = tpls.length-1; i >= 0; --i){
58636             this.compileTpl(tpls[i]);
58637             this.tpls[tpls[i].id] = tpls[i];
58638         }
58639         this.master = tpls[tpls.length-1];
58640         return this;
58641     },
58642     /**
58643      * same as applyTemplate, except it's done to one of the subTemplates
58644      * when using named templates, you can do:
58645      *
58646      * var str = pl.applySubTemplate('your-name', values);
58647      *
58648      * 
58649      * @param {Number} id of the template
58650      * @param {Object} values to apply to template
58651      * @param {Object} parent (normaly the instance of this object)
58652      */
58653     applySubTemplate : function(id, values, parent)
58654     {
58655         
58656         
58657         var t = this.tpls[id];
58658         
58659         
58660         try { 
58661             if(t.test && !t.test.call(this, values, parent)){
58662                 return '';
58663             }
58664         } catch(e) {
58665             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58666             Roo.log(e.toString());
58667             Roo.log(t.test);
58668             return ''
58669         }
58670         try { 
58671             
58672             if(t.exec && t.exec.call(this, values, parent)){
58673                 return '';
58674             }
58675         } catch(e) {
58676             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58677             Roo.log(e.toString());
58678             Roo.log(t.exec);
58679             return ''
58680         }
58681         try {
58682             var vs = t.target ? t.target.call(this, values, parent) : values;
58683             parent = t.target ? values : parent;
58684             if(t.target && vs instanceof Array){
58685                 var buf = [];
58686                 for(var i = 0, len = vs.length; i < len; i++){
58687                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58688                 }
58689                 return buf.join('');
58690             }
58691             return t.compiled.call(this, vs, parent);
58692         } catch (e) {
58693             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58694             Roo.log(e.toString());
58695             Roo.log(t.compiled);
58696             return '';
58697         }
58698     },
58699
58700     compileTpl : function(tpl)
58701     {
58702         var fm = Roo.util.Format;
58703         var useF = this.disableFormats !== true;
58704         var sep = Roo.isGecko ? "+" : ",";
58705         var undef = function(str) {
58706             Roo.log("Property not found :"  + str);
58707             return '';
58708         };
58709         
58710         var fn = function(m, name, format, args)
58711         {
58712             //Roo.log(arguments);
58713             args = args ? args.replace(/\\'/g,"'") : args;
58714             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58715             if (typeof(format) == 'undefined') {
58716                 format= 'htmlEncode';
58717             }
58718             if (format == 'raw' ) {
58719                 format = false;
58720             }
58721             
58722             if(name.substr(0, 4) == 'xtpl'){
58723                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58724             }
58725             
58726             // build an array of options to determine if value is undefined..
58727             
58728             // basically get 'xxxx.yyyy' then do
58729             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58730             //    (function () { Roo.log("Property not found"); return ''; })() :
58731             //    ......
58732             
58733             var udef_ar = [];
58734             var lookfor = '';
58735             Roo.each(name.split('.'), function(st) {
58736                 lookfor += (lookfor.length ? '.': '') + st;
58737                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58738             });
58739             
58740             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58741             
58742             
58743             if(format && useF){
58744                 
58745                 args = args ? ',' + args : "";
58746                  
58747                 if(format.substr(0, 5) != "this."){
58748                     format = "fm." + format + '(';
58749                 }else{
58750                     format = 'this.call("'+ format.substr(5) + '", ';
58751                     args = ", values";
58752                 }
58753                 
58754                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58755             }
58756              
58757             if (args.length) {
58758                 // called with xxyx.yuu:(test,test)
58759                 // change to ()
58760                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58761             }
58762             // raw.. - :raw modifier..
58763             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58764             
58765         };
58766         var body;
58767         // branched to use + in gecko and [].join() in others
58768         if(Roo.isGecko){
58769             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58770                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58771                     "';};};";
58772         }else{
58773             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58774             body.push(tpl.body.replace(/(\r\n|\n)/g,
58775                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58776             body.push("'].join('');};};");
58777             body = body.join('');
58778         }
58779         
58780         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58781        
58782         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58783         eval(body);
58784         
58785         return this;
58786     },
58787
58788     applyTemplate : function(values){
58789         return this.master.compiled.call(this, values, {});
58790         //var s = this.subs;
58791     },
58792
58793     apply : function(){
58794         return this.applyTemplate.apply(this, arguments);
58795     }
58796
58797  });
58798
58799 Roo.XTemplate.from = function(el){
58800     el = Roo.getDom(el);
58801     return new Roo.XTemplate(el.value || el.innerHTML);
58802 };