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                 this.proxy.setStatus(status);
20084             }
20085
20086             if(this.afterDragOver){
20087                 /**
20088                  * An empty function by default, but provided so that you can perform a custom action
20089                  * while the dragged item is over the drop target by providing an implementation.
20090                  * @param {Roo.dd.DragDrop} target The drop target
20091                  * @param {Event} e The event object
20092                  * @param {String} id The id of the dragged element
20093                  * @method afterDragOver
20094                  */
20095                 this.afterDragOver(target, e, id);
20096             }
20097         }
20098     },
20099
20100     /**
20101      * An empty function by default, but provided so that you can perform a custom action
20102      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20103      * @param {Roo.dd.DragDrop} target The drop target
20104      * @param {Event} e The event object
20105      * @param {String} id The id of the dragged element
20106      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20107      */
20108     beforeDragOver : function(target, e, id){
20109         return true;
20110     },
20111
20112     // private
20113     onDragOut : function(e, id){
20114         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20115         if(this.beforeDragOut(target, e, id) !== false){
20116             if(target.isNotifyTarget){
20117                 target.notifyOut(this, e, this.dragData);
20118             }
20119             this.proxy.reset();
20120             if(this.afterDragOut){
20121                 /**
20122                  * An empty function by default, but provided so that you can perform a custom action
20123                  * after the dragged item is dragged out of the target without dropping.
20124                  * @param {Roo.dd.DragDrop} target The drop target
20125                  * @param {Event} e The event object
20126                  * @param {String} id The id of the dragged element
20127                  * @method afterDragOut
20128                  */
20129                 this.afterDragOut(target, e, id);
20130             }
20131         }
20132         this.cachedTarget = null;
20133     },
20134
20135     /**
20136      * An empty function by default, but provided so that you can perform a custom action before the dragged
20137      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20138      * @param {Roo.dd.DragDrop} target The drop target
20139      * @param {Event} e The event object
20140      * @param {String} id The id of the dragged element
20141      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20142      */
20143     beforeDragOut : function(target, e, id){
20144         return true;
20145     },
20146     
20147     // private
20148     onDragDrop : function(e, id){
20149         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20150         if(this.beforeDragDrop(target, e, id) !== false){
20151             if(target.isNotifyTarget){
20152                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20153                     this.onValidDrop(target, e, id);
20154                 }else{
20155                     this.onInvalidDrop(target, e, id);
20156                 }
20157             }else{
20158                 this.onValidDrop(target, e, id);
20159             }
20160             
20161             if(this.afterDragDrop){
20162                 /**
20163                  * An empty function by default, but provided so that you can perform a custom action
20164                  * after a valid drag drop has occurred by providing an implementation.
20165                  * @param {Roo.dd.DragDrop} target The drop target
20166                  * @param {Event} e The event object
20167                  * @param {String} id The id of the dropped element
20168                  * @method afterDragDrop
20169                  */
20170                 this.afterDragDrop(target, e, id);
20171             }
20172         }
20173         delete this.cachedTarget;
20174     },
20175
20176     /**
20177      * An empty function by default, but provided so that you can perform a custom action before the dragged
20178      * item is dropped onto the target and optionally cancel the onDragDrop.
20179      * @param {Roo.dd.DragDrop} target The drop target
20180      * @param {Event} e The event object
20181      * @param {String} id The id of the dragged element
20182      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20183      */
20184     beforeDragDrop : function(target, e, id){
20185         return true;
20186     },
20187
20188     // private
20189     onValidDrop : function(target, e, id){
20190         this.hideProxy();
20191         if(this.afterValidDrop){
20192             /**
20193              * An empty function by default, but provided so that you can perform a custom action
20194              * after a valid drop has occurred by providing an implementation.
20195              * @param {Object} target The target DD 
20196              * @param {Event} e The event object
20197              * @param {String} id The id of the dropped element
20198              * @method afterInvalidDrop
20199              */
20200             this.afterValidDrop(target, e, id);
20201         }
20202     },
20203
20204     // private
20205     getRepairXY : function(e, data){
20206         return this.el.getXY();  
20207     },
20208
20209     // private
20210     onInvalidDrop : function(target, e, id){
20211         this.beforeInvalidDrop(target, e, id);
20212         if(this.cachedTarget){
20213             if(this.cachedTarget.isNotifyTarget){
20214                 this.cachedTarget.notifyOut(this, e, this.dragData);
20215             }
20216             this.cacheTarget = null;
20217         }
20218         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20219
20220         if(this.afterInvalidDrop){
20221             /**
20222              * An empty function by default, but provided so that you can perform a custom action
20223              * after an invalid drop has occurred by providing an implementation.
20224              * @param {Event} e The event object
20225              * @param {String} id The id of the dropped element
20226              * @method afterInvalidDrop
20227              */
20228             this.afterInvalidDrop(e, id);
20229         }
20230     },
20231
20232     // private
20233     afterRepair : function(){
20234         if(Roo.enableFx){
20235             this.el.highlight(this.hlColor || "c3daf9");
20236         }
20237         this.dragging = false;
20238     },
20239
20240     /**
20241      * An empty function by default, but provided so that you can perform a custom action after an invalid
20242      * drop has occurred.
20243      * @param {Roo.dd.DragDrop} target The drop target
20244      * @param {Event} e The event object
20245      * @param {String} id The id of the dragged element
20246      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20247      */
20248     beforeInvalidDrop : function(target, e, id){
20249         return true;
20250     },
20251
20252     // private
20253     handleMouseDown : function(e){
20254         if(this.dragging) {
20255             return;
20256         }
20257         var data = this.getDragData(e);
20258         if(data && this.onBeforeDrag(data, e) !== false){
20259             this.dragData = data;
20260             this.proxy.stop();
20261             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20262         } 
20263     },
20264
20265     /**
20266      * An empty function by default, but provided so that you can perform a custom action before the initial
20267      * drag event begins and optionally cancel it.
20268      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20269      * @param {Event} e The event object
20270      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20271      */
20272     onBeforeDrag : function(data, e){
20273         return true;
20274     },
20275
20276     /**
20277      * An empty function by default, but provided so that you can perform a custom action once the initial
20278      * drag event has begun.  The drag cannot be canceled from this function.
20279      * @param {Number} x The x position of the click on the dragged object
20280      * @param {Number} y The y position of the click on the dragged object
20281      */
20282     onStartDrag : Roo.emptyFn,
20283
20284     // private - YUI override
20285     startDrag : function(x, y){
20286         this.proxy.reset();
20287         this.dragging = true;
20288         this.proxy.update("");
20289         this.onInitDrag(x, y);
20290         this.proxy.show();
20291     },
20292
20293     // private
20294     onInitDrag : function(x, y){
20295         var clone = this.el.dom.cloneNode(true);
20296         clone.id = Roo.id(); // prevent duplicate ids
20297         this.proxy.update(clone);
20298         this.onStartDrag(x, y);
20299         return true;
20300     },
20301
20302     /**
20303      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20304      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20305      */
20306     getProxy : function(){
20307         return this.proxy;  
20308     },
20309
20310     /**
20311      * Hides the drag source's {@link Roo.dd.StatusProxy}
20312      */
20313     hideProxy : function(){
20314         this.proxy.hide();  
20315         this.proxy.reset(true);
20316         this.dragging = false;
20317     },
20318
20319     // private
20320     triggerCacheRefresh : function(){
20321         Roo.dd.DDM.refreshCache(this.groups);
20322     },
20323
20324     // private - override to prevent hiding
20325     b4EndDrag: function(e) {
20326     },
20327
20328     // private - override to prevent moving
20329     endDrag : function(e){
20330         this.onEndDrag(this.dragData, e);
20331     },
20332
20333     // private
20334     onEndDrag : function(data, e){
20335     },
20336     
20337     // private - pin to cursor
20338     autoOffset : function(x, y) {
20339         this.setDelta(-12, -20);
20340     }    
20341 });/*
20342  * Based on:
20343  * Ext JS Library 1.1.1
20344  * Copyright(c) 2006-2007, Ext JS, LLC.
20345  *
20346  * Originally Released Under LGPL - original licence link has changed is not relivant.
20347  *
20348  * Fork - LGPL
20349  * <script type="text/javascript">
20350  */
20351
20352
20353 /**
20354  * @class Roo.dd.DropTarget
20355  * @extends Roo.dd.DDTarget
20356  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20357  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20358  * @constructor
20359  * @param {String/HTMLElement/Element} el The container element
20360  * @param {Object} config
20361  */
20362 Roo.dd.DropTarget = function(el, config){
20363     this.el = Roo.get(el);
20364     
20365     var listeners = false; ;
20366     if (config && config.listeners) {
20367         listeners= config.listeners;
20368         delete config.listeners;
20369     }
20370     Roo.apply(this, config);
20371     
20372     if(this.containerScroll){
20373         Roo.dd.ScrollManager.register(this.el);
20374     }
20375     this.addEvents( {
20376          /**
20377          * @scope Roo.dd.DropTarget
20378          */
20379          
20380          /**
20381          * @event enter
20382          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20383          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20384          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20385          * 
20386          * IMPORTANT : it should set this.overClass and this.dropAllowed
20387          * 
20388          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20389          * @param {Event} e The event
20390          * @param {Object} data An object containing arbitrary data supplied by the drag source
20391          */
20392         "enter" : true,
20393         
20394          /**
20395          * @event over
20396          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20397          * This method will be called on every mouse movement while the drag source is over the drop target.
20398          * This default implementation simply returns the dropAllowed config value.
20399          * 
20400          * IMPORTANT : it should set this.dropAllowed
20401          * 
20402          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20403          * @param {Event} e The event
20404          * @param {Object} data An object containing arbitrary data supplied by the drag source
20405          
20406          */
20407         "over" : true,
20408         /**
20409          * @event out
20410          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20411          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20412          * overClass (if any) from the drop element.
20413          * 
20414          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20415          * @param {Event} e The event
20416          * @param {Object} data An object containing arbitrary data supplied by the drag source
20417          */
20418          "out" : true,
20419          
20420         /**
20421          * @event drop
20422          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20423          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20424          * implementation that does something to process the drop event and returns true so that the drag source's
20425          * repair action does not run.
20426          * 
20427          * IMPORTANT : it should set this.success
20428          * 
20429          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20430          * @param {Event} e The event
20431          * @param {Object} data An object containing arbitrary data supplied by the drag source
20432         */
20433          "drop" : true
20434     });
20435             
20436      
20437     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20438         this.el.dom, 
20439         this.ddGroup || this.group,
20440         {
20441             isTarget: true,
20442             listeners : listeners || {} 
20443            
20444         
20445         }
20446     );
20447
20448 };
20449
20450 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20451     /**
20452      * @cfg {String} overClass
20453      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20454      */
20455      /**
20456      * @cfg {String} ddGroup
20457      * The drag drop group to handle drop events for
20458      */
20459      
20460     /**
20461      * @cfg {String} dropAllowed
20462      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20463      */
20464     dropAllowed : "x-dd-drop-ok",
20465     /**
20466      * @cfg {String} dropNotAllowed
20467      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20468      */
20469     dropNotAllowed : "x-dd-drop-nodrop",
20470     /**
20471      * @cfg {boolean} success
20472      * set this after drop listener.. 
20473      */
20474     success : false,
20475     /**
20476      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20477      * if the drop point is valid for over/enter..
20478      */
20479     valid : false,
20480     // private
20481     isTarget : true,
20482
20483     // private
20484     isNotifyTarget : true,
20485     
20486     /**
20487      * @hide
20488      */
20489     notifyEnter : function(dd, e, data)
20490     {
20491         this.valid = true;
20492         this.fireEvent('enter', dd, e, data);
20493         if(this.overClass){
20494             this.el.addClass(this.overClass);
20495         }
20496         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20497             this.valid ? this.dropAllowed : this.dropNotAllowed
20498         );
20499     },
20500
20501     /**
20502      * @hide
20503      */
20504     notifyOver : function(dd, e, data)
20505     {
20506         this.valid = true;
20507         this.fireEvent('over', dd, e, data);
20508         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20509             this.valid ? this.dropAllowed : this.dropNotAllowed
20510         );
20511     },
20512
20513     /**
20514      * @hide
20515      */
20516     notifyOut : function(dd, e, data)
20517     {
20518         this.fireEvent('out', dd, e, data);
20519         if(this.overClass){
20520             this.el.removeClass(this.overClass);
20521         }
20522     },
20523
20524     /**
20525      * @hide
20526      */
20527     notifyDrop : function(dd, e, data)
20528     {
20529         this.success = false;
20530         this.fireEvent('drop', dd, e, data);
20531         return this.success;
20532     }
20533 });/*
20534  * Based on:
20535  * Ext JS Library 1.1.1
20536  * Copyright(c) 2006-2007, Ext JS, LLC.
20537  *
20538  * Originally Released Under LGPL - original licence link has changed is not relivant.
20539  *
20540  * Fork - LGPL
20541  * <script type="text/javascript">
20542  */
20543
20544
20545 /**
20546  * @class Roo.dd.DragZone
20547  * @extends Roo.dd.DragSource
20548  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20549  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20550  * @constructor
20551  * @param {String/HTMLElement/Element} el The container element
20552  * @param {Object} config
20553  */
20554 Roo.dd.DragZone = function(el, config){
20555     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20556     if(this.containerScroll){
20557         Roo.dd.ScrollManager.register(this.el);
20558     }
20559 };
20560
20561 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20562     /**
20563      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20564      * for auto scrolling during drag operations.
20565      */
20566     /**
20567      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20568      * method after a failed drop (defaults to "c3daf9" - light blue)
20569      */
20570
20571     /**
20572      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20573      * for a valid target to drag based on the mouse down. Override this method
20574      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20575      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20576      * @param {EventObject} e The mouse down event
20577      * @return {Object} The dragData
20578      */
20579     getDragData : function(e){
20580         return Roo.dd.Registry.getHandleFromEvent(e);
20581     },
20582     
20583     /**
20584      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20585      * this.dragData.ddel
20586      * @param {Number} x The x position of the click on the dragged object
20587      * @param {Number} y The y position of the click on the dragged object
20588      * @return {Boolean} true to continue the drag, false to cancel
20589      */
20590     onInitDrag : function(x, y){
20591         this.proxy.update(this.dragData.ddel.cloneNode(true));
20592         this.onStartDrag(x, y);
20593         return true;
20594     },
20595     
20596     /**
20597      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20598      */
20599     afterRepair : function(){
20600         if(Roo.enableFx){
20601             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20602         }
20603         this.dragging = false;
20604     },
20605
20606     /**
20607      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20608      * the XY of this.dragData.ddel
20609      * @param {EventObject} e The mouse up event
20610      * @return {Array} The xy location (e.g. [100, 200])
20611      */
20612     getRepairXY : function(e){
20613         return Roo.Element.fly(this.dragData.ddel).getXY();  
20614     }
20615 });/*
20616  * Based on:
20617  * Ext JS Library 1.1.1
20618  * Copyright(c) 2006-2007, Ext JS, LLC.
20619  *
20620  * Originally Released Under LGPL - original licence link has changed is not relivant.
20621  *
20622  * Fork - LGPL
20623  * <script type="text/javascript">
20624  */
20625 /**
20626  * @class Roo.dd.DropZone
20627  * @extends Roo.dd.DropTarget
20628  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20629  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20630  * @constructor
20631  * @param {String/HTMLElement/Element} el The container element
20632  * @param {Object} config
20633  */
20634 Roo.dd.DropZone = function(el, config){
20635     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20636 };
20637
20638 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20639     /**
20640      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20641      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20642      * provide your own custom lookup.
20643      * @param {Event} e The event
20644      * @return {Object} data The custom data
20645      */
20646     getTargetFromEvent : function(e){
20647         return Roo.dd.Registry.getTargetFromEvent(e);
20648     },
20649
20650     /**
20651      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20652      * that it has registered.  This method has no default implementation and should be overridden to provide
20653      * node-specific processing if necessary.
20654      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20655      * {@link #getTargetFromEvent} for this node)
20656      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20657      * @param {Event} e The event
20658      * @param {Object} data An object containing arbitrary data supplied by the drag source
20659      */
20660     onNodeEnter : function(n, dd, e, data){
20661         
20662     },
20663
20664     /**
20665      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20666      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20667      * overridden to provide the proper feedback.
20668      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20669      * {@link #getTargetFromEvent} for this node)
20670      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20671      * @param {Event} e The event
20672      * @param {Object} data An object containing arbitrary data supplied by the drag source
20673      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20674      * underlying {@link Roo.dd.StatusProxy} can be updated
20675      */
20676     onNodeOver : function(n, dd, e, data){
20677         return this.dropAllowed;
20678     },
20679
20680     /**
20681      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20682      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20683      * node-specific processing if necessary.
20684      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20685      * {@link #getTargetFromEvent} for this node)
20686      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20687      * @param {Event} e The event
20688      * @param {Object} data An object containing arbitrary data supplied by the drag source
20689      */
20690     onNodeOut : function(n, dd, e, data){
20691         
20692     },
20693
20694     /**
20695      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20696      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20697      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20698      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20699      * {@link #getTargetFromEvent} for this node)
20700      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20701      * @param {Event} e The event
20702      * @param {Object} data An object containing arbitrary data supplied by the drag source
20703      * @return {Boolean} True if the drop was valid, else false
20704      */
20705     onNodeDrop : function(n, dd, e, data){
20706         return false;
20707     },
20708
20709     /**
20710      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20711      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20712      * it should be overridden to provide the proper feedback if necessary.
20713      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20714      * @param {Event} e The event
20715      * @param {Object} data An object containing arbitrary data supplied by the drag source
20716      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20717      * underlying {@link Roo.dd.StatusProxy} can be updated
20718      */
20719     onContainerOver : function(dd, e, data){
20720         return this.dropNotAllowed;
20721     },
20722
20723     /**
20724      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20725      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20726      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20727      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20728      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20729      * @param {Event} e The event
20730      * @param {Object} data An object containing arbitrary data supplied by the drag source
20731      * @return {Boolean} True if the drop was valid, else false
20732      */
20733     onContainerDrop : function(dd, e, data){
20734         return false;
20735     },
20736
20737     /**
20738      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20739      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20740      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20741      * you should override this method and provide a custom implementation.
20742      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20743      * @param {Event} e The event
20744      * @param {Object} data An object containing arbitrary data supplied by the drag source
20745      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20746      * underlying {@link Roo.dd.StatusProxy} can be updated
20747      */
20748     notifyEnter : function(dd, e, data){
20749         return this.dropNotAllowed;
20750     },
20751
20752     /**
20753      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20754      * This method will be called on every mouse movement while the drag source is over the drop zone.
20755      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20756      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20757      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20758      * registered node, it will call {@link #onContainerOver}.
20759      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20760      * @param {Event} e The event
20761      * @param {Object} data An object containing arbitrary data supplied by the drag source
20762      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20763      * underlying {@link Roo.dd.StatusProxy} can be updated
20764      */
20765     notifyOver : function(dd, e, data){
20766         var n = this.getTargetFromEvent(e);
20767         if(!n){ // not over valid drop target
20768             if(this.lastOverNode){
20769                 this.onNodeOut(this.lastOverNode, dd, e, data);
20770                 this.lastOverNode = null;
20771             }
20772             return this.onContainerOver(dd, e, data);
20773         }
20774         if(this.lastOverNode != n){
20775             if(this.lastOverNode){
20776                 this.onNodeOut(this.lastOverNode, dd, e, data);
20777             }
20778             this.onNodeEnter(n, dd, e, data);
20779             this.lastOverNode = n;
20780         }
20781         return this.onNodeOver(n, dd, e, data);
20782     },
20783
20784     /**
20785      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20786      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20787      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20788      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20789      * @param {Event} e The event
20790      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20791      */
20792     notifyOut : function(dd, e, data){
20793         if(this.lastOverNode){
20794             this.onNodeOut(this.lastOverNode, dd, e, data);
20795             this.lastOverNode = null;
20796         }
20797     },
20798
20799     /**
20800      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20801      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20802      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20803      * otherwise it will call {@link #onContainerDrop}.
20804      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20805      * @param {Event} e The event
20806      * @param {Object} data An object containing arbitrary data supplied by the drag source
20807      * @return {Boolean} True if the drop was valid, else false
20808      */
20809     notifyDrop : function(dd, e, data){
20810         if(this.lastOverNode){
20811             this.onNodeOut(this.lastOverNode, dd, e, data);
20812             this.lastOverNode = null;
20813         }
20814         var n = this.getTargetFromEvent(e);
20815         return n ?
20816             this.onNodeDrop(n, dd, e, data) :
20817             this.onContainerDrop(dd, e, data);
20818     },
20819
20820     // private
20821     triggerCacheRefresh : function(){
20822         Roo.dd.DDM.refreshCache(this.groups);
20823     }  
20824 });/*
20825  * Based on:
20826  * Ext JS Library 1.1.1
20827  * Copyright(c) 2006-2007, Ext JS, LLC.
20828  *
20829  * Originally Released Under LGPL - original licence link has changed is not relivant.
20830  *
20831  * Fork - LGPL
20832  * <script type="text/javascript">
20833  */
20834
20835
20836 /**
20837  * @class Roo.data.SortTypes
20838  * @singleton
20839  * Defines the default sorting (casting?) comparison functions used when sorting data.
20840  */
20841 Roo.data.SortTypes = {
20842     /**
20843      * Default sort that does nothing
20844      * @param {Mixed} s The value being converted
20845      * @return {Mixed} The comparison value
20846      */
20847     none : function(s){
20848         return s;
20849     },
20850     
20851     /**
20852      * The regular expression used to strip tags
20853      * @type {RegExp}
20854      * @property
20855      */
20856     stripTagsRE : /<\/?[^>]+>/gi,
20857     
20858     /**
20859      * Strips all HTML tags to sort on text only
20860      * @param {Mixed} s The value being converted
20861      * @return {String} The comparison value
20862      */
20863     asText : function(s){
20864         return String(s).replace(this.stripTagsRE, "");
20865     },
20866     
20867     /**
20868      * Strips all HTML tags to sort on text only - Case insensitive
20869      * @param {Mixed} s The value being converted
20870      * @return {String} The comparison value
20871      */
20872     asUCText : function(s){
20873         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20874     },
20875     
20876     /**
20877      * Case insensitive string
20878      * @param {Mixed} s The value being converted
20879      * @return {String} The comparison value
20880      */
20881     asUCString : function(s) {
20882         return String(s).toUpperCase();
20883     },
20884     
20885     /**
20886      * Date sorting
20887      * @param {Mixed} s The value being converted
20888      * @return {Number} The comparison value
20889      */
20890     asDate : function(s) {
20891         if(!s){
20892             return 0;
20893         }
20894         if(s instanceof Date){
20895             return s.getTime();
20896         }
20897         return Date.parse(String(s));
20898     },
20899     
20900     /**
20901      * Float sorting
20902      * @param {Mixed} s The value being converted
20903      * @return {Float} The comparison value
20904      */
20905     asFloat : function(s) {
20906         var val = parseFloat(String(s).replace(/,/g, ""));
20907         if(isNaN(val)) val = 0;
20908         return val;
20909     },
20910     
20911     /**
20912      * Integer sorting
20913      * @param {Mixed} s The value being converted
20914      * @return {Number} The comparison value
20915      */
20916     asInt : function(s) {
20917         var val = parseInt(String(s).replace(/,/g, ""));
20918         if(isNaN(val)) val = 0;
20919         return val;
20920     }
20921 };/*
20922  * Based on:
20923  * Ext JS Library 1.1.1
20924  * Copyright(c) 2006-2007, Ext JS, LLC.
20925  *
20926  * Originally Released Under LGPL - original licence link has changed is not relivant.
20927  *
20928  * Fork - LGPL
20929  * <script type="text/javascript">
20930  */
20931
20932 /**
20933 * @class Roo.data.Record
20934  * Instances of this class encapsulate both record <em>definition</em> information, and record
20935  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20936  * to access Records cached in an {@link Roo.data.Store} object.<br>
20937  * <p>
20938  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20939  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20940  * objects.<br>
20941  * <p>
20942  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20943  * @constructor
20944  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20945  * {@link #create}. The parameters are the same.
20946  * @param {Array} data An associative Array of data values keyed by the field name.
20947  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20948  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20949  * not specified an integer id is generated.
20950  */
20951 Roo.data.Record = function(data, id){
20952     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20953     this.data = data;
20954 };
20955
20956 /**
20957  * Generate a constructor for a specific record layout.
20958  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20959  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20960  * Each field definition object may contain the following properties: <ul>
20961  * <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,
20962  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20963  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20964  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20965  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20966  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20967  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20968  * this may be omitted.</p></li>
20969  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20970  * <ul><li>auto (Default, implies no conversion)</li>
20971  * <li>string</li>
20972  * <li>int</li>
20973  * <li>float</li>
20974  * <li>boolean</li>
20975  * <li>date</li></ul></p></li>
20976  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20977  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20978  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20979  * by the Reader into an object that will be stored in the Record. It is passed the
20980  * following parameters:<ul>
20981  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20982  * </ul></p></li>
20983  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20984  * </ul>
20985  * <br>usage:<br><pre><code>
20986 var TopicRecord = Roo.data.Record.create(
20987     {name: 'title', mapping: 'topic_title'},
20988     {name: 'author', mapping: 'username'},
20989     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20990     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20991     {name: 'lastPoster', mapping: 'user2'},
20992     {name: 'excerpt', mapping: 'post_text'}
20993 );
20994
20995 var myNewRecord = new TopicRecord({
20996     title: 'Do my job please',
20997     author: 'noobie',
20998     totalPosts: 1,
20999     lastPost: new Date(),
21000     lastPoster: 'Animal',
21001     excerpt: 'No way dude!'
21002 });
21003 myStore.add(myNewRecord);
21004 </code></pre>
21005  * @method create
21006  * @static
21007  */
21008 Roo.data.Record.create = function(o){
21009     var f = function(){
21010         f.superclass.constructor.apply(this, arguments);
21011     };
21012     Roo.extend(f, Roo.data.Record);
21013     var p = f.prototype;
21014     p.fields = new Roo.util.MixedCollection(false, function(field){
21015         return field.name;
21016     });
21017     for(var i = 0, len = o.length; i < len; i++){
21018         p.fields.add(new Roo.data.Field(o[i]));
21019     }
21020     f.getField = function(name){
21021         return p.fields.get(name);  
21022     };
21023     return f;
21024 };
21025
21026 Roo.data.Record.AUTO_ID = 1000;
21027 Roo.data.Record.EDIT = 'edit';
21028 Roo.data.Record.REJECT = 'reject';
21029 Roo.data.Record.COMMIT = 'commit';
21030
21031 Roo.data.Record.prototype = {
21032     /**
21033      * Readonly flag - true if this record has been modified.
21034      * @type Boolean
21035      */
21036     dirty : false,
21037     editing : false,
21038     error: null,
21039     modified: null,
21040
21041     // private
21042     join : function(store){
21043         this.store = store;
21044     },
21045
21046     /**
21047      * Set the named field to the specified value.
21048      * @param {String} name The name of the field to set.
21049      * @param {Object} value The value to set the field to.
21050      */
21051     set : function(name, value){
21052         if(this.data[name] == value){
21053             return;
21054         }
21055         this.dirty = true;
21056         if(!this.modified){
21057             this.modified = {};
21058         }
21059         if(typeof this.modified[name] == 'undefined'){
21060             this.modified[name] = this.data[name];
21061         }
21062         this.data[name] = value;
21063         if(!this.editing && this.store){
21064             this.store.afterEdit(this);
21065         }       
21066     },
21067
21068     /**
21069      * Get the value of the named field.
21070      * @param {String} name The name of the field to get the value of.
21071      * @return {Object} The value of the field.
21072      */
21073     get : function(name){
21074         return this.data[name]; 
21075     },
21076
21077     // private
21078     beginEdit : function(){
21079         this.editing = true;
21080         this.modified = {}; 
21081     },
21082
21083     // private
21084     cancelEdit : function(){
21085         this.editing = false;
21086         delete this.modified;
21087     },
21088
21089     // private
21090     endEdit : function(){
21091         this.editing = false;
21092         if(this.dirty && this.store){
21093             this.store.afterEdit(this);
21094         }
21095     },
21096
21097     /**
21098      * Usually called by the {@link Roo.data.Store} which owns the Record.
21099      * Rejects all changes made to the Record since either creation, or the last commit operation.
21100      * Modified fields are reverted to their original values.
21101      * <p>
21102      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21103      * of reject operations.
21104      */
21105     reject : function(){
21106         var m = this.modified;
21107         for(var n in m){
21108             if(typeof m[n] != "function"){
21109                 this.data[n] = m[n];
21110             }
21111         }
21112         this.dirty = false;
21113         delete this.modified;
21114         this.editing = false;
21115         if(this.store){
21116             this.store.afterReject(this);
21117         }
21118     },
21119
21120     /**
21121      * Usually called by the {@link Roo.data.Store} which owns the Record.
21122      * Commits all changes made to the Record since either creation, or the last commit operation.
21123      * <p>
21124      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21125      * of commit operations.
21126      */
21127     commit : function(){
21128         this.dirty = false;
21129         delete this.modified;
21130         this.editing = false;
21131         if(this.store){
21132             this.store.afterCommit(this);
21133         }
21134     },
21135
21136     // private
21137     hasError : function(){
21138         return this.error != null;
21139     },
21140
21141     // private
21142     clearError : function(){
21143         this.error = null;
21144     },
21145
21146     /**
21147      * Creates a copy of this record.
21148      * @param {String} id (optional) A new record id if you don't want to use this record's id
21149      * @return {Record}
21150      */
21151     copy : function(newId) {
21152         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21153     }
21154 };/*
21155  * Based on:
21156  * Ext JS Library 1.1.1
21157  * Copyright(c) 2006-2007, Ext JS, LLC.
21158  *
21159  * Originally Released Under LGPL - original licence link has changed is not relivant.
21160  *
21161  * Fork - LGPL
21162  * <script type="text/javascript">
21163  */
21164
21165
21166
21167 /**
21168  * @class Roo.data.Store
21169  * @extends Roo.util.Observable
21170  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21171  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21172  * <p>
21173  * 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
21174  * has no knowledge of the format of the data returned by the Proxy.<br>
21175  * <p>
21176  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21177  * instances from the data object. These records are cached and made available through accessor functions.
21178  * @constructor
21179  * Creates a new Store.
21180  * @param {Object} config A config object containing the objects needed for the Store to access data,
21181  * and read the data into Records.
21182  */
21183 Roo.data.Store = function(config){
21184     this.data = new Roo.util.MixedCollection(false);
21185     this.data.getKey = function(o){
21186         return o.id;
21187     };
21188     this.baseParams = {};
21189     // private
21190     this.paramNames = {
21191         "start" : "start",
21192         "limit" : "limit",
21193         "sort" : "sort",
21194         "dir" : "dir",
21195         "multisort" : "_multisort"
21196     };
21197
21198     if(config && config.data){
21199         this.inlineData = config.data;
21200         delete config.data;
21201     }
21202
21203     Roo.apply(this, config);
21204     
21205     if(this.reader){ // reader passed
21206         this.reader = Roo.factory(this.reader, Roo.data);
21207         this.reader.xmodule = this.xmodule || false;
21208         if(!this.recordType){
21209             this.recordType = this.reader.recordType;
21210         }
21211         if(this.reader.onMetaChange){
21212             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21213         }
21214     }
21215
21216     if(this.recordType){
21217         this.fields = this.recordType.prototype.fields;
21218     }
21219     this.modified = [];
21220
21221     this.addEvents({
21222         /**
21223          * @event datachanged
21224          * Fires when the data cache has changed, and a widget which is using this Store
21225          * as a Record cache should refresh its view.
21226          * @param {Store} this
21227          */
21228         datachanged : true,
21229         /**
21230          * @event metachange
21231          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21232          * @param {Store} this
21233          * @param {Object} meta The JSON metadata
21234          */
21235         metachange : true,
21236         /**
21237          * @event add
21238          * Fires when Records have been added to the Store
21239          * @param {Store} this
21240          * @param {Roo.data.Record[]} records The array of Records added
21241          * @param {Number} index The index at which the record(s) were added
21242          */
21243         add : true,
21244         /**
21245          * @event remove
21246          * Fires when a Record has been removed from the Store
21247          * @param {Store} this
21248          * @param {Roo.data.Record} record The Record that was removed
21249          * @param {Number} index The index at which the record was removed
21250          */
21251         remove : true,
21252         /**
21253          * @event update
21254          * Fires when a Record has been updated
21255          * @param {Store} this
21256          * @param {Roo.data.Record} record The Record that was updated
21257          * @param {String} operation The update operation being performed.  Value may be one of:
21258          * <pre><code>
21259  Roo.data.Record.EDIT
21260  Roo.data.Record.REJECT
21261  Roo.data.Record.COMMIT
21262          * </code></pre>
21263          */
21264         update : true,
21265         /**
21266          * @event clear
21267          * Fires when the data cache has been cleared.
21268          * @param {Store} this
21269          */
21270         clear : true,
21271         /**
21272          * @event beforeload
21273          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21274          * the load action will be canceled.
21275          * @param {Store} this
21276          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21277          */
21278         beforeload : true,
21279         /**
21280          * @event beforeloadadd
21281          * Fires after a new set of Records has been loaded.
21282          * @param {Store} this
21283          * @param {Roo.data.Record[]} records The Records that were loaded
21284          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21285          */
21286         beforeloadadd : true,
21287         /**
21288          * @event load
21289          * Fires after a new set of Records has been loaded, before they are added to the store.
21290          * @param {Store} this
21291          * @param {Roo.data.Record[]} records The Records that were loaded
21292          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21293          * @params {Object} return from reader
21294          */
21295         load : true,
21296         /**
21297          * @event loadexception
21298          * Fires if an exception occurs in the Proxy during loading.
21299          * Called with the signature of the Proxy's "loadexception" event.
21300          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21301          * 
21302          * @param {Proxy} 
21303          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21304          * @param {Object} load options 
21305          * @param {Object} jsonData from your request (normally this contains the Exception)
21306          */
21307         loadexception : true
21308     });
21309     
21310     if(this.proxy){
21311         this.proxy = Roo.factory(this.proxy, Roo.data);
21312         this.proxy.xmodule = this.xmodule || false;
21313         this.relayEvents(this.proxy,  ["loadexception"]);
21314     }
21315     this.sortToggle = {};
21316     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21317
21318     Roo.data.Store.superclass.constructor.call(this);
21319
21320     if(this.inlineData){
21321         this.loadData(this.inlineData);
21322         delete this.inlineData;
21323     }
21324 };
21325
21326 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21327      /**
21328     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21329     * without a remote query - used by combo/forms at present.
21330     */
21331     
21332     /**
21333     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21334     */
21335     /**
21336     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21337     */
21338     /**
21339     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21340     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21341     */
21342     /**
21343     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21344     * on any HTTP request
21345     */
21346     /**
21347     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21348     */
21349     /**
21350     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21351     */
21352     multiSort: false,
21353     /**
21354     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21355     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21356     */
21357     remoteSort : false,
21358
21359     /**
21360     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21361      * loaded or when a record is removed. (defaults to false).
21362     */
21363     pruneModifiedRecords : false,
21364
21365     // private
21366     lastOptions : null,
21367
21368     /**
21369      * Add Records to the Store and fires the add event.
21370      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21371      */
21372     add : function(records){
21373         records = [].concat(records);
21374         for(var i = 0, len = records.length; i < len; i++){
21375             records[i].join(this);
21376         }
21377         var index = this.data.length;
21378         this.data.addAll(records);
21379         this.fireEvent("add", this, records, index);
21380     },
21381
21382     /**
21383      * Remove a Record from the Store and fires the remove event.
21384      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21385      */
21386     remove : function(record){
21387         var index = this.data.indexOf(record);
21388         this.data.removeAt(index);
21389         if(this.pruneModifiedRecords){
21390             this.modified.remove(record);
21391         }
21392         this.fireEvent("remove", this, record, index);
21393     },
21394
21395     /**
21396      * Remove all Records from the Store and fires the clear event.
21397      */
21398     removeAll : function(){
21399         this.data.clear();
21400         if(this.pruneModifiedRecords){
21401             this.modified = [];
21402         }
21403         this.fireEvent("clear", this);
21404     },
21405
21406     /**
21407      * Inserts Records to the Store at the given index and fires the add event.
21408      * @param {Number} index The start index at which to insert the passed Records.
21409      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21410      */
21411     insert : function(index, records){
21412         records = [].concat(records);
21413         for(var i = 0, len = records.length; i < len; i++){
21414             this.data.insert(index, records[i]);
21415             records[i].join(this);
21416         }
21417         this.fireEvent("add", this, records, index);
21418     },
21419
21420     /**
21421      * Get the index within the cache of the passed Record.
21422      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21423      * @return {Number} The index of the passed Record. Returns -1 if not found.
21424      */
21425     indexOf : function(record){
21426         return this.data.indexOf(record);
21427     },
21428
21429     /**
21430      * Get the index within the cache of the Record with the passed id.
21431      * @param {String} id The id of the Record to find.
21432      * @return {Number} The index of the Record. Returns -1 if not found.
21433      */
21434     indexOfId : function(id){
21435         return this.data.indexOfKey(id);
21436     },
21437
21438     /**
21439      * Get the Record with the specified id.
21440      * @param {String} id The id of the Record to find.
21441      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21442      */
21443     getById : function(id){
21444         return this.data.key(id);
21445     },
21446
21447     /**
21448      * Get the Record at the specified index.
21449      * @param {Number} index The index of the Record to find.
21450      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21451      */
21452     getAt : function(index){
21453         return this.data.itemAt(index);
21454     },
21455
21456     /**
21457      * Returns a range of Records between specified indices.
21458      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21459      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21460      * @return {Roo.data.Record[]} An array of Records
21461      */
21462     getRange : function(start, end){
21463         return this.data.getRange(start, end);
21464     },
21465
21466     // private
21467     storeOptions : function(o){
21468         o = Roo.apply({}, o);
21469         delete o.callback;
21470         delete o.scope;
21471         this.lastOptions = o;
21472     },
21473
21474     /**
21475      * Loads the Record cache from the configured Proxy using the configured Reader.
21476      * <p>
21477      * If using remote paging, then the first load call must specify the <em>start</em>
21478      * and <em>limit</em> properties in the options.params property to establish the initial
21479      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21480      * <p>
21481      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21482      * and this call will return before the new data has been loaded. Perform any post-processing
21483      * in a callback function, or in a "load" event handler.</strong>
21484      * <p>
21485      * @param {Object} options An object containing properties which control loading options:<ul>
21486      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21487      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21488      * passed the following arguments:<ul>
21489      * <li>r : Roo.data.Record[]</li>
21490      * <li>options: Options object from the load call</li>
21491      * <li>success: Boolean success indicator</li></ul></li>
21492      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21493      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21494      * </ul>
21495      */
21496     load : function(options){
21497         options = options || {};
21498         if(this.fireEvent("beforeload", this, options) !== false){
21499             this.storeOptions(options);
21500             var p = Roo.apply(options.params || {}, this.baseParams);
21501             // if meta was not loaded from remote source.. try requesting it.
21502             if (!this.reader.metaFromRemote) {
21503                 p._requestMeta = 1;
21504             }
21505             if(this.sortInfo && this.remoteSort){
21506                 var pn = this.paramNames;
21507                 p[pn["sort"]] = this.sortInfo.field;
21508                 p[pn["dir"]] = this.sortInfo.direction;
21509             }
21510             if (this.multiSort) {
21511                 var pn = this.paramNames;
21512                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21513             }
21514             
21515             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21516         }
21517     },
21518
21519     /**
21520      * Reloads the Record cache from the configured Proxy using the configured Reader and
21521      * the options from the last load operation performed.
21522      * @param {Object} options (optional) An object containing properties which may override the options
21523      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21524      * the most recently used options are reused).
21525      */
21526     reload : function(options){
21527         this.load(Roo.applyIf(options||{}, this.lastOptions));
21528     },
21529
21530     // private
21531     // Called as a callback by the Reader during a load operation.
21532     loadRecords : function(o, options, success){
21533         if(!o || success === false){
21534             if(success !== false){
21535                 this.fireEvent("load", this, [], options, o);
21536             }
21537             if(options.callback){
21538                 options.callback.call(options.scope || this, [], options, false);
21539             }
21540             return;
21541         }
21542         // if data returned failure - throw an exception.
21543         if (o.success === false) {
21544             // show a message if no listener is registered.
21545             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21546                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21547             }
21548             // loadmask wil be hooked into this..
21549             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21550             return;
21551         }
21552         var r = o.records, t = o.totalRecords || r.length;
21553         
21554         this.fireEvent("beforeloadadd", this, r, options, o);
21555         
21556         if(!options || options.add !== true){
21557             if(this.pruneModifiedRecords){
21558                 this.modified = [];
21559             }
21560             for(var i = 0, len = r.length; i < len; i++){
21561                 r[i].join(this);
21562             }
21563             if(this.snapshot){
21564                 this.data = this.snapshot;
21565                 delete this.snapshot;
21566             }
21567             this.data.clear();
21568             this.data.addAll(r);
21569             this.totalLength = t;
21570             this.applySort();
21571             this.fireEvent("datachanged", this);
21572         }else{
21573             this.totalLength = Math.max(t, this.data.length+r.length);
21574             this.add(r);
21575         }
21576         this.fireEvent("load", this, r, options, o);
21577         if(options.callback){
21578             options.callback.call(options.scope || this, r, options, true);
21579         }
21580     },
21581
21582
21583     /**
21584      * Loads data from a passed data block. A Reader which understands the format of the data
21585      * must have been configured in the constructor.
21586      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21587      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21588      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21589      */
21590     loadData : function(o, append){
21591         var r = this.reader.readRecords(o);
21592         this.loadRecords(r, {add: append}, true);
21593     },
21594
21595     /**
21596      * Gets the number of cached records.
21597      * <p>
21598      * <em>If using paging, this may not be the total size of the dataset. If the data object
21599      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21600      * the data set size</em>
21601      */
21602     getCount : function(){
21603         return this.data.length || 0;
21604     },
21605
21606     /**
21607      * Gets the total number of records in the dataset as returned by the server.
21608      * <p>
21609      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21610      * the dataset size</em>
21611      */
21612     getTotalCount : function(){
21613         return this.totalLength || 0;
21614     },
21615
21616     /**
21617      * Returns the sort state of the Store as an object with two properties:
21618      * <pre><code>
21619  field {String} The name of the field by which the Records are sorted
21620  direction {String} The sort order, "ASC" or "DESC"
21621      * </code></pre>
21622      */
21623     getSortState : function(){
21624         return this.sortInfo;
21625     },
21626
21627     // private
21628     applySort : function(){
21629         if(this.sortInfo && !this.remoteSort){
21630             var s = this.sortInfo, f = s.field;
21631             var st = this.fields.get(f).sortType;
21632             var fn = function(r1, r2){
21633                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21634                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21635             };
21636             this.data.sort(s.direction, fn);
21637             if(this.snapshot && this.snapshot != this.data){
21638                 this.snapshot.sort(s.direction, fn);
21639             }
21640         }
21641     },
21642
21643     /**
21644      * Sets the default sort column and order to be used by the next load operation.
21645      * @param {String} fieldName The name of the field to sort by.
21646      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21647      */
21648     setDefaultSort : function(field, dir){
21649         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21650     },
21651
21652     /**
21653      * Sort the Records.
21654      * If remote sorting is used, the sort is performed on the server, and the cache is
21655      * reloaded. If local sorting is used, the cache is sorted internally.
21656      * @param {String} fieldName The name of the field to sort by.
21657      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21658      */
21659     sort : function(fieldName, dir){
21660         var f = this.fields.get(fieldName);
21661         if(!dir){
21662             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21663             
21664             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21665                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21666             }else{
21667                 dir = f.sortDir;
21668             }
21669         }
21670         this.sortToggle[f.name] = dir;
21671         this.sortInfo = {field: f.name, direction: dir};
21672         if(!this.remoteSort){
21673             this.applySort();
21674             this.fireEvent("datachanged", this);
21675         }else{
21676             this.load(this.lastOptions);
21677         }
21678     },
21679
21680     /**
21681      * Calls the specified function for each of the Records in the cache.
21682      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21683      * Returning <em>false</em> aborts and exits the iteration.
21684      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21685      */
21686     each : function(fn, scope){
21687         this.data.each(fn, scope);
21688     },
21689
21690     /**
21691      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21692      * (e.g., during paging).
21693      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21694      */
21695     getModifiedRecords : function(){
21696         return this.modified;
21697     },
21698
21699     // private
21700     createFilterFn : function(property, value, anyMatch){
21701         if(!value.exec){ // not a regex
21702             value = String(value);
21703             if(value.length == 0){
21704                 return false;
21705             }
21706             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21707         }
21708         return function(r){
21709             return value.test(r.data[property]);
21710         };
21711     },
21712
21713     /**
21714      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21715      * @param {String} property A field on your records
21716      * @param {Number} start The record index to start at (defaults to 0)
21717      * @param {Number} end The last record index to include (defaults to length - 1)
21718      * @return {Number} The sum
21719      */
21720     sum : function(property, start, end){
21721         var rs = this.data.items, v = 0;
21722         start = start || 0;
21723         end = (end || end === 0) ? end : rs.length-1;
21724
21725         for(var i = start; i <= end; i++){
21726             v += (rs[i].data[property] || 0);
21727         }
21728         return v;
21729     },
21730
21731     /**
21732      * Filter the records by a specified property.
21733      * @param {String} field A field on your records
21734      * @param {String/RegExp} value Either a string that the field
21735      * should start with or a RegExp to test against the field
21736      * @param {Boolean} anyMatch True to match any part not just the beginning
21737      */
21738     filter : function(property, value, anyMatch){
21739         var fn = this.createFilterFn(property, value, anyMatch);
21740         return fn ? this.filterBy(fn) : this.clearFilter();
21741     },
21742
21743     /**
21744      * Filter by a function. The specified function will be called with each
21745      * record in this data source. If the function returns true the record is included,
21746      * otherwise it is filtered.
21747      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21748      * @param {Object} scope (optional) The scope of the function (defaults to this)
21749      */
21750     filterBy : function(fn, scope){
21751         this.snapshot = this.snapshot || this.data;
21752         this.data = this.queryBy(fn, scope||this);
21753         this.fireEvent("datachanged", this);
21754     },
21755
21756     /**
21757      * Query the records by a specified property.
21758      * @param {String} field A field on your records
21759      * @param {String/RegExp} value Either a string that the field
21760      * should start with or a RegExp to test against the field
21761      * @param {Boolean} anyMatch True to match any part not just the beginning
21762      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21763      */
21764     query : function(property, value, anyMatch){
21765         var fn = this.createFilterFn(property, value, anyMatch);
21766         return fn ? this.queryBy(fn) : this.data.clone();
21767     },
21768
21769     /**
21770      * Query by a function. The specified function will be called with each
21771      * record in this data source. If the function returns true the record is included
21772      * in the results.
21773      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21774      * @param {Object} scope (optional) The scope of the function (defaults to this)
21775       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21776      **/
21777     queryBy : function(fn, scope){
21778         var data = this.snapshot || this.data;
21779         return data.filterBy(fn, scope||this);
21780     },
21781
21782     /**
21783      * Collects unique values for a particular dataIndex from this store.
21784      * @param {String} dataIndex The property to collect
21785      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21786      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21787      * @return {Array} An array of the unique values
21788      **/
21789     collect : function(dataIndex, allowNull, bypassFilter){
21790         var d = (bypassFilter === true && this.snapshot) ?
21791                 this.snapshot.items : this.data.items;
21792         var v, sv, r = [], l = {};
21793         for(var i = 0, len = d.length; i < len; i++){
21794             v = d[i].data[dataIndex];
21795             sv = String(v);
21796             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21797                 l[sv] = true;
21798                 r[r.length] = v;
21799             }
21800         }
21801         return r;
21802     },
21803
21804     /**
21805      * Revert to a view of the Record cache with no filtering applied.
21806      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21807      */
21808     clearFilter : function(suppressEvent){
21809         if(this.snapshot && this.snapshot != this.data){
21810             this.data = this.snapshot;
21811             delete this.snapshot;
21812             if(suppressEvent !== true){
21813                 this.fireEvent("datachanged", this);
21814             }
21815         }
21816     },
21817
21818     // private
21819     afterEdit : function(record){
21820         if(this.modified.indexOf(record) == -1){
21821             this.modified.push(record);
21822         }
21823         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21824     },
21825     
21826     // private
21827     afterReject : function(record){
21828         this.modified.remove(record);
21829         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21830     },
21831
21832     // private
21833     afterCommit : function(record){
21834         this.modified.remove(record);
21835         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21836     },
21837
21838     /**
21839      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21840      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21841      */
21842     commitChanges : function(){
21843         var m = this.modified.slice(0);
21844         this.modified = [];
21845         for(var i = 0, len = m.length; i < len; i++){
21846             m[i].commit();
21847         }
21848     },
21849
21850     /**
21851      * Cancel outstanding changes on all changed records.
21852      */
21853     rejectChanges : function(){
21854         var m = this.modified.slice(0);
21855         this.modified = [];
21856         for(var i = 0, len = m.length; i < len; i++){
21857             m[i].reject();
21858         }
21859     },
21860
21861     onMetaChange : function(meta, rtype, o){
21862         this.recordType = rtype;
21863         this.fields = rtype.prototype.fields;
21864         delete this.snapshot;
21865         this.sortInfo = meta.sortInfo || this.sortInfo;
21866         this.modified = [];
21867         this.fireEvent('metachange', this, this.reader.meta);
21868     },
21869     
21870     moveIndex : function(data, type)
21871     {
21872         var index = this.indexOf(data);
21873         
21874         var newIndex = index + type;
21875         
21876         this.remove(data);
21877         
21878         this.insert(newIndex, data);
21879         
21880     }
21881 });/*
21882  * Based on:
21883  * Ext JS Library 1.1.1
21884  * Copyright(c) 2006-2007, Ext JS, LLC.
21885  *
21886  * Originally Released Under LGPL - original licence link has changed is not relivant.
21887  *
21888  * Fork - LGPL
21889  * <script type="text/javascript">
21890  */
21891
21892 /**
21893  * @class Roo.data.SimpleStore
21894  * @extends Roo.data.Store
21895  * Small helper class to make creating Stores from Array data easier.
21896  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21897  * @cfg {Array} fields An array of field definition objects, or field name strings.
21898  * @cfg {Array} data The multi-dimensional array of data
21899  * @constructor
21900  * @param {Object} config
21901  */
21902 Roo.data.SimpleStore = function(config){
21903     Roo.data.SimpleStore.superclass.constructor.call(this, {
21904         isLocal : true,
21905         reader: new Roo.data.ArrayReader({
21906                 id: config.id
21907             },
21908             Roo.data.Record.create(config.fields)
21909         ),
21910         proxy : new Roo.data.MemoryProxy(config.data)
21911     });
21912     this.load();
21913 };
21914 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21915  * Based on:
21916  * Ext JS Library 1.1.1
21917  * Copyright(c) 2006-2007, Ext JS, LLC.
21918  *
21919  * Originally Released Under LGPL - original licence link has changed is not relivant.
21920  *
21921  * Fork - LGPL
21922  * <script type="text/javascript">
21923  */
21924
21925 /**
21926 /**
21927  * @extends Roo.data.Store
21928  * @class Roo.data.JsonStore
21929  * Small helper class to make creating Stores for JSON data easier. <br/>
21930 <pre><code>
21931 var store = new Roo.data.JsonStore({
21932     url: 'get-images.php',
21933     root: 'images',
21934     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21935 });
21936 </code></pre>
21937  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21938  * JsonReader and HttpProxy (unless inline data is provided).</b>
21939  * @cfg {Array} fields An array of field definition objects, or field name strings.
21940  * @constructor
21941  * @param {Object} config
21942  */
21943 Roo.data.JsonStore = function(c){
21944     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21945         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21946         reader: new Roo.data.JsonReader(c, c.fields)
21947     }));
21948 };
21949 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21950  * Based on:
21951  * Ext JS Library 1.1.1
21952  * Copyright(c) 2006-2007, Ext JS, LLC.
21953  *
21954  * Originally Released Under LGPL - original licence link has changed is not relivant.
21955  *
21956  * Fork - LGPL
21957  * <script type="text/javascript">
21958  */
21959
21960  
21961 Roo.data.Field = function(config){
21962     if(typeof config == "string"){
21963         config = {name: config};
21964     }
21965     Roo.apply(this, config);
21966     
21967     if(!this.type){
21968         this.type = "auto";
21969     }
21970     
21971     var st = Roo.data.SortTypes;
21972     // named sortTypes are supported, here we look them up
21973     if(typeof this.sortType == "string"){
21974         this.sortType = st[this.sortType];
21975     }
21976     
21977     // set default sortType for strings and dates
21978     if(!this.sortType){
21979         switch(this.type){
21980             case "string":
21981                 this.sortType = st.asUCString;
21982                 break;
21983             case "date":
21984                 this.sortType = st.asDate;
21985                 break;
21986             default:
21987                 this.sortType = st.none;
21988         }
21989     }
21990
21991     // define once
21992     var stripRe = /[\$,%]/g;
21993
21994     // prebuilt conversion function for this field, instead of
21995     // switching every time we're reading a value
21996     if(!this.convert){
21997         var cv, dateFormat = this.dateFormat;
21998         switch(this.type){
21999             case "":
22000             case "auto":
22001             case undefined:
22002                 cv = function(v){ return v; };
22003                 break;
22004             case "string":
22005                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22006                 break;
22007             case "int":
22008                 cv = function(v){
22009                     return v !== undefined && v !== null && v !== '' ?
22010                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22011                     };
22012                 break;
22013             case "float":
22014                 cv = function(v){
22015                     return v !== undefined && v !== null && v !== '' ?
22016                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22017                     };
22018                 break;
22019             case "bool":
22020             case "boolean":
22021                 cv = function(v){ return v === true || v === "true" || v == 1; };
22022                 break;
22023             case "date":
22024                 cv = function(v){
22025                     if(!v){
22026                         return '';
22027                     }
22028                     if(v instanceof Date){
22029                         return v;
22030                     }
22031                     if(dateFormat){
22032                         if(dateFormat == "timestamp"){
22033                             return new Date(v*1000);
22034                         }
22035                         return Date.parseDate(v, dateFormat);
22036                     }
22037                     var parsed = Date.parse(v);
22038                     return parsed ? new Date(parsed) : null;
22039                 };
22040              break;
22041             
22042         }
22043         this.convert = cv;
22044     }
22045 };
22046
22047 Roo.data.Field.prototype = {
22048     dateFormat: null,
22049     defaultValue: "",
22050     mapping: null,
22051     sortType : null,
22052     sortDir : "ASC"
22053 };/*
22054  * Based on:
22055  * Ext JS Library 1.1.1
22056  * Copyright(c) 2006-2007, Ext JS, LLC.
22057  *
22058  * Originally Released Under LGPL - original licence link has changed is not relivant.
22059  *
22060  * Fork - LGPL
22061  * <script type="text/javascript">
22062  */
22063  
22064 // Base class for reading structured data from a data source.  This class is intended to be
22065 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22066
22067 /**
22068  * @class Roo.data.DataReader
22069  * Base class for reading structured data from a data source.  This class is intended to be
22070  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22071  */
22072
22073 Roo.data.DataReader = function(meta, recordType){
22074     
22075     this.meta = meta;
22076     
22077     this.recordType = recordType instanceof Array ? 
22078         Roo.data.Record.create(recordType) : recordType;
22079 };
22080
22081 Roo.data.DataReader.prototype = {
22082      /**
22083      * Create an empty record
22084      * @param {Object} data (optional) - overlay some values
22085      * @return {Roo.data.Record} record created.
22086      */
22087     newRow :  function(d) {
22088         var da =  {};
22089         this.recordType.prototype.fields.each(function(c) {
22090             switch( c.type) {
22091                 case 'int' : da[c.name] = 0; break;
22092                 case 'date' : da[c.name] = new Date(); break;
22093                 case 'float' : da[c.name] = 0.0; break;
22094                 case 'boolean' : da[c.name] = false; break;
22095                 default : da[c.name] = ""; break;
22096             }
22097             
22098         });
22099         return new this.recordType(Roo.apply(da, d));
22100     }
22101     
22102 };/*
22103  * Based on:
22104  * Ext JS Library 1.1.1
22105  * Copyright(c) 2006-2007, Ext JS, LLC.
22106  *
22107  * Originally Released Under LGPL - original licence link has changed is not relivant.
22108  *
22109  * Fork - LGPL
22110  * <script type="text/javascript">
22111  */
22112
22113 /**
22114  * @class Roo.data.DataProxy
22115  * @extends Roo.data.Observable
22116  * This class is an abstract base class for implementations which provide retrieval of
22117  * unformatted data objects.<br>
22118  * <p>
22119  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22120  * (of the appropriate type which knows how to parse the data object) to provide a block of
22121  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22122  * <p>
22123  * Custom implementations must implement the load method as described in
22124  * {@link Roo.data.HttpProxy#load}.
22125  */
22126 Roo.data.DataProxy = function(){
22127     this.addEvents({
22128         /**
22129          * @event beforeload
22130          * Fires before a network request is made to retrieve a data object.
22131          * @param {Object} This DataProxy object.
22132          * @param {Object} params The params parameter to the load function.
22133          */
22134         beforeload : true,
22135         /**
22136          * @event load
22137          * Fires before the load method's callback is called.
22138          * @param {Object} This DataProxy object.
22139          * @param {Object} o The data object.
22140          * @param {Object} arg The callback argument object passed to the load function.
22141          */
22142         load : true,
22143         /**
22144          * @event loadexception
22145          * Fires if an Exception occurs during data retrieval.
22146          * @param {Object} This DataProxy object.
22147          * @param {Object} o The data object.
22148          * @param {Object} arg The callback argument object passed to the load function.
22149          * @param {Object} e The Exception.
22150          */
22151         loadexception : true
22152     });
22153     Roo.data.DataProxy.superclass.constructor.call(this);
22154 };
22155
22156 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22157
22158     /**
22159      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22160      */
22161 /*
22162  * Based on:
22163  * Ext JS Library 1.1.1
22164  * Copyright(c) 2006-2007, Ext JS, LLC.
22165  *
22166  * Originally Released Under LGPL - original licence link has changed is not relivant.
22167  *
22168  * Fork - LGPL
22169  * <script type="text/javascript">
22170  */
22171 /**
22172  * @class Roo.data.MemoryProxy
22173  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22174  * to the Reader when its load method is called.
22175  * @constructor
22176  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22177  */
22178 Roo.data.MemoryProxy = function(data){
22179     if (data.data) {
22180         data = data.data;
22181     }
22182     Roo.data.MemoryProxy.superclass.constructor.call(this);
22183     this.data = data;
22184 };
22185
22186 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22187     /**
22188      * Load data from the requested source (in this case an in-memory
22189      * data object passed to the constructor), read the data object into
22190      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22191      * process that block using the passed callback.
22192      * @param {Object} params This parameter is not used by the MemoryProxy class.
22193      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22194      * object into a block of Roo.data.Records.
22195      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22196      * The function must be passed <ul>
22197      * <li>The Record block object</li>
22198      * <li>The "arg" argument from the load function</li>
22199      * <li>A boolean success indicator</li>
22200      * </ul>
22201      * @param {Object} scope The scope in which to call the callback
22202      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22203      */
22204     load : function(params, reader, callback, scope, arg){
22205         params = params || {};
22206         var result;
22207         try {
22208             result = reader.readRecords(this.data);
22209         }catch(e){
22210             this.fireEvent("loadexception", this, arg, null, e);
22211             callback.call(scope, null, arg, false);
22212             return;
22213         }
22214         callback.call(scope, result, arg, true);
22215     },
22216     
22217     // private
22218     update : function(params, records){
22219         
22220     }
22221 });/*
22222  * Based on:
22223  * Ext JS Library 1.1.1
22224  * Copyright(c) 2006-2007, Ext JS, LLC.
22225  *
22226  * Originally Released Under LGPL - original licence link has changed is not relivant.
22227  *
22228  * Fork - LGPL
22229  * <script type="text/javascript">
22230  */
22231 /**
22232  * @class Roo.data.HttpProxy
22233  * @extends Roo.data.DataProxy
22234  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22235  * configured to reference a certain URL.<br><br>
22236  * <p>
22237  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22238  * from which the running page was served.<br><br>
22239  * <p>
22240  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22241  * <p>
22242  * Be aware that to enable the browser to parse an XML document, the server must set
22243  * the Content-Type header in the HTTP response to "text/xml".
22244  * @constructor
22245  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22246  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22247  * will be used to make the request.
22248  */
22249 Roo.data.HttpProxy = function(conn){
22250     Roo.data.HttpProxy.superclass.constructor.call(this);
22251     // is conn a conn config or a real conn?
22252     this.conn = conn;
22253     this.useAjax = !conn || !conn.events;
22254   
22255 };
22256
22257 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22258     // thse are take from connection...
22259     
22260     /**
22261      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22262      */
22263     /**
22264      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22265      * extra parameters to each request made by this object. (defaults to undefined)
22266      */
22267     /**
22268      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22269      *  to each request made by this object. (defaults to undefined)
22270      */
22271     /**
22272      * @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)
22273      */
22274     /**
22275      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22276      */
22277      /**
22278      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22279      * @type Boolean
22280      */
22281   
22282
22283     /**
22284      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22285      * @type Boolean
22286      */
22287     /**
22288      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22289      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22290      * a finer-grained basis than the DataProxy events.
22291      */
22292     getConnection : function(){
22293         return this.useAjax ? Roo.Ajax : this.conn;
22294     },
22295
22296     /**
22297      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22298      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22299      * process that block using the passed callback.
22300      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22301      * for the request to the remote server.
22302      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22303      * object into a block of Roo.data.Records.
22304      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22305      * The function must be passed <ul>
22306      * <li>The Record block object</li>
22307      * <li>The "arg" argument from the load function</li>
22308      * <li>A boolean success indicator</li>
22309      * </ul>
22310      * @param {Object} scope The scope in which to call the callback
22311      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22312      */
22313     load : function(params, reader, callback, scope, arg){
22314         if(this.fireEvent("beforeload", this, params) !== false){
22315             var  o = {
22316                 params : params || {},
22317                 request: {
22318                     callback : callback,
22319                     scope : scope,
22320                     arg : arg
22321                 },
22322                 reader: reader,
22323                 callback : this.loadResponse,
22324                 scope: this
22325             };
22326             if(this.useAjax){
22327                 Roo.applyIf(o, this.conn);
22328                 if(this.activeRequest){
22329                     Roo.Ajax.abort(this.activeRequest);
22330                 }
22331                 this.activeRequest = Roo.Ajax.request(o);
22332             }else{
22333                 this.conn.request(o);
22334             }
22335         }else{
22336             callback.call(scope||this, null, arg, false);
22337         }
22338     },
22339
22340     // private
22341     loadResponse : function(o, success, response){
22342         delete this.activeRequest;
22343         if(!success){
22344             this.fireEvent("loadexception", this, o, response);
22345             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22346             return;
22347         }
22348         var result;
22349         try {
22350             result = o.reader.read(response);
22351         }catch(e){
22352             this.fireEvent("loadexception", this, o, response, e);
22353             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22354             return;
22355         }
22356         
22357         this.fireEvent("load", this, o, o.request.arg);
22358         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22359     },
22360
22361     // private
22362     update : function(dataSet){
22363
22364     },
22365
22366     // private
22367     updateResponse : function(dataSet){
22368
22369     }
22370 });/*
22371  * Based on:
22372  * Ext JS Library 1.1.1
22373  * Copyright(c) 2006-2007, Ext JS, LLC.
22374  *
22375  * Originally Released Under LGPL - original licence link has changed is not relivant.
22376  *
22377  * Fork - LGPL
22378  * <script type="text/javascript">
22379  */
22380
22381 /**
22382  * @class Roo.data.ScriptTagProxy
22383  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22384  * other than the originating domain of the running page.<br><br>
22385  * <p>
22386  * <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
22387  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22388  * <p>
22389  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22390  * source code that is used as the source inside a &lt;script> tag.<br><br>
22391  * <p>
22392  * In order for the browser to process the returned data, the server must wrap the data object
22393  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22394  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22395  * depending on whether the callback name was passed:
22396  * <p>
22397  * <pre><code>
22398 boolean scriptTag = false;
22399 String cb = request.getParameter("callback");
22400 if (cb != null) {
22401     scriptTag = true;
22402     response.setContentType("text/javascript");
22403 } else {
22404     response.setContentType("application/x-json");
22405 }
22406 Writer out = response.getWriter();
22407 if (scriptTag) {
22408     out.write(cb + "(");
22409 }
22410 out.print(dataBlock.toJsonString());
22411 if (scriptTag) {
22412     out.write(");");
22413 }
22414 </pre></code>
22415  *
22416  * @constructor
22417  * @param {Object} config A configuration object.
22418  */
22419 Roo.data.ScriptTagProxy = function(config){
22420     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22421     Roo.apply(this, config);
22422     this.head = document.getElementsByTagName("head")[0];
22423 };
22424
22425 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22426
22427 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22428     /**
22429      * @cfg {String} url The URL from which to request the data object.
22430      */
22431     /**
22432      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22433      */
22434     timeout : 30000,
22435     /**
22436      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22437      * the server the name of the callback function set up by the load call to process the returned data object.
22438      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22439      * javascript output which calls this named function passing the data object as its only parameter.
22440      */
22441     callbackParam : "callback",
22442     /**
22443      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22444      * name to the request.
22445      */
22446     nocache : true,
22447
22448     /**
22449      * Load data from the configured URL, read the data object into
22450      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22451      * process that block using the passed callback.
22452      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22453      * for the request to the remote server.
22454      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22455      * object into a block of Roo.data.Records.
22456      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22457      * The function must be passed <ul>
22458      * <li>The Record block object</li>
22459      * <li>The "arg" argument from the load function</li>
22460      * <li>A boolean success indicator</li>
22461      * </ul>
22462      * @param {Object} scope The scope in which to call the callback
22463      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22464      */
22465     load : function(params, reader, callback, scope, arg){
22466         if(this.fireEvent("beforeload", this, params) !== false){
22467
22468             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22469
22470             var url = this.url;
22471             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22472             if(this.nocache){
22473                 url += "&_dc=" + (new Date().getTime());
22474             }
22475             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22476             var trans = {
22477                 id : transId,
22478                 cb : "stcCallback"+transId,
22479                 scriptId : "stcScript"+transId,
22480                 params : params,
22481                 arg : arg,
22482                 url : url,
22483                 callback : callback,
22484                 scope : scope,
22485                 reader : reader
22486             };
22487             var conn = this;
22488
22489             window[trans.cb] = function(o){
22490                 conn.handleResponse(o, trans);
22491             };
22492
22493             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22494
22495             if(this.autoAbort !== false){
22496                 this.abort();
22497             }
22498
22499             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22500
22501             var script = document.createElement("script");
22502             script.setAttribute("src", url);
22503             script.setAttribute("type", "text/javascript");
22504             script.setAttribute("id", trans.scriptId);
22505             this.head.appendChild(script);
22506
22507             this.trans = trans;
22508         }else{
22509             callback.call(scope||this, null, arg, false);
22510         }
22511     },
22512
22513     // private
22514     isLoading : function(){
22515         return this.trans ? true : false;
22516     },
22517
22518     /**
22519      * Abort the current server request.
22520      */
22521     abort : function(){
22522         if(this.isLoading()){
22523             this.destroyTrans(this.trans);
22524         }
22525     },
22526
22527     // private
22528     destroyTrans : function(trans, isLoaded){
22529         this.head.removeChild(document.getElementById(trans.scriptId));
22530         clearTimeout(trans.timeoutId);
22531         if(isLoaded){
22532             window[trans.cb] = undefined;
22533             try{
22534                 delete window[trans.cb];
22535             }catch(e){}
22536         }else{
22537             // if hasn't been loaded, wait for load to remove it to prevent script error
22538             window[trans.cb] = function(){
22539                 window[trans.cb] = undefined;
22540                 try{
22541                     delete window[trans.cb];
22542                 }catch(e){}
22543             };
22544         }
22545     },
22546
22547     // private
22548     handleResponse : function(o, trans){
22549         this.trans = false;
22550         this.destroyTrans(trans, true);
22551         var result;
22552         try {
22553             result = trans.reader.readRecords(o);
22554         }catch(e){
22555             this.fireEvent("loadexception", this, o, trans.arg, e);
22556             trans.callback.call(trans.scope||window, null, trans.arg, false);
22557             return;
22558         }
22559         this.fireEvent("load", this, o, trans.arg);
22560         trans.callback.call(trans.scope||window, result, trans.arg, true);
22561     },
22562
22563     // private
22564     handleFailure : function(trans){
22565         this.trans = false;
22566         this.destroyTrans(trans, false);
22567         this.fireEvent("loadexception", this, null, trans.arg);
22568         trans.callback.call(trans.scope||window, null, trans.arg, false);
22569     }
22570 });/*
22571  * Based on:
22572  * Ext JS Library 1.1.1
22573  * Copyright(c) 2006-2007, Ext JS, LLC.
22574  *
22575  * Originally Released Under LGPL - original licence link has changed is not relivant.
22576  *
22577  * Fork - LGPL
22578  * <script type="text/javascript">
22579  */
22580
22581 /**
22582  * @class Roo.data.JsonReader
22583  * @extends Roo.data.DataReader
22584  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22585  * based on mappings in a provided Roo.data.Record constructor.
22586  * 
22587  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22588  * in the reply previously. 
22589  * 
22590  * <p>
22591  * Example code:
22592  * <pre><code>
22593 var RecordDef = Roo.data.Record.create([
22594     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22595     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22596 ]);
22597 var myReader = new Roo.data.JsonReader({
22598     totalProperty: "results",    // The property which contains the total dataset size (optional)
22599     root: "rows",                // The property which contains an Array of row objects
22600     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22601 }, RecordDef);
22602 </code></pre>
22603  * <p>
22604  * This would consume a JSON file like this:
22605  * <pre><code>
22606 { 'results': 2, 'rows': [
22607     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22608     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22609 }
22610 </code></pre>
22611  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22612  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22613  * paged from the remote server.
22614  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22615  * @cfg {String} root name of the property which contains the Array of row objects.
22616  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22617  * @cfg {Array} fields Array of field definition objects
22618  * @constructor
22619  * Create a new JsonReader
22620  * @param {Object} meta Metadata configuration options
22621  * @param {Object} recordType Either an Array of field definition objects,
22622  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22623  */
22624 Roo.data.JsonReader = function(meta, recordType){
22625     
22626     meta = meta || {};
22627     // set some defaults:
22628     Roo.applyIf(meta, {
22629         totalProperty: 'total',
22630         successProperty : 'success',
22631         root : 'data',
22632         id : 'id'
22633     });
22634     
22635     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22636 };
22637 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22638     
22639     /**
22640      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22641      * Used by Store query builder to append _requestMeta to params.
22642      * 
22643      */
22644     metaFromRemote : false,
22645     /**
22646      * This method is only used by a DataProxy which has retrieved data from a remote server.
22647      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22648      * @return {Object} data A data block which is used by an Roo.data.Store object as
22649      * a cache of Roo.data.Records.
22650      */
22651     read : function(response){
22652         var json = response.responseText;
22653        
22654         var o = /* eval:var:o */ eval("("+json+")");
22655         if(!o) {
22656             throw {message: "JsonReader.read: Json object not found"};
22657         }
22658         
22659         if(o.metaData){
22660             
22661             delete this.ef;
22662             this.metaFromRemote = true;
22663             this.meta = o.metaData;
22664             this.recordType = Roo.data.Record.create(o.metaData.fields);
22665             this.onMetaChange(this.meta, this.recordType, o);
22666         }
22667         return this.readRecords(o);
22668     },
22669
22670     // private function a store will implement
22671     onMetaChange : function(meta, recordType, o){
22672
22673     },
22674
22675     /**
22676          * @ignore
22677          */
22678     simpleAccess: function(obj, subsc) {
22679         return obj[subsc];
22680     },
22681
22682         /**
22683          * @ignore
22684          */
22685     getJsonAccessor: function(){
22686         var re = /[\[\.]/;
22687         return function(expr) {
22688             try {
22689                 return(re.test(expr))
22690                     ? new Function("obj", "return obj." + expr)
22691                     : function(obj){
22692                         return obj[expr];
22693                     };
22694             } catch(e){}
22695             return Roo.emptyFn;
22696         };
22697     }(),
22698
22699     /**
22700      * Create a data block containing Roo.data.Records from an XML document.
22701      * @param {Object} o An object which contains an Array of row objects in the property specified
22702      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22703      * which contains the total size of the dataset.
22704      * @return {Object} data A data block which is used by an Roo.data.Store object as
22705      * a cache of Roo.data.Records.
22706      */
22707     readRecords : function(o){
22708         /**
22709          * After any data loads, the raw JSON data is available for further custom processing.
22710          * @type Object
22711          */
22712         this.o = o;
22713         var s = this.meta, Record = this.recordType,
22714             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22715
22716 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22717         if (!this.ef) {
22718             if(s.totalProperty) {
22719                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22720                 }
22721                 if(s.successProperty) {
22722                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22723                 }
22724                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22725                 if (s.id) {
22726                         var g = this.getJsonAccessor(s.id);
22727                         this.getId = function(rec) {
22728                                 var r = g(rec);  
22729                                 return (r === undefined || r === "") ? null : r;
22730                         };
22731                 } else {
22732                         this.getId = function(){return null;};
22733                 }
22734             this.ef = [];
22735             for(var jj = 0; jj < fl; jj++){
22736                 f = fi[jj];
22737                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22738                 this.ef[jj] = this.getJsonAccessor(map);
22739             }
22740         }
22741
22742         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22743         if(s.totalProperty){
22744             var vt = parseInt(this.getTotal(o), 10);
22745             if(!isNaN(vt)){
22746                 totalRecords = vt;
22747             }
22748         }
22749         if(s.successProperty){
22750             var vs = this.getSuccess(o);
22751             if(vs === false || vs === 'false'){
22752                 success = false;
22753             }
22754         }
22755         var records = [];
22756         for(var i = 0; i < c; i++){
22757                 var n = root[i];
22758             var values = {};
22759             var id = this.getId(n);
22760             for(var j = 0; j < fl; j++){
22761                 f = fi[j];
22762             var v = this.ef[j](n);
22763             if (!f.convert) {
22764                 Roo.log('missing convert for ' + f.name);
22765                 Roo.log(f);
22766                 continue;
22767             }
22768             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22769             }
22770             var record = new Record(values, id);
22771             record.json = n;
22772             records[i] = record;
22773         }
22774         return {
22775             raw : o,
22776             success : success,
22777             records : records,
22778             totalRecords : totalRecords
22779         };
22780     }
22781 });/*
22782  * Based on:
22783  * Ext JS Library 1.1.1
22784  * Copyright(c) 2006-2007, Ext JS, LLC.
22785  *
22786  * Originally Released Under LGPL - original licence link has changed is not relivant.
22787  *
22788  * Fork - LGPL
22789  * <script type="text/javascript">
22790  */
22791
22792 /**
22793  * @class Roo.data.XmlReader
22794  * @extends Roo.data.DataReader
22795  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22796  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22797  * <p>
22798  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22799  * header in the HTTP response must be set to "text/xml".</em>
22800  * <p>
22801  * Example code:
22802  * <pre><code>
22803 var RecordDef = Roo.data.Record.create([
22804    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22805    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22806 ]);
22807 var myReader = new Roo.data.XmlReader({
22808    totalRecords: "results", // The element which contains the total dataset size (optional)
22809    record: "row",           // The repeated element which contains row information
22810    id: "id"                 // The element within the row that provides an ID for the record (optional)
22811 }, RecordDef);
22812 </code></pre>
22813  * <p>
22814  * This would consume an XML file like this:
22815  * <pre><code>
22816 &lt;?xml?>
22817 &lt;dataset>
22818  &lt;results>2&lt;/results>
22819  &lt;row>
22820    &lt;id>1&lt;/id>
22821    &lt;name>Bill&lt;/name>
22822    &lt;occupation>Gardener&lt;/occupation>
22823  &lt;/row>
22824  &lt;row>
22825    &lt;id>2&lt;/id>
22826    &lt;name>Ben&lt;/name>
22827    &lt;occupation>Horticulturalist&lt;/occupation>
22828  &lt;/row>
22829 &lt;/dataset>
22830 </code></pre>
22831  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22832  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22833  * paged from the remote server.
22834  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22835  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22836  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22837  * a record identifier value.
22838  * @constructor
22839  * Create a new XmlReader
22840  * @param {Object} meta Metadata configuration options
22841  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22842  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22843  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22844  */
22845 Roo.data.XmlReader = function(meta, recordType){
22846     meta = meta || {};
22847     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22848 };
22849 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22850     /**
22851      * This method is only used by a DataProxy which has retrieved data from a remote server.
22852          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22853          * to contain a method called 'responseXML' that returns an XML document object.
22854      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22855      * a cache of Roo.data.Records.
22856      */
22857     read : function(response){
22858         var doc = response.responseXML;
22859         if(!doc) {
22860             throw {message: "XmlReader.read: XML Document not available"};
22861         }
22862         return this.readRecords(doc);
22863     },
22864
22865     /**
22866      * Create a data block containing Roo.data.Records from an XML document.
22867          * @param {Object} doc A parsed XML document.
22868      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22869      * a cache of Roo.data.Records.
22870      */
22871     readRecords : function(doc){
22872         /**
22873          * After any data loads/reads, the raw XML Document is available for further custom processing.
22874          * @type XMLDocument
22875          */
22876         this.xmlData = doc;
22877         var root = doc.documentElement || doc;
22878         var q = Roo.DomQuery;
22879         var recordType = this.recordType, fields = recordType.prototype.fields;
22880         var sid = this.meta.id;
22881         var totalRecords = 0, success = true;
22882         if(this.meta.totalRecords){
22883             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22884         }
22885         
22886         if(this.meta.success){
22887             var sv = q.selectValue(this.meta.success, root, true);
22888             success = sv !== false && sv !== 'false';
22889         }
22890         var records = [];
22891         var ns = q.select(this.meta.record, root);
22892         for(var i = 0, len = ns.length; i < len; i++) {
22893                 var n = ns[i];
22894                 var values = {};
22895                 var id = sid ? q.selectValue(sid, n) : undefined;
22896                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22897                     var f = fields.items[j];
22898                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22899                     v = f.convert(v);
22900                     values[f.name] = v;
22901                 }
22902                 var record = new recordType(values, id);
22903                 record.node = n;
22904                 records[records.length] = record;
22905             }
22906
22907             return {
22908                 success : success,
22909                 records : records,
22910                 totalRecords : totalRecords || records.length
22911             };
22912     }
22913 });/*
22914  * Based on:
22915  * Ext JS Library 1.1.1
22916  * Copyright(c) 2006-2007, Ext JS, LLC.
22917  *
22918  * Originally Released Under LGPL - original licence link has changed is not relivant.
22919  *
22920  * Fork - LGPL
22921  * <script type="text/javascript">
22922  */
22923
22924 /**
22925  * @class Roo.data.ArrayReader
22926  * @extends Roo.data.DataReader
22927  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22928  * Each element of that Array represents a row of data fields. The
22929  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22930  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22931  * <p>
22932  * Example code:.
22933  * <pre><code>
22934 var RecordDef = Roo.data.Record.create([
22935     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22936     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22937 ]);
22938 var myReader = new Roo.data.ArrayReader({
22939     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22940 }, RecordDef);
22941 </code></pre>
22942  * <p>
22943  * This would consume an Array like this:
22944  * <pre><code>
22945 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22946   </code></pre>
22947  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22948  * @constructor
22949  * Create a new JsonReader
22950  * @param {Object} meta Metadata configuration options.
22951  * @param {Object} recordType Either an Array of field definition objects
22952  * as specified to {@link Roo.data.Record#create},
22953  * or an {@link Roo.data.Record} object
22954  * created using {@link Roo.data.Record#create}.
22955  */
22956 Roo.data.ArrayReader = function(meta, recordType){
22957     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22958 };
22959
22960 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22961     /**
22962      * Create a data block containing Roo.data.Records from an XML document.
22963      * @param {Object} o An Array of row objects which represents the dataset.
22964      * @return {Object} data A data block which is used by an Roo.data.Store object as
22965      * a cache of Roo.data.Records.
22966      */
22967     readRecords : function(o){
22968         var sid = this.meta ? this.meta.id : null;
22969         var recordType = this.recordType, fields = recordType.prototype.fields;
22970         var records = [];
22971         var root = o;
22972             for(var i = 0; i < root.length; i++){
22973                     var n = root[i];
22974                 var values = {};
22975                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22976                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22977                 var f = fields.items[j];
22978                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22979                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22980                 v = f.convert(v);
22981                 values[f.name] = v;
22982             }
22983                 var record = new recordType(values, id);
22984                 record.json = n;
22985                 records[records.length] = record;
22986             }
22987             return {
22988                 records : records,
22989                 totalRecords : records.length
22990             };
22991     }
22992 });/*
22993  * Based on:
22994  * Ext JS Library 1.1.1
22995  * Copyright(c) 2006-2007, Ext JS, LLC.
22996  *
22997  * Originally Released Under LGPL - original licence link has changed is not relivant.
22998  *
22999  * Fork - LGPL
23000  * <script type="text/javascript">
23001  */
23002
23003
23004 /**
23005  * @class Roo.data.Tree
23006  * @extends Roo.util.Observable
23007  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23008  * in the tree have most standard DOM functionality.
23009  * @constructor
23010  * @param {Node} root (optional) The root node
23011  */
23012 Roo.data.Tree = function(root){
23013    this.nodeHash = {};
23014    /**
23015     * The root node for this tree
23016     * @type Node
23017     */
23018    this.root = null;
23019    if(root){
23020        this.setRootNode(root);
23021    }
23022    this.addEvents({
23023        /**
23024         * @event append
23025         * Fires when a new child node is appended to a node in this tree.
23026         * @param {Tree} tree The owner tree
23027         * @param {Node} parent The parent node
23028         * @param {Node} node The newly appended node
23029         * @param {Number} index The index of the newly appended node
23030         */
23031        "append" : true,
23032        /**
23033         * @event remove
23034         * Fires when a child node is removed from a node in this tree.
23035         * @param {Tree} tree The owner tree
23036         * @param {Node} parent The parent node
23037         * @param {Node} node The child node removed
23038         */
23039        "remove" : true,
23040        /**
23041         * @event move
23042         * Fires when a node is moved to a new location in the tree
23043         * @param {Tree} tree The owner tree
23044         * @param {Node} node The node moved
23045         * @param {Node} oldParent The old parent of this node
23046         * @param {Node} newParent The new parent of this node
23047         * @param {Number} index The index it was moved to
23048         */
23049        "move" : true,
23050        /**
23051         * @event insert
23052         * Fires when a new child node is inserted in a node in this tree.
23053         * @param {Tree} tree The owner tree
23054         * @param {Node} parent The parent node
23055         * @param {Node} node The child node inserted
23056         * @param {Node} refNode The child node the node was inserted before
23057         */
23058        "insert" : true,
23059        /**
23060         * @event beforeappend
23061         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23062         * @param {Tree} tree The owner tree
23063         * @param {Node} parent The parent node
23064         * @param {Node} node The child node to be appended
23065         */
23066        "beforeappend" : true,
23067        /**
23068         * @event beforeremove
23069         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23070         * @param {Tree} tree The owner tree
23071         * @param {Node} parent The parent node
23072         * @param {Node} node The child node to be removed
23073         */
23074        "beforeremove" : true,
23075        /**
23076         * @event beforemove
23077         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23078         * @param {Tree} tree The owner tree
23079         * @param {Node} node The node being moved
23080         * @param {Node} oldParent The parent of the node
23081         * @param {Node} newParent The new parent the node is moving to
23082         * @param {Number} index The index it is being moved to
23083         */
23084        "beforemove" : true,
23085        /**
23086         * @event beforeinsert
23087         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23088         * @param {Tree} tree The owner tree
23089         * @param {Node} parent The parent node
23090         * @param {Node} node The child node to be inserted
23091         * @param {Node} refNode The child node the node is being inserted before
23092         */
23093        "beforeinsert" : true
23094    });
23095
23096     Roo.data.Tree.superclass.constructor.call(this);
23097 };
23098
23099 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23100     pathSeparator: "/",
23101
23102     proxyNodeEvent : function(){
23103         return this.fireEvent.apply(this, arguments);
23104     },
23105
23106     /**
23107      * Returns the root node for this tree.
23108      * @return {Node}
23109      */
23110     getRootNode : function(){
23111         return this.root;
23112     },
23113
23114     /**
23115      * Sets the root node for this tree.
23116      * @param {Node} node
23117      * @return {Node}
23118      */
23119     setRootNode : function(node){
23120         this.root = node;
23121         node.ownerTree = this;
23122         node.isRoot = true;
23123         this.registerNode(node);
23124         return node;
23125     },
23126
23127     /**
23128      * Gets a node in this tree by its id.
23129      * @param {String} id
23130      * @return {Node}
23131      */
23132     getNodeById : function(id){
23133         return this.nodeHash[id];
23134     },
23135
23136     registerNode : function(node){
23137         this.nodeHash[node.id] = node;
23138     },
23139
23140     unregisterNode : function(node){
23141         delete this.nodeHash[node.id];
23142     },
23143
23144     toString : function(){
23145         return "[Tree"+(this.id?" "+this.id:"")+"]";
23146     }
23147 });
23148
23149 /**
23150  * @class Roo.data.Node
23151  * @extends Roo.util.Observable
23152  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23153  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23154  * @constructor
23155  * @param {Object} attributes The attributes/config for the node
23156  */
23157 Roo.data.Node = function(attributes){
23158     /**
23159      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23160      * @type {Object}
23161      */
23162     this.attributes = attributes || {};
23163     this.leaf = this.attributes.leaf;
23164     /**
23165      * The node id. @type String
23166      */
23167     this.id = this.attributes.id;
23168     if(!this.id){
23169         this.id = Roo.id(null, "ynode-");
23170         this.attributes.id = this.id;
23171     }
23172      
23173     
23174     /**
23175      * All child nodes of this node. @type Array
23176      */
23177     this.childNodes = [];
23178     if(!this.childNodes.indexOf){ // indexOf is a must
23179         this.childNodes.indexOf = function(o){
23180             for(var i = 0, len = this.length; i < len; i++){
23181                 if(this[i] == o) {
23182                     return i;
23183                 }
23184             }
23185             return -1;
23186         };
23187     }
23188     /**
23189      * The parent node for this node. @type Node
23190      */
23191     this.parentNode = null;
23192     /**
23193      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23194      */
23195     this.firstChild = null;
23196     /**
23197      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23198      */
23199     this.lastChild = null;
23200     /**
23201      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23202      */
23203     this.previousSibling = null;
23204     /**
23205      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23206      */
23207     this.nextSibling = null;
23208
23209     this.addEvents({
23210        /**
23211         * @event append
23212         * Fires when a new child node is appended
23213         * @param {Tree} tree The owner tree
23214         * @param {Node} this This node
23215         * @param {Node} node The newly appended node
23216         * @param {Number} index The index of the newly appended node
23217         */
23218        "append" : true,
23219        /**
23220         * @event remove
23221         * Fires when a child node is removed
23222         * @param {Tree} tree The owner tree
23223         * @param {Node} this This node
23224         * @param {Node} node The removed node
23225         */
23226        "remove" : true,
23227        /**
23228         * @event move
23229         * Fires when this node is moved to a new location in the tree
23230         * @param {Tree} tree The owner tree
23231         * @param {Node} this This node
23232         * @param {Node} oldParent The old parent of this node
23233         * @param {Node} newParent The new parent of this node
23234         * @param {Number} index The index it was moved to
23235         */
23236        "move" : true,
23237        /**
23238         * @event insert
23239         * Fires when a new child node is inserted.
23240         * @param {Tree} tree The owner tree
23241         * @param {Node} this This node
23242         * @param {Node} node The child node inserted
23243         * @param {Node} refNode The child node the node was inserted before
23244         */
23245        "insert" : true,
23246        /**
23247         * @event beforeappend
23248         * Fires before a new child is appended, return false to cancel the append.
23249         * @param {Tree} tree The owner tree
23250         * @param {Node} this This node
23251         * @param {Node} node The child node to be appended
23252         */
23253        "beforeappend" : true,
23254        /**
23255         * @event beforeremove
23256         * Fires before a child is removed, return false to cancel the remove.
23257         * @param {Tree} tree The owner tree
23258         * @param {Node} this This node
23259         * @param {Node} node The child node to be removed
23260         */
23261        "beforeremove" : true,
23262        /**
23263         * @event beforemove
23264         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23265         * @param {Tree} tree The owner tree
23266         * @param {Node} this This node
23267         * @param {Node} oldParent The parent of this node
23268         * @param {Node} newParent The new parent this node is moving to
23269         * @param {Number} index The index it is being moved to
23270         */
23271        "beforemove" : true,
23272        /**
23273         * @event beforeinsert
23274         * Fires before a new child is inserted, return false to cancel the insert.
23275         * @param {Tree} tree The owner tree
23276         * @param {Node} this This node
23277         * @param {Node} node The child node to be inserted
23278         * @param {Node} refNode The child node the node is being inserted before
23279         */
23280        "beforeinsert" : true
23281    });
23282     this.listeners = this.attributes.listeners;
23283     Roo.data.Node.superclass.constructor.call(this);
23284 };
23285
23286 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23287     fireEvent : function(evtName){
23288         // first do standard event for this node
23289         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23290             return false;
23291         }
23292         // then bubble it up to the tree if the event wasn't cancelled
23293         var ot = this.getOwnerTree();
23294         if(ot){
23295             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23296                 return false;
23297             }
23298         }
23299         return true;
23300     },
23301
23302     /**
23303      * Returns true if this node is a leaf
23304      * @return {Boolean}
23305      */
23306     isLeaf : function(){
23307         return this.leaf === true;
23308     },
23309
23310     // private
23311     setFirstChild : function(node){
23312         this.firstChild = node;
23313     },
23314
23315     //private
23316     setLastChild : function(node){
23317         this.lastChild = node;
23318     },
23319
23320
23321     /**
23322      * Returns true if this node is the last child of its parent
23323      * @return {Boolean}
23324      */
23325     isLast : function(){
23326        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23327     },
23328
23329     /**
23330      * Returns true if this node is the first child of its parent
23331      * @return {Boolean}
23332      */
23333     isFirst : function(){
23334        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23335     },
23336
23337     hasChildNodes : function(){
23338         return !this.isLeaf() && this.childNodes.length > 0;
23339     },
23340
23341     /**
23342      * Insert node(s) as the last child node of this node.
23343      * @param {Node/Array} node The node or Array of nodes to append
23344      * @return {Node} The appended node if single append, or null if an array was passed
23345      */
23346     appendChild : function(node){
23347         var multi = false;
23348         if(node instanceof Array){
23349             multi = node;
23350         }else if(arguments.length > 1){
23351             multi = arguments;
23352         }
23353         // if passed an array or multiple args do them one by one
23354         if(multi){
23355             for(var i = 0, len = multi.length; i < len; i++) {
23356                 this.appendChild(multi[i]);
23357             }
23358         }else{
23359             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23360                 return false;
23361             }
23362             var index = this.childNodes.length;
23363             var oldParent = node.parentNode;
23364             // it's a move, make sure we move it cleanly
23365             if(oldParent){
23366                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23367                     return false;
23368                 }
23369                 oldParent.removeChild(node);
23370             }
23371             index = this.childNodes.length;
23372             if(index == 0){
23373                 this.setFirstChild(node);
23374             }
23375             this.childNodes.push(node);
23376             node.parentNode = this;
23377             var ps = this.childNodes[index-1];
23378             if(ps){
23379                 node.previousSibling = ps;
23380                 ps.nextSibling = node;
23381             }else{
23382                 node.previousSibling = null;
23383             }
23384             node.nextSibling = null;
23385             this.setLastChild(node);
23386             node.setOwnerTree(this.getOwnerTree());
23387             this.fireEvent("append", this.ownerTree, this, node, index);
23388             if(oldParent){
23389                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23390             }
23391             return node;
23392         }
23393     },
23394
23395     /**
23396      * Removes a child node from this node.
23397      * @param {Node} node The node to remove
23398      * @return {Node} The removed node
23399      */
23400     removeChild : function(node){
23401         var index = this.childNodes.indexOf(node);
23402         if(index == -1){
23403             return false;
23404         }
23405         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23406             return false;
23407         }
23408
23409         // remove it from childNodes collection
23410         this.childNodes.splice(index, 1);
23411
23412         // update siblings
23413         if(node.previousSibling){
23414             node.previousSibling.nextSibling = node.nextSibling;
23415         }
23416         if(node.nextSibling){
23417             node.nextSibling.previousSibling = node.previousSibling;
23418         }
23419
23420         // update child refs
23421         if(this.firstChild == node){
23422             this.setFirstChild(node.nextSibling);
23423         }
23424         if(this.lastChild == node){
23425             this.setLastChild(node.previousSibling);
23426         }
23427
23428         node.setOwnerTree(null);
23429         // clear any references from the node
23430         node.parentNode = null;
23431         node.previousSibling = null;
23432         node.nextSibling = null;
23433         this.fireEvent("remove", this.ownerTree, this, node);
23434         return node;
23435     },
23436
23437     /**
23438      * Inserts the first node before the second node in this nodes childNodes collection.
23439      * @param {Node} node The node to insert
23440      * @param {Node} refNode The node to insert before (if null the node is appended)
23441      * @return {Node} The inserted node
23442      */
23443     insertBefore : function(node, refNode){
23444         if(!refNode){ // like standard Dom, refNode can be null for append
23445             return this.appendChild(node);
23446         }
23447         // nothing to do
23448         if(node == refNode){
23449             return false;
23450         }
23451
23452         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23453             return false;
23454         }
23455         var index = this.childNodes.indexOf(refNode);
23456         var oldParent = node.parentNode;
23457         var refIndex = index;
23458
23459         // when moving internally, indexes will change after remove
23460         if(oldParent == this && this.childNodes.indexOf(node) < index){
23461             refIndex--;
23462         }
23463
23464         // it's a move, make sure we move it cleanly
23465         if(oldParent){
23466             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23467                 return false;
23468             }
23469             oldParent.removeChild(node);
23470         }
23471         if(refIndex == 0){
23472             this.setFirstChild(node);
23473         }
23474         this.childNodes.splice(refIndex, 0, node);
23475         node.parentNode = this;
23476         var ps = this.childNodes[refIndex-1];
23477         if(ps){
23478             node.previousSibling = ps;
23479             ps.nextSibling = node;
23480         }else{
23481             node.previousSibling = null;
23482         }
23483         node.nextSibling = refNode;
23484         refNode.previousSibling = node;
23485         node.setOwnerTree(this.getOwnerTree());
23486         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23487         if(oldParent){
23488             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23489         }
23490         return node;
23491     },
23492
23493     /**
23494      * Returns the child node at the specified index.
23495      * @param {Number} index
23496      * @return {Node}
23497      */
23498     item : function(index){
23499         return this.childNodes[index];
23500     },
23501
23502     /**
23503      * Replaces one child node in this node with another.
23504      * @param {Node} newChild The replacement node
23505      * @param {Node} oldChild The node to replace
23506      * @return {Node} The replaced node
23507      */
23508     replaceChild : function(newChild, oldChild){
23509         this.insertBefore(newChild, oldChild);
23510         this.removeChild(oldChild);
23511         return oldChild;
23512     },
23513
23514     /**
23515      * Returns the index of a child node
23516      * @param {Node} node
23517      * @return {Number} The index of the node or -1 if it was not found
23518      */
23519     indexOf : function(child){
23520         return this.childNodes.indexOf(child);
23521     },
23522
23523     /**
23524      * Returns the tree this node is in.
23525      * @return {Tree}
23526      */
23527     getOwnerTree : function(){
23528         // if it doesn't have one, look for one
23529         if(!this.ownerTree){
23530             var p = this;
23531             while(p){
23532                 if(p.ownerTree){
23533                     this.ownerTree = p.ownerTree;
23534                     break;
23535                 }
23536                 p = p.parentNode;
23537             }
23538         }
23539         return this.ownerTree;
23540     },
23541
23542     /**
23543      * Returns depth of this node (the root node has a depth of 0)
23544      * @return {Number}
23545      */
23546     getDepth : function(){
23547         var depth = 0;
23548         var p = this;
23549         while(p.parentNode){
23550             ++depth;
23551             p = p.parentNode;
23552         }
23553         return depth;
23554     },
23555
23556     // private
23557     setOwnerTree : function(tree){
23558         // if it's move, we need to update everyone
23559         if(tree != this.ownerTree){
23560             if(this.ownerTree){
23561                 this.ownerTree.unregisterNode(this);
23562             }
23563             this.ownerTree = tree;
23564             var cs = this.childNodes;
23565             for(var i = 0, len = cs.length; i < len; i++) {
23566                 cs[i].setOwnerTree(tree);
23567             }
23568             if(tree){
23569                 tree.registerNode(this);
23570             }
23571         }
23572     },
23573
23574     /**
23575      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23576      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23577      * @return {String} The path
23578      */
23579     getPath : function(attr){
23580         attr = attr || "id";
23581         var p = this.parentNode;
23582         var b = [this.attributes[attr]];
23583         while(p){
23584             b.unshift(p.attributes[attr]);
23585             p = p.parentNode;
23586         }
23587         var sep = this.getOwnerTree().pathSeparator;
23588         return sep + b.join(sep);
23589     },
23590
23591     /**
23592      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23593      * function call will be the scope provided or the current node. The arguments to the function
23594      * will be the args provided or the current node. If the function returns false at any point,
23595      * the bubble is stopped.
23596      * @param {Function} fn The function to call
23597      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23598      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23599      */
23600     bubble : function(fn, scope, args){
23601         var p = this;
23602         while(p){
23603             if(fn.call(scope || p, args || p) === false){
23604                 break;
23605             }
23606             p = p.parentNode;
23607         }
23608     },
23609
23610     /**
23611      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23612      * function call will be the scope provided or the current node. The arguments to the function
23613      * will be the args provided or the current node. If the function returns false at any point,
23614      * the cascade is stopped on that branch.
23615      * @param {Function} fn The function to call
23616      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23617      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23618      */
23619     cascade : function(fn, scope, args){
23620         if(fn.call(scope || this, args || this) !== false){
23621             var cs = this.childNodes;
23622             for(var i = 0, len = cs.length; i < len; i++) {
23623                 cs[i].cascade(fn, scope, args);
23624             }
23625         }
23626     },
23627
23628     /**
23629      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23630      * function call will be the scope provided or the current node. The arguments to the function
23631      * will be the args provided or the current node. If the function returns false at any point,
23632      * the iteration stops.
23633      * @param {Function} fn The function to call
23634      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23635      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23636      */
23637     eachChild : function(fn, scope, args){
23638         var cs = this.childNodes;
23639         for(var i = 0, len = cs.length; i < len; i++) {
23640                 if(fn.call(scope || this, args || cs[i]) === false){
23641                     break;
23642                 }
23643         }
23644     },
23645
23646     /**
23647      * Finds the first child that has the attribute with the specified value.
23648      * @param {String} attribute The attribute name
23649      * @param {Mixed} value The value to search for
23650      * @return {Node} The found child or null if none was found
23651      */
23652     findChild : function(attribute, value){
23653         var cs = this.childNodes;
23654         for(var i = 0, len = cs.length; i < len; i++) {
23655                 if(cs[i].attributes[attribute] == value){
23656                     return cs[i];
23657                 }
23658         }
23659         return null;
23660     },
23661
23662     /**
23663      * Finds the first child by a custom function. The child matches if the function passed
23664      * returns true.
23665      * @param {Function} fn
23666      * @param {Object} scope (optional)
23667      * @return {Node} The found child or null if none was found
23668      */
23669     findChildBy : function(fn, scope){
23670         var cs = this.childNodes;
23671         for(var i = 0, len = cs.length; i < len; i++) {
23672                 if(fn.call(scope||cs[i], cs[i]) === true){
23673                     return cs[i];
23674                 }
23675         }
23676         return null;
23677     },
23678
23679     /**
23680      * Sorts this nodes children using the supplied sort function
23681      * @param {Function} fn
23682      * @param {Object} scope (optional)
23683      */
23684     sort : function(fn, scope){
23685         var cs = this.childNodes;
23686         var len = cs.length;
23687         if(len > 0){
23688             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23689             cs.sort(sortFn);
23690             for(var i = 0; i < len; i++){
23691                 var n = cs[i];
23692                 n.previousSibling = cs[i-1];
23693                 n.nextSibling = cs[i+1];
23694                 if(i == 0){
23695                     this.setFirstChild(n);
23696                 }
23697                 if(i == len-1){
23698                     this.setLastChild(n);
23699                 }
23700             }
23701         }
23702     },
23703
23704     /**
23705      * Returns true if this node is an ancestor (at any point) of the passed node.
23706      * @param {Node} node
23707      * @return {Boolean}
23708      */
23709     contains : function(node){
23710         return node.isAncestor(this);
23711     },
23712
23713     /**
23714      * Returns true if the passed node is an ancestor (at any point) of this node.
23715      * @param {Node} node
23716      * @return {Boolean}
23717      */
23718     isAncestor : function(node){
23719         var p = this.parentNode;
23720         while(p){
23721             if(p == node){
23722                 return true;
23723             }
23724             p = p.parentNode;
23725         }
23726         return false;
23727     },
23728
23729     toString : function(){
23730         return "[Node"+(this.id?" "+this.id:"")+"]";
23731     }
23732 });/*
23733  * Based on:
23734  * Ext JS Library 1.1.1
23735  * Copyright(c) 2006-2007, Ext JS, LLC.
23736  *
23737  * Originally Released Under LGPL - original licence link has changed is not relivant.
23738  *
23739  * Fork - LGPL
23740  * <script type="text/javascript">
23741  */
23742  (function(){ 
23743 /**
23744  * @class Roo.Layer
23745  * @extends Roo.Element
23746  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23747  * automatic maintaining of shadow/shim positions.
23748  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23749  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23750  * you can pass a string with a CSS class name. False turns off the shadow.
23751  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23752  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23753  * @cfg {String} cls CSS class to add to the element
23754  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23755  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23756  * @constructor
23757  * @param {Object} config An object with config options.
23758  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23759  */
23760
23761 Roo.Layer = function(config, existingEl){
23762     config = config || {};
23763     var dh = Roo.DomHelper;
23764     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23765     if(existingEl){
23766         this.dom = Roo.getDom(existingEl);
23767     }
23768     if(!this.dom){
23769         var o = config.dh || {tag: "div", cls: "x-layer"};
23770         this.dom = dh.append(pel, o);
23771     }
23772     if(config.cls){
23773         this.addClass(config.cls);
23774     }
23775     this.constrain = config.constrain !== false;
23776     this.visibilityMode = Roo.Element.VISIBILITY;
23777     if(config.id){
23778         this.id = this.dom.id = config.id;
23779     }else{
23780         this.id = Roo.id(this.dom);
23781     }
23782     this.zindex = config.zindex || this.getZIndex();
23783     this.position("absolute", this.zindex);
23784     if(config.shadow){
23785         this.shadowOffset = config.shadowOffset || 4;
23786         this.shadow = new Roo.Shadow({
23787             offset : this.shadowOffset,
23788             mode : config.shadow
23789         });
23790     }else{
23791         this.shadowOffset = 0;
23792     }
23793     this.useShim = config.shim !== false && Roo.useShims;
23794     this.useDisplay = config.useDisplay;
23795     this.hide();
23796 };
23797
23798 var supr = Roo.Element.prototype;
23799
23800 // shims are shared among layer to keep from having 100 iframes
23801 var shims = [];
23802
23803 Roo.extend(Roo.Layer, Roo.Element, {
23804
23805     getZIndex : function(){
23806         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23807     },
23808
23809     getShim : function(){
23810         if(!this.useShim){
23811             return null;
23812         }
23813         if(this.shim){
23814             return this.shim;
23815         }
23816         var shim = shims.shift();
23817         if(!shim){
23818             shim = this.createShim();
23819             shim.enableDisplayMode('block');
23820             shim.dom.style.display = 'none';
23821             shim.dom.style.visibility = 'visible';
23822         }
23823         var pn = this.dom.parentNode;
23824         if(shim.dom.parentNode != pn){
23825             pn.insertBefore(shim.dom, this.dom);
23826         }
23827         shim.setStyle('z-index', this.getZIndex()-2);
23828         this.shim = shim;
23829         return shim;
23830     },
23831
23832     hideShim : function(){
23833         if(this.shim){
23834             this.shim.setDisplayed(false);
23835             shims.push(this.shim);
23836             delete this.shim;
23837         }
23838     },
23839
23840     disableShadow : function(){
23841         if(this.shadow){
23842             this.shadowDisabled = true;
23843             this.shadow.hide();
23844             this.lastShadowOffset = this.shadowOffset;
23845             this.shadowOffset = 0;
23846         }
23847     },
23848
23849     enableShadow : function(show){
23850         if(this.shadow){
23851             this.shadowDisabled = false;
23852             this.shadowOffset = this.lastShadowOffset;
23853             delete this.lastShadowOffset;
23854             if(show){
23855                 this.sync(true);
23856             }
23857         }
23858     },
23859
23860     // private
23861     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23862     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23863     sync : function(doShow){
23864         var sw = this.shadow;
23865         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23866             var sh = this.getShim();
23867
23868             var w = this.getWidth(),
23869                 h = this.getHeight();
23870
23871             var l = this.getLeft(true),
23872                 t = this.getTop(true);
23873
23874             if(sw && !this.shadowDisabled){
23875                 if(doShow && !sw.isVisible()){
23876                     sw.show(this);
23877                 }else{
23878                     sw.realign(l, t, w, h);
23879                 }
23880                 if(sh){
23881                     if(doShow){
23882                        sh.show();
23883                     }
23884                     // fit the shim behind the shadow, so it is shimmed too
23885                     var a = sw.adjusts, s = sh.dom.style;
23886                     s.left = (Math.min(l, l+a.l))+"px";
23887                     s.top = (Math.min(t, t+a.t))+"px";
23888                     s.width = (w+a.w)+"px";
23889                     s.height = (h+a.h)+"px";
23890                 }
23891             }else if(sh){
23892                 if(doShow){
23893                    sh.show();
23894                 }
23895                 sh.setSize(w, h);
23896                 sh.setLeftTop(l, t);
23897             }
23898             
23899         }
23900     },
23901
23902     // private
23903     destroy : function(){
23904         this.hideShim();
23905         if(this.shadow){
23906             this.shadow.hide();
23907         }
23908         this.removeAllListeners();
23909         var pn = this.dom.parentNode;
23910         if(pn){
23911             pn.removeChild(this.dom);
23912         }
23913         Roo.Element.uncache(this.id);
23914     },
23915
23916     remove : function(){
23917         this.destroy();
23918     },
23919
23920     // private
23921     beginUpdate : function(){
23922         this.updating = true;
23923     },
23924
23925     // private
23926     endUpdate : function(){
23927         this.updating = false;
23928         this.sync(true);
23929     },
23930
23931     // private
23932     hideUnders : function(negOffset){
23933         if(this.shadow){
23934             this.shadow.hide();
23935         }
23936         this.hideShim();
23937     },
23938
23939     // private
23940     constrainXY : function(){
23941         if(this.constrain){
23942             var vw = Roo.lib.Dom.getViewWidth(),
23943                 vh = Roo.lib.Dom.getViewHeight();
23944             var s = Roo.get(document).getScroll();
23945
23946             var xy = this.getXY();
23947             var x = xy[0], y = xy[1];   
23948             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23949             // only move it if it needs it
23950             var moved = false;
23951             // first validate right/bottom
23952             if((x + w) > vw+s.left){
23953                 x = vw - w - this.shadowOffset;
23954                 moved = true;
23955             }
23956             if((y + h) > vh+s.top){
23957                 y = vh - h - this.shadowOffset;
23958                 moved = true;
23959             }
23960             // then make sure top/left isn't negative
23961             if(x < s.left){
23962                 x = s.left;
23963                 moved = true;
23964             }
23965             if(y < s.top){
23966                 y = s.top;
23967                 moved = true;
23968             }
23969             if(moved){
23970                 if(this.avoidY){
23971                     var ay = this.avoidY;
23972                     if(y <= ay && (y+h) >= ay){
23973                         y = ay-h-5;   
23974                     }
23975                 }
23976                 xy = [x, y];
23977                 this.storeXY(xy);
23978                 supr.setXY.call(this, xy);
23979                 this.sync();
23980             }
23981         }
23982     },
23983
23984     isVisible : function(){
23985         return this.visible;    
23986     },
23987
23988     // private
23989     showAction : function(){
23990         this.visible = true; // track visibility to prevent getStyle calls
23991         if(this.useDisplay === true){
23992             this.setDisplayed("");
23993         }else if(this.lastXY){
23994             supr.setXY.call(this, this.lastXY);
23995         }else if(this.lastLT){
23996             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23997         }
23998     },
23999
24000     // private
24001     hideAction : function(){
24002         this.visible = false;
24003         if(this.useDisplay === true){
24004             this.setDisplayed(false);
24005         }else{
24006             this.setLeftTop(-10000,-10000);
24007         }
24008     },
24009
24010     // overridden Element method
24011     setVisible : function(v, a, d, c, e){
24012         if(v){
24013             this.showAction();
24014         }
24015         if(a && v){
24016             var cb = function(){
24017                 this.sync(true);
24018                 if(c){
24019                     c();
24020                 }
24021             }.createDelegate(this);
24022             supr.setVisible.call(this, true, true, d, cb, e);
24023         }else{
24024             if(!v){
24025                 this.hideUnders(true);
24026             }
24027             var cb = c;
24028             if(a){
24029                 cb = function(){
24030                     this.hideAction();
24031                     if(c){
24032                         c();
24033                     }
24034                 }.createDelegate(this);
24035             }
24036             supr.setVisible.call(this, v, a, d, cb, e);
24037             if(v){
24038                 this.sync(true);
24039             }else if(!a){
24040                 this.hideAction();
24041             }
24042         }
24043     },
24044
24045     storeXY : function(xy){
24046         delete this.lastLT;
24047         this.lastXY = xy;
24048     },
24049
24050     storeLeftTop : function(left, top){
24051         delete this.lastXY;
24052         this.lastLT = [left, top];
24053     },
24054
24055     // private
24056     beforeFx : function(){
24057         this.beforeAction();
24058         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24059     },
24060
24061     // private
24062     afterFx : function(){
24063         Roo.Layer.superclass.afterFx.apply(this, arguments);
24064         this.sync(this.isVisible());
24065     },
24066
24067     // private
24068     beforeAction : function(){
24069         if(!this.updating && this.shadow){
24070             this.shadow.hide();
24071         }
24072     },
24073
24074     // overridden Element method
24075     setLeft : function(left){
24076         this.storeLeftTop(left, this.getTop(true));
24077         supr.setLeft.apply(this, arguments);
24078         this.sync();
24079     },
24080
24081     setTop : function(top){
24082         this.storeLeftTop(this.getLeft(true), top);
24083         supr.setTop.apply(this, arguments);
24084         this.sync();
24085     },
24086
24087     setLeftTop : function(left, top){
24088         this.storeLeftTop(left, top);
24089         supr.setLeftTop.apply(this, arguments);
24090         this.sync();
24091     },
24092
24093     setXY : function(xy, a, d, c, e){
24094         this.fixDisplay();
24095         this.beforeAction();
24096         this.storeXY(xy);
24097         var cb = this.createCB(c);
24098         supr.setXY.call(this, xy, a, d, cb, e);
24099         if(!a){
24100             cb();
24101         }
24102     },
24103
24104     // private
24105     createCB : function(c){
24106         var el = this;
24107         return function(){
24108             el.constrainXY();
24109             el.sync(true);
24110             if(c){
24111                 c();
24112             }
24113         };
24114     },
24115
24116     // overridden Element method
24117     setX : function(x, a, d, c, e){
24118         this.setXY([x, this.getY()], a, d, c, e);
24119     },
24120
24121     // overridden Element method
24122     setY : function(y, a, d, c, e){
24123         this.setXY([this.getX(), y], a, d, c, e);
24124     },
24125
24126     // overridden Element method
24127     setSize : function(w, h, a, d, c, e){
24128         this.beforeAction();
24129         var cb = this.createCB(c);
24130         supr.setSize.call(this, w, h, a, d, cb, e);
24131         if(!a){
24132             cb();
24133         }
24134     },
24135
24136     // overridden Element method
24137     setWidth : function(w, a, d, c, e){
24138         this.beforeAction();
24139         var cb = this.createCB(c);
24140         supr.setWidth.call(this, w, a, d, cb, e);
24141         if(!a){
24142             cb();
24143         }
24144     },
24145
24146     // overridden Element method
24147     setHeight : function(h, a, d, c, e){
24148         this.beforeAction();
24149         var cb = this.createCB(c);
24150         supr.setHeight.call(this, h, a, d, cb, e);
24151         if(!a){
24152             cb();
24153         }
24154     },
24155
24156     // overridden Element method
24157     setBounds : function(x, y, w, h, a, d, c, e){
24158         this.beforeAction();
24159         var cb = this.createCB(c);
24160         if(!a){
24161             this.storeXY([x, y]);
24162             supr.setXY.call(this, [x, y]);
24163             supr.setSize.call(this, w, h, a, d, cb, e);
24164             cb();
24165         }else{
24166             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24167         }
24168         return this;
24169     },
24170     
24171     /**
24172      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24173      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24174      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24175      * @param {Number} zindex The new z-index to set
24176      * @return {this} The Layer
24177      */
24178     setZIndex : function(zindex){
24179         this.zindex = zindex;
24180         this.setStyle("z-index", zindex + 2);
24181         if(this.shadow){
24182             this.shadow.setZIndex(zindex + 1);
24183         }
24184         if(this.shim){
24185             this.shim.setStyle("z-index", zindex);
24186         }
24187     }
24188 });
24189 })();/*
24190  * Based on:
24191  * Ext JS Library 1.1.1
24192  * Copyright(c) 2006-2007, Ext JS, LLC.
24193  *
24194  * Originally Released Under LGPL - original licence link has changed is not relivant.
24195  *
24196  * Fork - LGPL
24197  * <script type="text/javascript">
24198  */
24199
24200
24201 /**
24202  * @class Roo.Shadow
24203  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24204  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24205  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24206  * @constructor
24207  * Create a new Shadow
24208  * @param {Object} config The config object
24209  */
24210 Roo.Shadow = function(config){
24211     Roo.apply(this, config);
24212     if(typeof this.mode != "string"){
24213         this.mode = this.defaultMode;
24214     }
24215     var o = this.offset, a = {h: 0};
24216     var rad = Math.floor(this.offset/2);
24217     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24218         case "drop":
24219             a.w = 0;
24220             a.l = a.t = o;
24221             a.t -= 1;
24222             if(Roo.isIE){
24223                 a.l -= this.offset + rad;
24224                 a.t -= this.offset + rad;
24225                 a.w -= rad;
24226                 a.h -= rad;
24227                 a.t += 1;
24228             }
24229         break;
24230         case "sides":
24231             a.w = (o*2);
24232             a.l = -o;
24233             a.t = o-1;
24234             if(Roo.isIE){
24235                 a.l -= (this.offset - rad);
24236                 a.t -= this.offset + rad;
24237                 a.l += 1;
24238                 a.w -= (this.offset - rad)*2;
24239                 a.w -= rad + 1;
24240                 a.h -= 1;
24241             }
24242         break;
24243         case "frame":
24244             a.w = a.h = (o*2);
24245             a.l = a.t = -o;
24246             a.t += 1;
24247             a.h -= 2;
24248             if(Roo.isIE){
24249                 a.l -= (this.offset - rad);
24250                 a.t -= (this.offset - rad);
24251                 a.l += 1;
24252                 a.w -= (this.offset + rad + 1);
24253                 a.h -= (this.offset + rad);
24254                 a.h += 1;
24255             }
24256         break;
24257     };
24258
24259     this.adjusts = a;
24260 };
24261
24262 Roo.Shadow.prototype = {
24263     /**
24264      * @cfg {String} mode
24265      * The shadow display mode.  Supports the following options:<br />
24266      * sides: Shadow displays on both sides and bottom only<br />
24267      * frame: Shadow displays equally on all four sides<br />
24268      * drop: Traditional bottom-right drop shadow (default)
24269      */
24270     /**
24271      * @cfg {String} offset
24272      * The number of pixels to offset the shadow from the element (defaults to 4)
24273      */
24274     offset: 4,
24275
24276     // private
24277     defaultMode: "drop",
24278
24279     /**
24280      * Displays the shadow under the target element
24281      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24282      */
24283     show : function(target){
24284         target = Roo.get(target);
24285         if(!this.el){
24286             this.el = Roo.Shadow.Pool.pull();
24287             if(this.el.dom.nextSibling != target.dom){
24288                 this.el.insertBefore(target);
24289             }
24290         }
24291         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24292         if(Roo.isIE){
24293             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24294         }
24295         this.realign(
24296             target.getLeft(true),
24297             target.getTop(true),
24298             target.getWidth(),
24299             target.getHeight()
24300         );
24301         this.el.dom.style.display = "block";
24302     },
24303
24304     /**
24305      * Returns true if the shadow is visible, else false
24306      */
24307     isVisible : function(){
24308         return this.el ? true : false;  
24309     },
24310
24311     /**
24312      * Direct alignment when values are already available. Show must be called at least once before
24313      * calling this method to ensure it is initialized.
24314      * @param {Number} left The target element left position
24315      * @param {Number} top The target element top position
24316      * @param {Number} width The target element width
24317      * @param {Number} height The target element height
24318      */
24319     realign : function(l, t, w, h){
24320         if(!this.el){
24321             return;
24322         }
24323         var a = this.adjusts, d = this.el.dom, s = d.style;
24324         var iea = 0;
24325         s.left = (l+a.l)+"px";
24326         s.top = (t+a.t)+"px";
24327         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24328  
24329         if(s.width != sws || s.height != shs){
24330             s.width = sws;
24331             s.height = shs;
24332             if(!Roo.isIE){
24333                 var cn = d.childNodes;
24334                 var sww = Math.max(0, (sw-12))+"px";
24335                 cn[0].childNodes[1].style.width = sww;
24336                 cn[1].childNodes[1].style.width = sww;
24337                 cn[2].childNodes[1].style.width = sww;
24338                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24339             }
24340         }
24341     },
24342
24343     /**
24344      * Hides this shadow
24345      */
24346     hide : function(){
24347         if(this.el){
24348             this.el.dom.style.display = "none";
24349             Roo.Shadow.Pool.push(this.el);
24350             delete this.el;
24351         }
24352     },
24353
24354     /**
24355      * Adjust the z-index of this shadow
24356      * @param {Number} zindex The new z-index
24357      */
24358     setZIndex : function(z){
24359         this.zIndex = z;
24360         if(this.el){
24361             this.el.setStyle("z-index", z);
24362         }
24363     }
24364 };
24365
24366 // Private utility class that manages the internal Shadow cache
24367 Roo.Shadow.Pool = function(){
24368     var p = [];
24369     var markup = Roo.isIE ?
24370                  '<div class="x-ie-shadow"></div>' :
24371                  '<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>';
24372     return {
24373         pull : function(){
24374             var sh = p.shift();
24375             if(!sh){
24376                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24377                 sh.autoBoxAdjust = false;
24378             }
24379             return sh;
24380         },
24381
24382         push : function(sh){
24383             p.push(sh);
24384         }
24385     };
24386 }();/*
24387  * Based on:
24388  * Ext JS Library 1.1.1
24389  * Copyright(c) 2006-2007, Ext JS, LLC.
24390  *
24391  * Originally Released Under LGPL - original licence link has changed is not relivant.
24392  *
24393  * Fork - LGPL
24394  * <script type="text/javascript">
24395  */
24396
24397
24398 /**
24399  * @class Roo.SplitBar
24400  * @extends Roo.util.Observable
24401  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24402  * <br><br>
24403  * Usage:
24404  * <pre><code>
24405 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24406                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24407 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24408 split.minSize = 100;
24409 split.maxSize = 600;
24410 split.animate = true;
24411 split.on('moved', splitterMoved);
24412 </code></pre>
24413  * @constructor
24414  * Create a new SplitBar
24415  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24416  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24417  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24418  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24419                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24420                         position of the SplitBar).
24421  */
24422 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24423     
24424     /** @private */
24425     this.el = Roo.get(dragElement, true);
24426     this.el.dom.unselectable = "on";
24427     /** @private */
24428     this.resizingEl = Roo.get(resizingElement, true);
24429
24430     /**
24431      * @private
24432      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24433      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24434      * @type Number
24435      */
24436     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24437     
24438     /**
24439      * The minimum size of the resizing element. (Defaults to 0)
24440      * @type Number
24441      */
24442     this.minSize = 0;
24443     
24444     /**
24445      * The maximum size of the resizing element. (Defaults to 2000)
24446      * @type Number
24447      */
24448     this.maxSize = 2000;
24449     
24450     /**
24451      * Whether to animate the transition to the new size
24452      * @type Boolean
24453      */
24454     this.animate = false;
24455     
24456     /**
24457      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24458      * @type Boolean
24459      */
24460     this.useShim = false;
24461     
24462     /** @private */
24463     this.shim = null;
24464     
24465     if(!existingProxy){
24466         /** @private */
24467         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24468     }else{
24469         this.proxy = Roo.get(existingProxy).dom;
24470     }
24471     /** @private */
24472     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24473     
24474     /** @private */
24475     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24476     
24477     /** @private */
24478     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24479     
24480     /** @private */
24481     this.dragSpecs = {};
24482     
24483     /**
24484      * @private The adapter to use to positon and resize elements
24485      */
24486     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24487     this.adapter.init(this);
24488     
24489     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24490         /** @private */
24491         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24492         this.el.addClass("x-splitbar-h");
24493     }else{
24494         /** @private */
24495         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24496         this.el.addClass("x-splitbar-v");
24497     }
24498     
24499     this.addEvents({
24500         /**
24501          * @event resize
24502          * Fires when the splitter is moved (alias for {@link #event-moved})
24503          * @param {Roo.SplitBar} this
24504          * @param {Number} newSize the new width or height
24505          */
24506         "resize" : true,
24507         /**
24508          * @event moved
24509          * Fires when the splitter is moved
24510          * @param {Roo.SplitBar} this
24511          * @param {Number} newSize the new width or height
24512          */
24513         "moved" : true,
24514         /**
24515          * @event beforeresize
24516          * Fires before the splitter is dragged
24517          * @param {Roo.SplitBar} this
24518          */
24519         "beforeresize" : true,
24520
24521         "beforeapply" : true
24522     });
24523
24524     Roo.util.Observable.call(this);
24525 };
24526
24527 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24528     onStartProxyDrag : function(x, y){
24529         this.fireEvent("beforeresize", this);
24530         if(!this.overlay){
24531             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24532             o.unselectable();
24533             o.enableDisplayMode("block");
24534             // all splitbars share the same overlay
24535             Roo.SplitBar.prototype.overlay = o;
24536         }
24537         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24538         this.overlay.show();
24539         Roo.get(this.proxy).setDisplayed("block");
24540         var size = this.adapter.getElementSize(this);
24541         this.activeMinSize = this.getMinimumSize();;
24542         this.activeMaxSize = this.getMaximumSize();;
24543         var c1 = size - this.activeMinSize;
24544         var c2 = Math.max(this.activeMaxSize - size, 0);
24545         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24546             this.dd.resetConstraints();
24547             this.dd.setXConstraint(
24548                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24549                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24550             );
24551             this.dd.setYConstraint(0, 0);
24552         }else{
24553             this.dd.resetConstraints();
24554             this.dd.setXConstraint(0, 0);
24555             this.dd.setYConstraint(
24556                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24557                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24558             );
24559          }
24560         this.dragSpecs.startSize = size;
24561         this.dragSpecs.startPoint = [x, y];
24562         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24563     },
24564     
24565     /** 
24566      * @private Called after the drag operation by the DDProxy
24567      */
24568     onEndProxyDrag : function(e){
24569         Roo.get(this.proxy).setDisplayed(false);
24570         var endPoint = Roo.lib.Event.getXY(e);
24571         if(this.overlay){
24572             this.overlay.hide();
24573         }
24574         var newSize;
24575         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24576             newSize = this.dragSpecs.startSize + 
24577                 (this.placement == Roo.SplitBar.LEFT ?
24578                     endPoint[0] - this.dragSpecs.startPoint[0] :
24579                     this.dragSpecs.startPoint[0] - endPoint[0]
24580                 );
24581         }else{
24582             newSize = this.dragSpecs.startSize + 
24583                 (this.placement == Roo.SplitBar.TOP ?
24584                     endPoint[1] - this.dragSpecs.startPoint[1] :
24585                     this.dragSpecs.startPoint[1] - endPoint[1]
24586                 );
24587         }
24588         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24589         if(newSize != this.dragSpecs.startSize){
24590             if(this.fireEvent('beforeapply', this, newSize) !== false){
24591                 this.adapter.setElementSize(this, newSize);
24592                 this.fireEvent("moved", this, newSize);
24593                 this.fireEvent("resize", this, newSize);
24594             }
24595         }
24596     },
24597     
24598     /**
24599      * Get the adapter this SplitBar uses
24600      * @return The adapter object
24601      */
24602     getAdapter : function(){
24603         return this.adapter;
24604     },
24605     
24606     /**
24607      * Set the adapter this SplitBar uses
24608      * @param {Object} adapter A SplitBar adapter object
24609      */
24610     setAdapter : function(adapter){
24611         this.adapter = adapter;
24612         this.adapter.init(this);
24613     },
24614     
24615     /**
24616      * Gets the minimum size for the resizing element
24617      * @return {Number} The minimum size
24618      */
24619     getMinimumSize : function(){
24620         return this.minSize;
24621     },
24622     
24623     /**
24624      * Sets the minimum size for the resizing element
24625      * @param {Number} minSize The minimum size
24626      */
24627     setMinimumSize : function(minSize){
24628         this.minSize = minSize;
24629     },
24630     
24631     /**
24632      * Gets the maximum size for the resizing element
24633      * @return {Number} The maximum size
24634      */
24635     getMaximumSize : function(){
24636         return this.maxSize;
24637     },
24638     
24639     /**
24640      * Sets the maximum size for the resizing element
24641      * @param {Number} maxSize The maximum size
24642      */
24643     setMaximumSize : function(maxSize){
24644         this.maxSize = maxSize;
24645     },
24646     
24647     /**
24648      * Sets the initialize size for the resizing element
24649      * @param {Number} size The initial size
24650      */
24651     setCurrentSize : function(size){
24652         var oldAnimate = this.animate;
24653         this.animate = false;
24654         this.adapter.setElementSize(this, size);
24655         this.animate = oldAnimate;
24656     },
24657     
24658     /**
24659      * Destroy this splitbar. 
24660      * @param {Boolean} removeEl True to remove the element
24661      */
24662     destroy : function(removeEl){
24663         if(this.shim){
24664             this.shim.remove();
24665         }
24666         this.dd.unreg();
24667         this.proxy.parentNode.removeChild(this.proxy);
24668         if(removeEl){
24669             this.el.remove();
24670         }
24671     }
24672 });
24673
24674 /**
24675  * @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.
24676  */
24677 Roo.SplitBar.createProxy = function(dir){
24678     var proxy = new Roo.Element(document.createElement("div"));
24679     proxy.unselectable();
24680     var cls = 'x-splitbar-proxy';
24681     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24682     document.body.appendChild(proxy.dom);
24683     return proxy.dom;
24684 };
24685
24686 /** 
24687  * @class Roo.SplitBar.BasicLayoutAdapter
24688  * Default Adapter. It assumes the splitter and resizing element are not positioned
24689  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24690  */
24691 Roo.SplitBar.BasicLayoutAdapter = function(){
24692 };
24693
24694 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24695     // do nothing for now
24696     init : function(s){
24697     
24698     },
24699     /**
24700      * Called before drag operations to get the current size of the resizing element. 
24701      * @param {Roo.SplitBar} s The SplitBar using this adapter
24702      */
24703      getElementSize : function(s){
24704         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24705             return s.resizingEl.getWidth();
24706         }else{
24707             return s.resizingEl.getHeight();
24708         }
24709     },
24710     
24711     /**
24712      * Called after drag operations to set the size of the resizing element.
24713      * @param {Roo.SplitBar} s The SplitBar using this adapter
24714      * @param {Number} newSize The new size to set
24715      * @param {Function} onComplete A function to be invoked when resizing is complete
24716      */
24717     setElementSize : function(s, newSize, onComplete){
24718         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24719             if(!s.animate){
24720                 s.resizingEl.setWidth(newSize);
24721                 if(onComplete){
24722                     onComplete(s, newSize);
24723                 }
24724             }else{
24725                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24726             }
24727         }else{
24728             
24729             if(!s.animate){
24730                 s.resizingEl.setHeight(newSize);
24731                 if(onComplete){
24732                     onComplete(s, newSize);
24733                 }
24734             }else{
24735                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24736             }
24737         }
24738     }
24739 };
24740
24741 /** 
24742  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24743  * @extends Roo.SplitBar.BasicLayoutAdapter
24744  * Adapter that  moves the splitter element to align with the resized sizing element. 
24745  * Used with an absolute positioned SplitBar.
24746  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24747  * document.body, make sure you assign an id to the body element.
24748  */
24749 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24750     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24751     this.container = Roo.get(container);
24752 };
24753
24754 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24755     init : function(s){
24756         this.basic.init(s);
24757     },
24758     
24759     getElementSize : function(s){
24760         return this.basic.getElementSize(s);
24761     },
24762     
24763     setElementSize : function(s, newSize, onComplete){
24764         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24765     },
24766     
24767     moveSplitter : function(s){
24768         var yes = Roo.SplitBar;
24769         switch(s.placement){
24770             case yes.LEFT:
24771                 s.el.setX(s.resizingEl.getRight());
24772                 break;
24773             case yes.RIGHT:
24774                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24775                 break;
24776             case yes.TOP:
24777                 s.el.setY(s.resizingEl.getBottom());
24778                 break;
24779             case yes.BOTTOM:
24780                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24781                 break;
24782         }
24783     }
24784 };
24785
24786 /**
24787  * Orientation constant - Create a vertical SplitBar
24788  * @static
24789  * @type Number
24790  */
24791 Roo.SplitBar.VERTICAL = 1;
24792
24793 /**
24794  * Orientation constant - Create a horizontal SplitBar
24795  * @static
24796  * @type Number
24797  */
24798 Roo.SplitBar.HORIZONTAL = 2;
24799
24800 /**
24801  * Placement constant - The resizing element is to the left of the splitter element
24802  * @static
24803  * @type Number
24804  */
24805 Roo.SplitBar.LEFT = 1;
24806
24807 /**
24808  * Placement constant - The resizing element is to the right of the splitter element
24809  * @static
24810  * @type Number
24811  */
24812 Roo.SplitBar.RIGHT = 2;
24813
24814 /**
24815  * Placement constant - The resizing element is positioned above the splitter element
24816  * @static
24817  * @type Number
24818  */
24819 Roo.SplitBar.TOP = 3;
24820
24821 /**
24822  * Placement constant - The resizing element is positioned under splitter element
24823  * @static
24824  * @type Number
24825  */
24826 Roo.SplitBar.BOTTOM = 4;
24827 /*
24828  * Based on:
24829  * Ext JS Library 1.1.1
24830  * Copyright(c) 2006-2007, Ext JS, LLC.
24831  *
24832  * Originally Released Under LGPL - original licence link has changed is not relivant.
24833  *
24834  * Fork - LGPL
24835  * <script type="text/javascript">
24836  */
24837
24838 /**
24839  * @class Roo.View
24840  * @extends Roo.util.Observable
24841  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24842  * This class also supports single and multi selection modes. <br>
24843  * Create a data model bound view:
24844  <pre><code>
24845  var store = new Roo.data.Store(...);
24846
24847  var view = new Roo.View({
24848     el : "my-element",
24849     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24850  
24851     singleSelect: true,
24852     selectedClass: "ydataview-selected",
24853     store: store
24854  });
24855
24856  // listen for node click?
24857  view.on("click", function(vw, index, node, e){
24858  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24859  });
24860
24861  // load XML data
24862  dataModel.load("foobar.xml");
24863  </code></pre>
24864  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24865  * <br><br>
24866  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24867  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24868  * 
24869  * Note: old style constructor is still suported (container, template, config)
24870  * 
24871  * @constructor
24872  * Create a new View
24873  * @param {Object} config The config object
24874  * 
24875  */
24876 Roo.View = function(config, depreciated_tpl, depreciated_config){
24877     
24878     this.parent = false;
24879     
24880     if (typeof(depreciated_tpl) == 'undefined') {
24881         // new way.. - universal constructor.
24882         Roo.apply(this, config);
24883         this.el  = Roo.get(this.el);
24884     } else {
24885         // old format..
24886         this.el  = Roo.get(config);
24887         this.tpl = depreciated_tpl;
24888         Roo.apply(this, depreciated_config);
24889     }
24890     this.wrapEl  = this.el.wrap().wrap();
24891     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24892     
24893     
24894     if(typeof(this.tpl) == "string"){
24895         this.tpl = new Roo.Template(this.tpl);
24896     } else {
24897         // support xtype ctors..
24898         this.tpl = new Roo.factory(this.tpl, Roo);
24899     }
24900     
24901     
24902     this.tpl.compile();
24903     
24904     /** @private */
24905     this.addEvents({
24906         /**
24907          * @event beforeclick
24908          * Fires before a click is processed. Returns false to cancel the default action.
24909          * @param {Roo.View} this
24910          * @param {Number} index The index of the target node
24911          * @param {HTMLElement} node The target node
24912          * @param {Roo.EventObject} e The raw event object
24913          */
24914             "beforeclick" : true,
24915         /**
24916          * @event click
24917          * Fires when a template node is clicked.
24918          * @param {Roo.View} this
24919          * @param {Number} index The index of the target node
24920          * @param {HTMLElement} node The target node
24921          * @param {Roo.EventObject} e The raw event object
24922          */
24923             "click" : true,
24924         /**
24925          * @event dblclick
24926          * Fires when a template node is double clicked.
24927          * @param {Roo.View} this
24928          * @param {Number} index The index of the target node
24929          * @param {HTMLElement} node The target node
24930          * @param {Roo.EventObject} e The raw event object
24931          */
24932             "dblclick" : true,
24933         /**
24934          * @event contextmenu
24935          * Fires when a template node is right clicked.
24936          * @param {Roo.View} this
24937          * @param {Number} index The index of the target node
24938          * @param {HTMLElement} node The target node
24939          * @param {Roo.EventObject} e The raw event object
24940          */
24941             "contextmenu" : true,
24942         /**
24943          * @event selectionchange
24944          * Fires when the selected nodes change.
24945          * @param {Roo.View} this
24946          * @param {Array} selections Array of the selected nodes
24947          */
24948             "selectionchange" : true,
24949     
24950         /**
24951          * @event beforeselect
24952          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24953          * @param {Roo.View} this
24954          * @param {HTMLElement} node The node to be selected
24955          * @param {Array} selections Array of currently selected nodes
24956          */
24957             "beforeselect" : true,
24958         /**
24959          * @event preparedata
24960          * Fires on every row to render, to allow you to change the data.
24961          * @param {Roo.View} this
24962          * @param {Object} data to be rendered (change this)
24963          */
24964           "preparedata" : true
24965           
24966           
24967         });
24968
24969
24970
24971     this.el.on({
24972         "click": this.onClick,
24973         "dblclick": this.onDblClick,
24974         "contextmenu": this.onContextMenu,
24975         scope:this
24976     });
24977
24978     this.selections = [];
24979     this.nodes = [];
24980     this.cmp = new Roo.CompositeElementLite([]);
24981     if(this.store){
24982         this.store = Roo.factory(this.store, Roo.data);
24983         this.setStore(this.store, true);
24984     }
24985     
24986     if ( this.footer && this.footer.xtype) {
24987            
24988          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24989         
24990         this.footer.dataSource = this.store
24991         this.footer.container = fctr;
24992         this.footer = Roo.factory(this.footer, Roo);
24993         fctr.insertFirst(this.el);
24994         
24995         // this is a bit insane - as the paging toolbar seems to detach the el..
24996 //        dom.parentNode.parentNode.parentNode
24997          // they get detached?
24998     }
24999     
25000     
25001     Roo.View.superclass.constructor.call(this);
25002     
25003     
25004 };
25005
25006 Roo.extend(Roo.View, Roo.util.Observable, {
25007     
25008      /**
25009      * @cfg {Roo.data.Store} store Data store to load data from.
25010      */
25011     store : false,
25012     
25013     /**
25014      * @cfg {String|Roo.Element} el The container element.
25015      */
25016     el : '',
25017     
25018     /**
25019      * @cfg {String|Roo.Template} tpl The template used by this View 
25020      */
25021     tpl : false,
25022     /**
25023      * @cfg {String} dataName the named area of the template to use as the data area
25024      *                          Works with domtemplates roo-name="name"
25025      */
25026     dataName: false,
25027     /**
25028      * @cfg {String} selectedClass The css class to add to selected nodes
25029      */
25030     selectedClass : "x-view-selected",
25031      /**
25032      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25033      */
25034     emptyText : "",
25035     
25036     /**
25037      * @cfg {String} text to display on mask (default Loading)
25038      */
25039     mask : false,
25040     /**
25041      * @cfg {Boolean} multiSelect Allow multiple selection
25042      */
25043     multiSelect : false,
25044     /**
25045      * @cfg {Boolean} singleSelect Allow single selection
25046      */
25047     singleSelect:  false,
25048     
25049     /**
25050      * @cfg {Boolean} toggleSelect - selecting 
25051      */
25052     toggleSelect : false,
25053     
25054     /**
25055      * @cfg {Boolean} tickable - selecting 
25056      */
25057     tickable : false,
25058     
25059     /**
25060      * Returns the element this view is bound to.
25061      * @return {Roo.Element}
25062      */
25063     getEl : function(){
25064         return this.wrapEl;
25065     },
25066     
25067     
25068
25069     /**
25070      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25071      */
25072     refresh : function(){
25073         //Roo.log('refresh');
25074         var t = this.tpl;
25075         
25076         // if we are using something like 'domtemplate', then
25077         // the what gets used is:
25078         // t.applySubtemplate(NAME, data, wrapping data..)
25079         // the outer template then get' applied with
25080         //     the store 'extra data'
25081         // and the body get's added to the
25082         //      roo-name="data" node?
25083         //      <span class='roo-tpl-{name}'></span> ?????
25084         
25085         
25086         
25087         this.clearSelections();
25088         this.el.update("");
25089         var html = [];
25090         var records = this.store.getRange();
25091         if(records.length < 1) {
25092             
25093             // is this valid??  = should it render a template??
25094             
25095             this.el.update(this.emptyText);
25096             return;
25097         }
25098         var el = this.el;
25099         if (this.dataName) {
25100             this.el.update(t.apply(this.store.meta)); //????
25101             el = this.el.child('.roo-tpl-' + this.dataName);
25102         }
25103         
25104         for(var i = 0, len = records.length; i < len; i++){
25105             var data = this.prepareData(records[i].data, i, records[i]);
25106             this.fireEvent("preparedata", this, data, i, records[i]);
25107             
25108             var d = Roo.apply({}, data);
25109             
25110             if(this.tickable){
25111                 Roo.apply(d, {'roo-id' : Roo.id()});
25112                 
25113                 var _this = this;
25114             
25115                 Roo.each(this.parent.item, function(item){
25116                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25117                         return;
25118                     }
25119                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25120                 });
25121             }
25122             
25123             html[html.length] = Roo.util.Format.trim(
25124                 this.dataName ?
25125                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25126                     t.apply(d)
25127             );
25128         }
25129         
25130         
25131         
25132         el.update(html.join(""));
25133         this.nodes = el.dom.childNodes;
25134         this.updateIndexes(0);
25135     },
25136     
25137
25138     /**
25139      * Function to override to reformat the data that is sent to
25140      * the template for each node.
25141      * DEPRICATED - use the preparedata event handler.
25142      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25143      * a JSON object for an UpdateManager bound view).
25144      */
25145     prepareData : function(data, index, record)
25146     {
25147         this.fireEvent("preparedata", this, data, index, record);
25148         return data;
25149     },
25150
25151     onUpdate : function(ds, record){
25152         // Roo.log('on update');   
25153         this.clearSelections();
25154         var index = this.store.indexOf(record);
25155         var n = this.nodes[index];
25156         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25157         n.parentNode.removeChild(n);
25158         this.updateIndexes(index, index);
25159     },
25160
25161     
25162     
25163 // --------- FIXME     
25164     onAdd : function(ds, records, index)
25165     {
25166         //Roo.log(['on Add', ds, records, index] );        
25167         this.clearSelections();
25168         if(this.nodes.length == 0){
25169             this.refresh();
25170             return;
25171         }
25172         var n = this.nodes[index];
25173         for(var i = 0, len = records.length; i < len; i++){
25174             var d = this.prepareData(records[i].data, i, records[i]);
25175             if(n){
25176                 this.tpl.insertBefore(n, d);
25177             }else{
25178                 
25179                 this.tpl.append(this.el, d);
25180             }
25181         }
25182         this.updateIndexes(index);
25183     },
25184
25185     onRemove : function(ds, record, index){
25186        // Roo.log('onRemove');
25187         this.clearSelections();
25188         var el = this.dataName  ?
25189             this.el.child('.roo-tpl-' + this.dataName) :
25190             this.el; 
25191         
25192         el.dom.removeChild(this.nodes[index]);
25193         this.updateIndexes(index);
25194     },
25195
25196     /**
25197      * Refresh an individual node.
25198      * @param {Number} index
25199      */
25200     refreshNode : function(index){
25201         this.onUpdate(this.store, this.store.getAt(index));
25202     },
25203
25204     updateIndexes : function(startIndex, endIndex){
25205         var ns = this.nodes;
25206         startIndex = startIndex || 0;
25207         endIndex = endIndex || ns.length - 1;
25208         for(var i = startIndex; i <= endIndex; i++){
25209             ns[i].nodeIndex = i;
25210         }
25211     },
25212
25213     /**
25214      * Changes the data store this view uses and refresh the view.
25215      * @param {Store} store
25216      */
25217     setStore : function(store, initial){
25218         if(!initial && this.store){
25219             this.store.un("datachanged", this.refresh);
25220             this.store.un("add", this.onAdd);
25221             this.store.un("remove", this.onRemove);
25222             this.store.un("update", this.onUpdate);
25223             this.store.un("clear", this.refresh);
25224             this.store.un("beforeload", this.onBeforeLoad);
25225             this.store.un("load", this.onLoad);
25226             this.store.un("loadexception", this.onLoad);
25227         }
25228         if(store){
25229           
25230             store.on("datachanged", this.refresh, this);
25231             store.on("add", this.onAdd, this);
25232             store.on("remove", this.onRemove, this);
25233             store.on("update", this.onUpdate, this);
25234             store.on("clear", this.refresh, this);
25235             store.on("beforeload", this.onBeforeLoad, this);
25236             store.on("load", this.onLoad, this);
25237             store.on("loadexception", this.onLoad, this);
25238         }
25239         
25240         if(store){
25241             this.refresh();
25242         }
25243     },
25244     /**
25245      * onbeforeLoad - masks the loading area.
25246      *
25247      */
25248     onBeforeLoad : function(store,opts)
25249     {
25250          //Roo.log('onBeforeLoad');   
25251         if (!opts.add) {
25252             this.el.update("");
25253         }
25254         this.el.mask(this.mask ? this.mask : "Loading" ); 
25255     },
25256     onLoad : function ()
25257     {
25258         this.el.unmask();
25259     },
25260     
25261
25262     /**
25263      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25264      * @param {HTMLElement} node
25265      * @return {HTMLElement} The template node
25266      */
25267     findItemFromChild : function(node){
25268         var el = this.dataName  ?
25269             this.el.child('.roo-tpl-' + this.dataName,true) :
25270             this.el.dom; 
25271         
25272         if(!node || node.parentNode == el){
25273                     return node;
25274             }
25275             var p = node.parentNode;
25276             while(p && p != el){
25277             if(p.parentNode == el){
25278                 return p;
25279             }
25280             p = p.parentNode;
25281         }
25282             return null;
25283     },
25284
25285     /** @ignore */
25286     onClick : function(e){
25287         var item = this.findItemFromChild(e.getTarget());
25288         if(item){
25289             var index = this.indexOf(item);
25290             if(this.onItemClick(item, index, e) !== false){
25291                 this.fireEvent("click", this, index, item, e);
25292             }
25293         }else{
25294             this.clearSelections();
25295         }
25296     },
25297
25298     /** @ignore */
25299     onContextMenu : function(e){
25300         var item = this.findItemFromChild(e.getTarget());
25301         if(item){
25302             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25303         }
25304     },
25305
25306     /** @ignore */
25307     onDblClick : function(e){
25308         var item = this.findItemFromChild(e.getTarget());
25309         if(item){
25310             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25311         }
25312     },
25313
25314     onItemClick : function(item, index, e)
25315     {
25316         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25317             return false;
25318         }
25319         if (this.toggleSelect) {
25320             var m = this.isSelected(item) ? 'unselect' : 'select';
25321             //Roo.log(m);
25322             var _t = this;
25323             _t[m](item, true, false);
25324             return true;
25325         }
25326         if(this.multiSelect || this.singleSelect){
25327             if(this.multiSelect && e.shiftKey && this.lastSelection){
25328                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25329             }else{
25330                 this.select(item, this.multiSelect && e.ctrlKey);
25331                 this.lastSelection = item;
25332             }
25333             
25334             if(!this.tickable){
25335                 e.preventDefault();
25336             }
25337             
25338         }
25339         return true;
25340     },
25341
25342     /**
25343      * Get the number of selected nodes.
25344      * @return {Number}
25345      */
25346     getSelectionCount : function(){
25347         return this.selections.length;
25348     },
25349
25350     /**
25351      * Get the currently selected nodes.
25352      * @return {Array} An array of HTMLElements
25353      */
25354     getSelectedNodes : function(){
25355         return this.selections;
25356     },
25357
25358     /**
25359      * Get the indexes of the selected nodes.
25360      * @return {Array}
25361      */
25362     getSelectedIndexes : function(){
25363         var indexes = [], s = this.selections;
25364         for(var i = 0, len = s.length; i < len; i++){
25365             indexes.push(s[i].nodeIndex);
25366         }
25367         return indexes;
25368     },
25369
25370     /**
25371      * Clear all selections
25372      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25373      */
25374     clearSelections : function(suppressEvent){
25375         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25376             this.cmp.elements = this.selections;
25377             this.cmp.removeClass(this.selectedClass);
25378             this.selections = [];
25379             if(!suppressEvent){
25380                 this.fireEvent("selectionchange", this, this.selections);
25381             }
25382         }
25383     },
25384
25385     /**
25386      * Returns true if the passed node is selected
25387      * @param {HTMLElement/Number} node The node or node index
25388      * @return {Boolean}
25389      */
25390     isSelected : function(node){
25391         var s = this.selections;
25392         if(s.length < 1){
25393             return false;
25394         }
25395         node = this.getNode(node);
25396         return s.indexOf(node) !== -1;
25397     },
25398
25399     /**
25400      * Selects nodes.
25401      * @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
25402      * @param {Boolean} keepExisting (optional) true to keep existing selections
25403      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25404      */
25405     select : function(nodeInfo, keepExisting, suppressEvent){
25406         if(nodeInfo instanceof Array){
25407             if(!keepExisting){
25408                 this.clearSelections(true);
25409             }
25410             for(var i = 0, len = nodeInfo.length; i < len; i++){
25411                 this.select(nodeInfo[i], true, true);
25412             }
25413             return;
25414         } 
25415         var node = this.getNode(nodeInfo);
25416         if(!node || this.isSelected(node)){
25417             return; // already selected.
25418         }
25419         if(!keepExisting){
25420             this.clearSelections(true);
25421         }
25422         
25423         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25424             Roo.fly(node).addClass(this.selectedClass);
25425             this.selections.push(node);
25426             if(!suppressEvent){
25427                 this.fireEvent("selectionchange", this, this.selections);
25428             }
25429         }
25430         
25431         
25432     },
25433       /**
25434      * Unselects nodes.
25435      * @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
25436      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25437      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25438      */
25439     unselect : function(nodeInfo, keepExisting, suppressEvent)
25440     {
25441         if(nodeInfo instanceof Array){
25442             Roo.each(this.selections, function(s) {
25443                 this.unselect(s, nodeInfo);
25444             }, this);
25445             return;
25446         }
25447         var node = this.getNode(nodeInfo);
25448         if(!node || !this.isSelected(node)){
25449             //Roo.log("not selected");
25450             return; // not selected.
25451         }
25452         // fireevent???
25453         var ns = [];
25454         Roo.each(this.selections, function(s) {
25455             if (s == node ) {
25456                 Roo.fly(node).removeClass(this.selectedClass);
25457
25458                 return;
25459             }
25460             ns.push(s);
25461         },this);
25462         
25463         this.selections= ns;
25464         this.fireEvent("selectionchange", this, this.selections);
25465     },
25466
25467     /**
25468      * Gets a template node.
25469      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25470      * @return {HTMLElement} The node or null if it wasn't found
25471      */
25472     getNode : function(nodeInfo){
25473         if(typeof nodeInfo == "string"){
25474             return document.getElementById(nodeInfo);
25475         }else if(typeof nodeInfo == "number"){
25476             return this.nodes[nodeInfo];
25477         }
25478         return nodeInfo;
25479     },
25480
25481     /**
25482      * Gets a range template nodes.
25483      * @param {Number} startIndex
25484      * @param {Number} endIndex
25485      * @return {Array} An array of nodes
25486      */
25487     getNodes : function(start, end){
25488         var ns = this.nodes;
25489         start = start || 0;
25490         end = typeof end == "undefined" ? ns.length - 1 : end;
25491         var nodes = [];
25492         if(start <= end){
25493             for(var i = start; i <= end; i++){
25494                 nodes.push(ns[i]);
25495             }
25496         } else{
25497             for(var i = start; i >= end; i--){
25498                 nodes.push(ns[i]);
25499             }
25500         }
25501         return nodes;
25502     },
25503
25504     /**
25505      * Finds the index of the passed node
25506      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25507      * @return {Number} The index of the node or -1
25508      */
25509     indexOf : function(node){
25510         node = this.getNode(node);
25511         if(typeof node.nodeIndex == "number"){
25512             return node.nodeIndex;
25513         }
25514         var ns = this.nodes;
25515         for(var i = 0, len = ns.length; i < len; i++){
25516             if(ns[i] == node){
25517                 return i;
25518             }
25519         }
25520         return -1;
25521     }
25522 });
25523 /*
25524  * Based on:
25525  * Ext JS Library 1.1.1
25526  * Copyright(c) 2006-2007, Ext JS, LLC.
25527  *
25528  * Originally Released Under LGPL - original licence link has changed is not relivant.
25529  *
25530  * Fork - LGPL
25531  * <script type="text/javascript">
25532  */
25533
25534 /**
25535  * @class Roo.JsonView
25536  * @extends Roo.View
25537  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25538 <pre><code>
25539 var view = new Roo.JsonView({
25540     container: "my-element",
25541     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25542     multiSelect: true, 
25543     jsonRoot: "data" 
25544 });
25545
25546 // listen for node click?
25547 view.on("click", function(vw, index, node, e){
25548     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25549 });
25550
25551 // direct load of JSON data
25552 view.load("foobar.php");
25553
25554 // Example from my blog list
25555 var tpl = new Roo.Template(
25556     '&lt;div class="entry"&gt;' +
25557     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25558     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25559     "&lt;/div&gt;&lt;hr /&gt;"
25560 );
25561
25562 var moreView = new Roo.JsonView({
25563     container :  "entry-list", 
25564     template : tpl,
25565     jsonRoot: "posts"
25566 });
25567 moreView.on("beforerender", this.sortEntries, this);
25568 moreView.load({
25569     url: "/blog/get-posts.php",
25570     params: "allposts=true",
25571     text: "Loading Blog Entries..."
25572 });
25573 </code></pre>
25574
25575 * Note: old code is supported with arguments : (container, template, config)
25576
25577
25578  * @constructor
25579  * Create a new JsonView
25580  * 
25581  * @param {Object} config The config object
25582  * 
25583  */
25584 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25585     
25586     
25587     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25588
25589     var um = this.el.getUpdateManager();
25590     um.setRenderer(this);
25591     um.on("update", this.onLoad, this);
25592     um.on("failure", this.onLoadException, this);
25593
25594     /**
25595      * @event beforerender
25596      * Fires before rendering of the downloaded JSON data.
25597      * @param {Roo.JsonView} this
25598      * @param {Object} data The JSON data loaded
25599      */
25600     /**
25601      * @event load
25602      * Fires when data is loaded.
25603      * @param {Roo.JsonView} this
25604      * @param {Object} data The JSON data loaded
25605      * @param {Object} response The raw Connect response object
25606      */
25607     /**
25608      * @event loadexception
25609      * Fires when loading fails.
25610      * @param {Roo.JsonView} this
25611      * @param {Object} response The raw Connect response object
25612      */
25613     this.addEvents({
25614         'beforerender' : true,
25615         'load' : true,
25616         'loadexception' : true
25617     });
25618 };
25619 Roo.extend(Roo.JsonView, Roo.View, {
25620     /**
25621      * @type {String} The root property in the loaded JSON object that contains the data
25622      */
25623     jsonRoot : "",
25624
25625     /**
25626      * Refreshes the view.
25627      */
25628     refresh : function(){
25629         this.clearSelections();
25630         this.el.update("");
25631         var html = [];
25632         var o = this.jsonData;
25633         if(o && o.length > 0){
25634             for(var i = 0, len = o.length; i < len; i++){
25635                 var data = this.prepareData(o[i], i, o);
25636                 html[html.length] = this.tpl.apply(data);
25637             }
25638         }else{
25639             html.push(this.emptyText);
25640         }
25641         this.el.update(html.join(""));
25642         this.nodes = this.el.dom.childNodes;
25643         this.updateIndexes(0);
25644     },
25645
25646     /**
25647      * 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.
25648      * @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:
25649      <pre><code>
25650      view.load({
25651          url: "your-url.php",
25652          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25653          callback: yourFunction,
25654          scope: yourObject, //(optional scope)
25655          discardUrl: false,
25656          nocache: false,
25657          text: "Loading...",
25658          timeout: 30,
25659          scripts: false
25660      });
25661      </code></pre>
25662      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25663      * 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.
25664      * @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}
25665      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25666      * @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.
25667      */
25668     load : function(){
25669         var um = this.el.getUpdateManager();
25670         um.update.apply(um, arguments);
25671     },
25672
25673     render : function(el, response){
25674         this.clearSelections();
25675         this.el.update("");
25676         var o;
25677         try{
25678             o = Roo.util.JSON.decode(response.responseText);
25679             if(this.jsonRoot){
25680                 
25681                 o = o[this.jsonRoot];
25682             }
25683         } catch(e){
25684         }
25685         /**
25686          * The current JSON data or null
25687          */
25688         this.jsonData = o;
25689         this.beforeRender();
25690         this.refresh();
25691     },
25692
25693 /**
25694  * Get the number of records in the current JSON dataset
25695  * @return {Number}
25696  */
25697     getCount : function(){
25698         return this.jsonData ? this.jsonData.length : 0;
25699     },
25700
25701 /**
25702  * Returns the JSON object for the specified node(s)
25703  * @param {HTMLElement/Array} node The node or an array of nodes
25704  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25705  * you get the JSON object for the node
25706  */
25707     getNodeData : function(node){
25708         if(node instanceof Array){
25709             var data = [];
25710             for(var i = 0, len = node.length; i < len; i++){
25711                 data.push(this.getNodeData(node[i]));
25712             }
25713             return data;
25714         }
25715         return this.jsonData[this.indexOf(node)] || null;
25716     },
25717
25718     beforeRender : function(){
25719         this.snapshot = this.jsonData;
25720         if(this.sortInfo){
25721             this.sort.apply(this, this.sortInfo);
25722         }
25723         this.fireEvent("beforerender", this, this.jsonData);
25724     },
25725
25726     onLoad : function(el, o){
25727         this.fireEvent("load", this, this.jsonData, o);
25728     },
25729
25730     onLoadException : function(el, o){
25731         this.fireEvent("loadexception", this, o);
25732     },
25733
25734 /**
25735  * Filter the data by a specific property.
25736  * @param {String} property A property on your JSON objects
25737  * @param {String/RegExp} value Either string that the property values
25738  * should start with, or a RegExp to test against the property
25739  */
25740     filter : function(property, value){
25741         if(this.jsonData){
25742             var data = [];
25743             var ss = this.snapshot;
25744             if(typeof value == "string"){
25745                 var vlen = value.length;
25746                 if(vlen == 0){
25747                     this.clearFilter();
25748                     return;
25749                 }
25750                 value = value.toLowerCase();
25751                 for(var i = 0, len = ss.length; i < len; i++){
25752                     var o = ss[i];
25753                     if(o[property].substr(0, vlen).toLowerCase() == value){
25754                         data.push(o);
25755                     }
25756                 }
25757             } else if(value.exec){ // regex?
25758                 for(var i = 0, len = ss.length; i < len; i++){
25759                     var o = ss[i];
25760                     if(value.test(o[property])){
25761                         data.push(o);
25762                     }
25763                 }
25764             } else{
25765                 return;
25766             }
25767             this.jsonData = data;
25768             this.refresh();
25769         }
25770     },
25771
25772 /**
25773  * Filter by a function. The passed function will be called with each
25774  * object in the current dataset. If the function returns true the value is kept,
25775  * otherwise it is filtered.
25776  * @param {Function} fn
25777  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25778  */
25779     filterBy : function(fn, scope){
25780         if(this.jsonData){
25781             var data = [];
25782             var ss = this.snapshot;
25783             for(var i = 0, len = ss.length; i < len; i++){
25784                 var o = ss[i];
25785                 if(fn.call(scope || this, o)){
25786                     data.push(o);
25787                 }
25788             }
25789             this.jsonData = data;
25790             this.refresh();
25791         }
25792     },
25793
25794 /**
25795  * Clears the current filter.
25796  */
25797     clearFilter : function(){
25798         if(this.snapshot && this.jsonData != this.snapshot){
25799             this.jsonData = this.snapshot;
25800             this.refresh();
25801         }
25802     },
25803
25804
25805 /**
25806  * Sorts the data for this view and refreshes it.
25807  * @param {String} property A property on your JSON objects to sort on
25808  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25809  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25810  */
25811     sort : function(property, dir, sortType){
25812         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25813         if(this.jsonData){
25814             var p = property;
25815             var dsc = dir && dir.toLowerCase() == "desc";
25816             var f = function(o1, o2){
25817                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25818                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25819                 ;
25820                 if(v1 < v2){
25821                     return dsc ? +1 : -1;
25822                 } else if(v1 > v2){
25823                     return dsc ? -1 : +1;
25824                 } else{
25825                     return 0;
25826                 }
25827             };
25828             this.jsonData.sort(f);
25829             this.refresh();
25830             if(this.jsonData != this.snapshot){
25831                 this.snapshot.sort(f);
25832             }
25833         }
25834     }
25835 });/*
25836  * Based on:
25837  * Ext JS Library 1.1.1
25838  * Copyright(c) 2006-2007, Ext JS, LLC.
25839  *
25840  * Originally Released Under LGPL - original licence link has changed is not relivant.
25841  *
25842  * Fork - LGPL
25843  * <script type="text/javascript">
25844  */
25845  
25846
25847 /**
25848  * @class Roo.ColorPalette
25849  * @extends Roo.Component
25850  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25851  * Here's an example of typical usage:
25852  * <pre><code>
25853 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25854 cp.render('my-div');
25855
25856 cp.on('select', function(palette, selColor){
25857     // do something with selColor
25858 });
25859 </code></pre>
25860  * @constructor
25861  * Create a new ColorPalette
25862  * @param {Object} config The config object
25863  */
25864 Roo.ColorPalette = function(config){
25865     Roo.ColorPalette.superclass.constructor.call(this, config);
25866     this.addEvents({
25867         /**
25868              * @event select
25869              * Fires when a color is selected
25870              * @param {ColorPalette} this
25871              * @param {String} color The 6-digit color hex code (without the # symbol)
25872              */
25873         select: true
25874     });
25875
25876     if(this.handler){
25877         this.on("select", this.handler, this.scope, true);
25878     }
25879 };
25880 Roo.extend(Roo.ColorPalette, Roo.Component, {
25881     /**
25882      * @cfg {String} itemCls
25883      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25884      */
25885     itemCls : "x-color-palette",
25886     /**
25887      * @cfg {String} value
25888      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25889      * the hex codes are case-sensitive.
25890      */
25891     value : null,
25892     clickEvent:'click',
25893     // private
25894     ctype: "Roo.ColorPalette",
25895
25896     /**
25897      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25898      */
25899     allowReselect : false,
25900
25901     /**
25902      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25903      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25904      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25905      * of colors with the width setting until the box is symmetrical.</p>
25906      * <p>You can override individual colors if needed:</p>
25907      * <pre><code>
25908 var cp = new Roo.ColorPalette();
25909 cp.colors[0] = "FF0000";  // change the first box to red
25910 </code></pre>
25911
25912 Or you can provide a custom array of your own for complete control:
25913 <pre><code>
25914 var cp = new Roo.ColorPalette();
25915 cp.colors = ["000000", "993300", "333300"];
25916 </code></pre>
25917      * @type Array
25918      */
25919     colors : [
25920         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25921         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25922         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25923         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25924         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25925     ],
25926
25927     // private
25928     onRender : function(container, position){
25929         var t = new Roo.MasterTemplate(
25930             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25931         );
25932         var c = this.colors;
25933         for(var i = 0, len = c.length; i < len; i++){
25934             t.add([c[i]]);
25935         }
25936         var el = document.createElement("div");
25937         el.className = this.itemCls;
25938         t.overwrite(el);
25939         container.dom.insertBefore(el, position);
25940         this.el = Roo.get(el);
25941         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25942         if(this.clickEvent != 'click'){
25943             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25944         }
25945     },
25946
25947     // private
25948     afterRender : function(){
25949         Roo.ColorPalette.superclass.afterRender.call(this);
25950         if(this.value){
25951             var s = this.value;
25952             this.value = null;
25953             this.select(s);
25954         }
25955     },
25956
25957     // private
25958     handleClick : function(e, t){
25959         e.preventDefault();
25960         if(!this.disabled){
25961             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25962             this.select(c.toUpperCase());
25963         }
25964     },
25965
25966     /**
25967      * Selects the specified color in the palette (fires the select event)
25968      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25969      */
25970     select : function(color){
25971         color = color.replace("#", "");
25972         if(color != this.value || this.allowReselect){
25973             var el = this.el;
25974             if(this.value){
25975                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25976             }
25977             el.child("a.color-"+color).addClass("x-color-palette-sel");
25978             this.value = color;
25979             this.fireEvent("select", this, color);
25980         }
25981     }
25982 });/*
25983  * Based on:
25984  * Ext JS Library 1.1.1
25985  * Copyright(c) 2006-2007, Ext JS, LLC.
25986  *
25987  * Originally Released Under LGPL - original licence link has changed is not relivant.
25988  *
25989  * Fork - LGPL
25990  * <script type="text/javascript">
25991  */
25992  
25993 /**
25994  * @class Roo.DatePicker
25995  * @extends Roo.Component
25996  * Simple date picker class.
25997  * @constructor
25998  * Create a new DatePicker
25999  * @param {Object} config The config object
26000  */
26001 Roo.DatePicker = function(config){
26002     Roo.DatePicker.superclass.constructor.call(this, config);
26003
26004     this.value = config && config.value ?
26005                  config.value.clearTime() : new Date().clearTime();
26006
26007     this.addEvents({
26008         /**
26009              * @event select
26010              * Fires when a date is selected
26011              * @param {DatePicker} this
26012              * @param {Date} date The selected date
26013              */
26014         'select': true,
26015         /**
26016              * @event monthchange
26017              * Fires when the displayed month changes 
26018              * @param {DatePicker} this
26019              * @param {Date} date The selected month
26020              */
26021         'monthchange': true
26022     });
26023
26024     if(this.handler){
26025         this.on("select", this.handler,  this.scope || this);
26026     }
26027     // build the disabledDatesRE
26028     if(!this.disabledDatesRE && this.disabledDates){
26029         var dd = this.disabledDates;
26030         var re = "(?:";
26031         for(var i = 0; i < dd.length; i++){
26032             re += dd[i];
26033             if(i != dd.length-1) re += "|";
26034         }
26035         this.disabledDatesRE = new RegExp(re + ")");
26036     }
26037 };
26038
26039 Roo.extend(Roo.DatePicker, Roo.Component, {
26040     /**
26041      * @cfg {String} todayText
26042      * The text to display on the button that selects the current date (defaults to "Today")
26043      */
26044     todayText : "Today",
26045     /**
26046      * @cfg {String} okText
26047      * The text to display on the ok button
26048      */
26049     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26050     /**
26051      * @cfg {String} cancelText
26052      * The text to display on the cancel button
26053      */
26054     cancelText : "Cancel",
26055     /**
26056      * @cfg {String} todayTip
26057      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26058      */
26059     todayTip : "{0} (Spacebar)",
26060     /**
26061      * @cfg {Date} minDate
26062      * Minimum allowable date (JavaScript date object, defaults to null)
26063      */
26064     minDate : null,
26065     /**
26066      * @cfg {Date} maxDate
26067      * Maximum allowable date (JavaScript date object, defaults to null)
26068      */
26069     maxDate : null,
26070     /**
26071      * @cfg {String} minText
26072      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26073      */
26074     minText : "This date is before the minimum date",
26075     /**
26076      * @cfg {String} maxText
26077      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26078      */
26079     maxText : "This date is after the maximum date",
26080     /**
26081      * @cfg {String} format
26082      * The default date format string which can be overriden for localization support.  The format must be
26083      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26084      */
26085     format : "m/d/y",
26086     /**
26087      * @cfg {Array} disabledDays
26088      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26089      */
26090     disabledDays : null,
26091     /**
26092      * @cfg {String} disabledDaysText
26093      * The tooltip to display when the date falls on a disabled day (defaults to "")
26094      */
26095     disabledDaysText : "",
26096     /**
26097      * @cfg {RegExp} disabledDatesRE
26098      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26099      */
26100     disabledDatesRE : null,
26101     /**
26102      * @cfg {String} disabledDatesText
26103      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26104      */
26105     disabledDatesText : "",
26106     /**
26107      * @cfg {Boolean} constrainToViewport
26108      * True to constrain the date picker to the viewport (defaults to true)
26109      */
26110     constrainToViewport : true,
26111     /**
26112      * @cfg {Array} monthNames
26113      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26114      */
26115     monthNames : Date.monthNames,
26116     /**
26117      * @cfg {Array} dayNames
26118      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26119      */
26120     dayNames : Date.dayNames,
26121     /**
26122      * @cfg {String} nextText
26123      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26124      */
26125     nextText: 'Next Month (Control+Right)',
26126     /**
26127      * @cfg {String} prevText
26128      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26129      */
26130     prevText: 'Previous Month (Control+Left)',
26131     /**
26132      * @cfg {String} monthYearText
26133      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26134      */
26135     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26136     /**
26137      * @cfg {Number} startDay
26138      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26139      */
26140     startDay : 0,
26141     /**
26142      * @cfg {Bool} showClear
26143      * Show a clear button (usefull for date form elements that can be blank.)
26144      */
26145     
26146     showClear: false,
26147     
26148     /**
26149      * Sets the value of the date field
26150      * @param {Date} value The date to set
26151      */
26152     setValue : function(value){
26153         var old = this.value;
26154         
26155         if (typeof(value) == 'string') {
26156          
26157             value = Date.parseDate(value, this.format);
26158         }
26159         if (!value) {
26160             value = new Date();
26161         }
26162         
26163         this.value = value.clearTime(true);
26164         if(this.el){
26165             this.update(this.value);
26166         }
26167     },
26168
26169     /**
26170      * Gets the current selected value of the date field
26171      * @return {Date} The selected date
26172      */
26173     getValue : function(){
26174         return this.value;
26175     },
26176
26177     // private
26178     focus : function(){
26179         if(this.el){
26180             this.update(this.activeDate);
26181         }
26182     },
26183
26184     // privateval
26185     onRender : function(container, position){
26186         
26187         var m = [
26188              '<table cellspacing="0">',
26189                 '<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>',
26190                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26191         var dn = this.dayNames;
26192         for(var i = 0; i < 7; i++){
26193             var d = this.startDay+i;
26194             if(d > 6){
26195                 d = d-7;
26196             }
26197             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26198         }
26199         m[m.length] = "</tr></thead><tbody><tr>";
26200         for(var i = 0; i < 42; i++) {
26201             if(i % 7 == 0 && i != 0){
26202                 m[m.length] = "</tr><tr>";
26203             }
26204             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26205         }
26206         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26207             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26208
26209         var el = document.createElement("div");
26210         el.className = "x-date-picker";
26211         el.innerHTML = m.join("");
26212
26213         container.dom.insertBefore(el, position);
26214
26215         this.el = Roo.get(el);
26216         this.eventEl = Roo.get(el.firstChild);
26217
26218         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26219             handler: this.showPrevMonth,
26220             scope: this,
26221             preventDefault:true,
26222             stopDefault:true
26223         });
26224
26225         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26226             handler: this.showNextMonth,
26227             scope: this,
26228             preventDefault:true,
26229             stopDefault:true
26230         });
26231
26232         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26233
26234         this.monthPicker = this.el.down('div.x-date-mp');
26235         this.monthPicker.enableDisplayMode('block');
26236         
26237         var kn = new Roo.KeyNav(this.eventEl, {
26238             "left" : function(e){
26239                 e.ctrlKey ?
26240                     this.showPrevMonth() :
26241                     this.update(this.activeDate.add("d", -1));
26242             },
26243
26244             "right" : function(e){
26245                 e.ctrlKey ?
26246                     this.showNextMonth() :
26247                     this.update(this.activeDate.add("d", 1));
26248             },
26249
26250             "up" : function(e){
26251                 e.ctrlKey ?
26252                     this.showNextYear() :
26253                     this.update(this.activeDate.add("d", -7));
26254             },
26255
26256             "down" : function(e){
26257                 e.ctrlKey ?
26258                     this.showPrevYear() :
26259                     this.update(this.activeDate.add("d", 7));
26260             },
26261
26262             "pageUp" : function(e){
26263                 this.showNextMonth();
26264             },
26265
26266             "pageDown" : function(e){
26267                 this.showPrevMonth();
26268             },
26269
26270             "enter" : function(e){
26271                 e.stopPropagation();
26272                 return true;
26273             },
26274
26275             scope : this
26276         });
26277
26278         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26279
26280         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26281
26282         this.el.unselectable();
26283         
26284         this.cells = this.el.select("table.x-date-inner tbody td");
26285         this.textNodes = this.el.query("table.x-date-inner tbody span");
26286
26287         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26288             text: "&#160;",
26289             tooltip: this.monthYearText
26290         });
26291
26292         this.mbtn.on('click', this.showMonthPicker, this);
26293         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26294
26295
26296         var today = (new Date()).dateFormat(this.format);
26297         
26298         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26299         if (this.showClear) {
26300             baseTb.add( new Roo.Toolbar.Fill());
26301         }
26302         baseTb.add({
26303             text: String.format(this.todayText, today),
26304             tooltip: String.format(this.todayTip, today),
26305             handler: this.selectToday,
26306             scope: this
26307         });
26308         
26309         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26310             
26311         //});
26312         if (this.showClear) {
26313             
26314             baseTb.add( new Roo.Toolbar.Fill());
26315             baseTb.add({
26316                 text: '&#160;',
26317                 cls: 'x-btn-icon x-btn-clear',
26318                 handler: function() {
26319                     //this.value = '';
26320                     this.fireEvent("select", this, '');
26321                 },
26322                 scope: this
26323             });
26324         }
26325         
26326         
26327         if(Roo.isIE){
26328             this.el.repaint();
26329         }
26330         this.update(this.value);
26331     },
26332
26333     createMonthPicker : function(){
26334         if(!this.monthPicker.dom.firstChild){
26335             var buf = ['<table border="0" cellspacing="0">'];
26336             for(var i = 0; i < 6; i++){
26337                 buf.push(
26338                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26339                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26340                     i == 0 ?
26341                     '<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>' :
26342                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26343                 );
26344             }
26345             buf.push(
26346                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26347                     this.okText,
26348                     '</button><button type="button" class="x-date-mp-cancel">',
26349                     this.cancelText,
26350                     '</button></td></tr>',
26351                 '</table>'
26352             );
26353             this.monthPicker.update(buf.join(''));
26354             this.monthPicker.on('click', this.onMonthClick, this);
26355             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26356
26357             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26358             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26359
26360             this.mpMonths.each(function(m, a, i){
26361                 i += 1;
26362                 if((i%2) == 0){
26363                     m.dom.xmonth = 5 + Math.round(i * .5);
26364                 }else{
26365                     m.dom.xmonth = Math.round((i-1) * .5);
26366                 }
26367             });
26368         }
26369     },
26370
26371     showMonthPicker : function(){
26372         this.createMonthPicker();
26373         var size = this.el.getSize();
26374         this.monthPicker.setSize(size);
26375         this.monthPicker.child('table').setSize(size);
26376
26377         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26378         this.updateMPMonth(this.mpSelMonth);
26379         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26380         this.updateMPYear(this.mpSelYear);
26381
26382         this.monthPicker.slideIn('t', {duration:.2});
26383     },
26384
26385     updateMPYear : function(y){
26386         this.mpyear = y;
26387         var ys = this.mpYears.elements;
26388         for(var i = 1; i <= 10; i++){
26389             var td = ys[i-1], y2;
26390             if((i%2) == 0){
26391                 y2 = y + Math.round(i * .5);
26392                 td.firstChild.innerHTML = y2;
26393                 td.xyear = y2;
26394             }else{
26395                 y2 = y - (5-Math.round(i * .5));
26396                 td.firstChild.innerHTML = y2;
26397                 td.xyear = y2;
26398             }
26399             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26400         }
26401     },
26402
26403     updateMPMonth : function(sm){
26404         this.mpMonths.each(function(m, a, i){
26405             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26406         });
26407     },
26408
26409     selectMPMonth: function(m){
26410         
26411     },
26412
26413     onMonthClick : function(e, t){
26414         e.stopEvent();
26415         var el = new Roo.Element(t), pn;
26416         if(el.is('button.x-date-mp-cancel')){
26417             this.hideMonthPicker();
26418         }
26419         else if(el.is('button.x-date-mp-ok')){
26420             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26421             this.hideMonthPicker();
26422         }
26423         else if(pn = el.up('td.x-date-mp-month', 2)){
26424             this.mpMonths.removeClass('x-date-mp-sel');
26425             pn.addClass('x-date-mp-sel');
26426             this.mpSelMonth = pn.dom.xmonth;
26427         }
26428         else if(pn = el.up('td.x-date-mp-year', 2)){
26429             this.mpYears.removeClass('x-date-mp-sel');
26430             pn.addClass('x-date-mp-sel');
26431             this.mpSelYear = pn.dom.xyear;
26432         }
26433         else if(el.is('a.x-date-mp-prev')){
26434             this.updateMPYear(this.mpyear-10);
26435         }
26436         else if(el.is('a.x-date-mp-next')){
26437             this.updateMPYear(this.mpyear+10);
26438         }
26439     },
26440
26441     onMonthDblClick : function(e, t){
26442         e.stopEvent();
26443         var el = new Roo.Element(t), pn;
26444         if(pn = el.up('td.x-date-mp-month', 2)){
26445             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26446             this.hideMonthPicker();
26447         }
26448         else if(pn = el.up('td.x-date-mp-year', 2)){
26449             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26450             this.hideMonthPicker();
26451         }
26452     },
26453
26454     hideMonthPicker : function(disableAnim){
26455         if(this.monthPicker){
26456             if(disableAnim === true){
26457                 this.monthPicker.hide();
26458             }else{
26459                 this.monthPicker.slideOut('t', {duration:.2});
26460             }
26461         }
26462     },
26463
26464     // private
26465     showPrevMonth : function(e){
26466         this.update(this.activeDate.add("mo", -1));
26467     },
26468
26469     // private
26470     showNextMonth : function(e){
26471         this.update(this.activeDate.add("mo", 1));
26472     },
26473
26474     // private
26475     showPrevYear : function(){
26476         this.update(this.activeDate.add("y", -1));
26477     },
26478
26479     // private
26480     showNextYear : function(){
26481         this.update(this.activeDate.add("y", 1));
26482     },
26483
26484     // private
26485     handleMouseWheel : function(e){
26486         var delta = e.getWheelDelta();
26487         if(delta > 0){
26488             this.showPrevMonth();
26489             e.stopEvent();
26490         } else if(delta < 0){
26491             this.showNextMonth();
26492             e.stopEvent();
26493         }
26494     },
26495
26496     // private
26497     handleDateClick : function(e, t){
26498         e.stopEvent();
26499         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26500             this.setValue(new Date(t.dateValue));
26501             this.fireEvent("select", this, this.value);
26502         }
26503     },
26504
26505     // private
26506     selectToday : function(){
26507         this.setValue(new Date().clearTime());
26508         this.fireEvent("select", this, this.value);
26509     },
26510
26511     // private
26512     update : function(date)
26513     {
26514         var vd = this.activeDate;
26515         this.activeDate = date;
26516         if(vd && this.el){
26517             var t = date.getTime();
26518             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26519                 this.cells.removeClass("x-date-selected");
26520                 this.cells.each(function(c){
26521                    if(c.dom.firstChild.dateValue == t){
26522                        c.addClass("x-date-selected");
26523                        setTimeout(function(){
26524                             try{c.dom.firstChild.focus();}catch(e){}
26525                        }, 50);
26526                        return false;
26527                    }
26528                 });
26529                 return;
26530             }
26531         }
26532         
26533         var days = date.getDaysInMonth();
26534         var firstOfMonth = date.getFirstDateOfMonth();
26535         var startingPos = firstOfMonth.getDay()-this.startDay;
26536
26537         if(startingPos <= this.startDay){
26538             startingPos += 7;
26539         }
26540
26541         var pm = date.add("mo", -1);
26542         var prevStart = pm.getDaysInMonth()-startingPos;
26543
26544         var cells = this.cells.elements;
26545         var textEls = this.textNodes;
26546         days += startingPos;
26547
26548         // convert everything to numbers so it's fast
26549         var day = 86400000;
26550         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26551         var today = new Date().clearTime().getTime();
26552         var sel = date.clearTime().getTime();
26553         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26554         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26555         var ddMatch = this.disabledDatesRE;
26556         var ddText = this.disabledDatesText;
26557         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26558         var ddaysText = this.disabledDaysText;
26559         var format = this.format;
26560
26561         var setCellClass = function(cal, cell){
26562             cell.title = "";
26563             var t = d.getTime();
26564             cell.firstChild.dateValue = t;
26565             if(t == today){
26566                 cell.className += " x-date-today";
26567                 cell.title = cal.todayText;
26568             }
26569             if(t == sel){
26570                 cell.className += " x-date-selected";
26571                 setTimeout(function(){
26572                     try{cell.firstChild.focus();}catch(e){}
26573                 }, 50);
26574             }
26575             // disabling
26576             if(t < min) {
26577                 cell.className = " x-date-disabled";
26578                 cell.title = cal.minText;
26579                 return;
26580             }
26581             if(t > max) {
26582                 cell.className = " x-date-disabled";
26583                 cell.title = cal.maxText;
26584                 return;
26585             }
26586             if(ddays){
26587                 if(ddays.indexOf(d.getDay()) != -1){
26588                     cell.title = ddaysText;
26589                     cell.className = " x-date-disabled";
26590                 }
26591             }
26592             if(ddMatch && format){
26593                 var fvalue = d.dateFormat(format);
26594                 if(ddMatch.test(fvalue)){
26595                     cell.title = ddText.replace("%0", fvalue);
26596                     cell.className = " x-date-disabled";
26597                 }
26598             }
26599         };
26600
26601         var i = 0;
26602         for(; i < startingPos; i++) {
26603             textEls[i].innerHTML = (++prevStart);
26604             d.setDate(d.getDate()+1);
26605             cells[i].className = "x-date-prevday";
26606             setCellClass(this, cells[i]);
26607         }
26608         for(; i < days; i++){
26609             intDay = i - startingPos + 1;
26610             textEls[i].innerHTML = (intDay);
26611             d.setDate(d.getDate()+1);
26612             cells[i].className = "x-date-active";
26613             setCellClass(this, cells[i]);
26614         }
26615         var extraDays = 0;
26616         for(; i < 42; i++) {
26617              textEls[i].innerHTML = (++extraDays);
26618              d.setDate(d.getDate()+1);
26619              cells[i].className = "x-date-nextday";
26620              setCellClass(this, cells[i]);
26621         }
26622
26623         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26624         this.fireEvent('monthchange', this, date);
26625         
26626         if(!this.internalRender){
26627             var main = this.el.dom.firstChild;
26628             var w = main.offsetWidth;
26629             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26630             Roo.fly(main).setWidth(w);
26631             this.internalRender = true;
26632             // opera does not respect the auto grow header center column
26633             // then, after it gets a width opera refuses to recalculate
26634             // without a second pass
26635             if(Roo.isOpera && !this.secondPass){
26636                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26637                 this.secondPass = true;
26638                 this.update.defer(10, this, [date]);
26639             }
26640         }
26641         
26642         
26643     }
26644 });        /*
26645  * Based on:
26646  * Ext JS Library 1.1.1
26647  * Copyright(c) 2006-2007, Ext JS, LLC.
26648  *
26649  * Originally Released Under LGPL - original licence link has changed is not relivant.
26650  *
26651  * Fork - LGPL
26652  * <script type="text/javascript">
26653  */
26654 /**
26655  * @class Roo.TabPanel
26656  * @extends Roo.util.Observable
26657  * A lightweight tab container.
26658  * <br><br>
26659  * Usage:
26660  * <pre><code>
26661 // basic tabs 1, built from existing content
26662 var tabs = new Roo.TabPanel("tabs1");
26663 tabs.addTab("script", "View Script");
26664 tabs.addTab("markup", "View Markup");
26665 tabs.activate("script");
26666
26667 // more advanced tabs, built from javascript
26668 var jtabs = new Roo.TabPanel("jtabs");
26669 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26670
26671 // set up the UpdateManager
26672 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26673 var updater = tab2.getUpdateManager();
26674 updater.setDefaultUrl("ajax1.htm");
26675 tab2.on('activate', updater.refresh, updater, true);
26676
26677 // Use setUrl for Ajax loading
26678 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26679 tab3.setUrl("ajax2.htm", null, true);
26680
26681 // Disabled tab
26682 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26683 tab4.disable();
26684
26685 jtabs.activate("jtabs-1");
26686  * </code></pre>
26687  * @constructor
26688  * Create a new TabPanel.
26689  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26690  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26691  */
26692 Roo.TabPanel = function(container, config){
26693     /**
26694     * The container element for this TabPanel.
26695     * @type Roo.Element
26696     */
26697     this.el = Roo.get(container, true);
26698     if(config){
26699         if(typeof config == "boolean"){
26700             this.tabPosition = config ? "bottom" : "top";
26701         }else{
26702             Roo.apply(this, config);
26703         }
26704     }
26705     if(this.tabPosition == "bottom"){
26706         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26707         this.el.addClass("x-tabs-bottom");
26708     }
26709     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26710     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26711     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26712     if(Roo.isIE){
26713         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26714     }
26715     if(this.tabPosition != "bottom"){
26716         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26717          * @type Roo.Element
26718          */
26719         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26720         this.el.addClass("x-tabs-top");
26721     }
26722     this.items = [];
26723
26724     this.bodyEl.setStyle("position", "relative");
26725
26726     this.active = null;
26727     this.activateDelegate = this.activate.createDelegate(this);
26728
26729     this.addEvents({
26730         /**
26731          * @event tabchange
26732          * Fires when the active tab changes
26733          * @param {Roo.TabPanel} this
26734          * @param {Roo.TabPanelItem} activePanel The new active tab
26735          */
26736         "tabchange": true,
26737         /**
26738          * @event beforetabchange
26739          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26740          * @param {Roo.TabPanel} this
26741          * @param {Object} e Set cancel to true on this object to cancel the tab change
26742          * @param {Roo.TabPanelItem} tab The tab being changed to
26743          */
26744         "beforetabchange" : true
26745     });
26746
26747     Roo.EventManager.onWindowResize(this.onResize, this);
26748     this.cpad = this.el.getPadding("lr");
26749     this.hiddenCount = 0;
26750
26751
26752     // toolbar on the tabbar support...
26753     if (this.toolbar) {
26754         var tcfg = this.toolbar;
26755         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26756         this.toolbar = new Roo.Toolbar(tcfg);
26757         if (Roo.isSafari) {
26758             var tbl = tcfg.container.child('table', true);
26759             tbl.setAttribute('width', '100%');
26760         }
26761         
26762     }
26763    
26764
26765
26766     Roo.TabPanel.superclass.constructor.call(this);
26767 };
26768
26769 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26770     /*
26771      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26772      */
26773     tabPosition : "top",
26774     /*
26775      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26776      */
26777     currentTabWidth : 0,
26778     /*
26779      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26780      */
26781     minTabWidth : 40,
26782     /*
26783      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26784      */
26785     maxTabWidth : 250,
26786     /*
26787      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26788      */
26789     preferredTabWidth : 175,
26790     /*
26791      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26792      */
26793     resizeTabs : false,
26794     /*
26795      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26796      */
26797     monitorResize : true,
26798     /*
26799      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26800      */
26801     toolbar : false,
26802
26803     /**
26804      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26805      * @param {String} id The id of the div to use <b>or create</b>
26806      * @param {String} text The text for the tab
26807      * @param {String} content (optional) Content to put in the TabPanelItem body
26808      * @param {Boolean} closable (optional) True to create a close icon on the tab
26809      * @return {Roo.TabPanelItem} The created TabPanelItem
26810      */
26811     addTab : function(id, text, content, closable){
26812         var item = new Roo.TabPanelItem(this, id, text, closable);
26813         this.addTabItem(item);
26814         if(content){
26815             item.setContent(content);
26816         }
26817         return item;
26818     },
26819
26820     /**
26821      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26822      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26823      * @return {Roo.TabPanelItem}
26824      */
26825     getTab : function(id){
26826         return this.items[id];
26827     },
26828
26829     /**
26830      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26831      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26832      */
26833     hideTab : function(id){
26834         var t = this.items[id];
26835         if(!t.isHidden()){
26836            t.setHidden(true);
26837            this.hiddenCount++;
26838            this.autoSizeTabs();
26839         }
26840     },
26841
26842     /**
26843      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26844      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26845      */
26846     unhideTab : function(id){
26847         var t = this.items[id];
26848         if(t.isHidden()){
26849            t.setHidden(false);
26850            this.hiddenCount--;
26851            this.autoSizeTabs();
26852         }
26853     },
26854
26855     /**
26856      * Adds an existing {@link Roo.TabPanelItem}.
26857      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26858      */
26859     addTabItem : function(item){
26860         this.items[item.id] = item;
26861         this.items.push(item);
26862         if(this.resizeTabs){
26863            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26864            this.autoSizeTabs();
26865         }else{
26866             item.autoSize();
26867         }
26868     },
26869
26870     /**
26871      * Removes a {@link Roo.TabPanelItem}.
26872      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26873      */
26874     removeTab : function(id){
26875         var items = this.items;
26876         var tab = items[id];
26877         if(!tab) { return; }
26878         var index = items.indexOf(tab);
26879         if(this.active == tab && items.length > 1){
26880             var newTab = this.getNextAvailable(index);
26881             if(newTab) {
26882                 newTab.activate();
26883             }
26884         }
26885         this.stripEl.dom.removeChild(tab.pnode.dom);
26886         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26887             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26888         }
26889         items.splice(index, 1);
26890         delete this.items[tab.id];
26891         tab.fireEvent("close", tab);
26892         tab.purgeListeners();
26893         this.autoSizeTabs();
26894     },
26895
26896     getNextAvailable : function(start){
26897         var items = this.items;
26898         var index = start;
26899         // look for a next tab that will slide over to
26900         // replace the one being removed
26901         while(index < items.length){
26902             var item = items[++index];
26903             if(item && !item.isHidden()){
26904                 return item;
26905             }
26906         }
26907         // if one isn't found select the previous tab (on the left)
26908         index = start;
26909         while(index >= 0){
26910             var item = items[--index];
26911             if(item && !item.isHidden()){
26912                 return item;
26913             }
26914         }
26915         return null;
26916     },
26917
26918     /**
26919      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26920      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26921      */
26922     disableTab : function(id){
26923         var tab = this.items[id];
26924         if(tab && this.active != tab){
26925             tab.disable();
26926         }
26927     },
26928
26929     /**
26930      * Enables a {@link Roo.TabPanelItem} that is disabled.
26931      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26932      */
26933     enableTab : function(id){
26934         var tab = this.items[id];
26935         tab.enable();
26936     },
26937
26938     /**
26939      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26940      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26941      * @return {Roo.TabPanelItem} The TabPanelItem.
26942      */
26943     activate : function(id){
26944         var tab = this.items[id];
26945         if(!tab){
26946             return null;
26947         }
26948         if(tab == this.active || tab.disabled){
26949             return tab;
26950         }
26951         var e = {};
26952         this.fireEvent("beforetabchange", this, e, tab);
26953         if(e.cancel !== true && !tab.disabled){
26954             if(this.active){
26955                 this.active.hide();
26956             }
26957             this.active = this.items[id];
26958             this.active.show();
26959             this.fireEvent("tabchange", this, this.active);
26960         }
26961         return tab;
26962     },
26963
26964     /**
26965      * Gets the active {@link Roo.TabPanelItem}.
26966      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26967      */
26968     getActiveTab : function(){
26969         return this.active;
26970     },
26971
26972     /**
26973      * Updates the tab body element to fit the height of the container element
26974      * for overflow scrolling
26975      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26976      */
26977     syncHeight : function(targetHeight){
26978         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26979         var bm = this.bodyEl.getMargins();
26980         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26981         this.bodyEl.setHeight(newHeight);
26982         return newHeight;
26983     },
26984
26985     onResize : function(){
26986         if(this.monitorResize){
26987             this.autoSizeTabs();
26988         }
26989     },
26990
26991     /**
26992      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26993      */
26994     beginUpdate : function(){
26995         this.updating = true;
26996     },
26997
26998     /**
26999      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
27000      */
27001     endUpdate : function(){
27002         this.updating = false;
27003         this.autoSizeTabs();
27004     },
27005
27006     /**
27007      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27008      */
27009     autoSizeTabs : function(){
27010         var count = this.items.length;
27011         var vcount = count - this.hiddenCount;
27012         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
27013         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27014         var availWidth = Math.floor(w / vcount);
27015         var b = this.stripBody;
27016         if(b.getWidth() > w){
27017             var tabs = this.items;
27018             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27019             if(availWidth < this.minTabWidth){
27020                 /*if(!this.sleft){    // incomplete scrolling code
27021                     this.createScrollButtons();
27022                 }
27023                 this.showScroll();
27024                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27025             }
27026         }else{
27027             if(this.currentTabWidth < this.preferredTabWidth){
27028                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27029             }
27030         }
27031     },
27032
27033     /**
27034      * Returns the number of tabs in this TabPanel.
27035      * @return {Number}
27036      */
27037      getCount : function(){
27038          return this.items.length;
27039      },
27040
27041     /**
27042      * Resizes all the tabs to the passed width
27043      * @param {Number} The new width
27044      */
27045     setTabWidth : function(width){
27046         this.currentTabWidth = width;
27047         for(var i = 0, len = this.items.length; i < len; i++) {
27048                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27049         }
27050     },
27051
27052     /**
27053      * Destroys this TabPanel
27054      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27055      */
27056     destroy : function(removeEl){
27057         Roo.EventManager.removeResizeListener(this.onResize, this);
27058         for(var i = 0, len = this.items.length; i < len; i++){
27059             this.items[i].purgeListeners();
27060         }
27061         if(removeEl === true){
27062             this.el.update("");
27063             this.el.remove();
27064         }
27065     }
27066 });
27067
27068 /**
27069  * @class Roo.TabPanelItem
27070  * @extends Roo.util.Observable
27071  * Represents an individual item (tab plus body) in a TabPanel.
27072  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27073  * @param {String} id The id of this TabPanelItem
27074  * @param {String} text The text for the tab of this TabPanelItem
27075  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27076  */
27077 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27078     /**
27079      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27080      * @type Roo.TabPanel
27081      */
27082     this.tabPanel = tabPanel;
27083     /**
27084      * The id for this TabPanelItem
27085      * @type String
27086      */
27087     this.id = id;
27088     /** @private */
27089     this.disabled = false;
27090     /** @private */
27091     this.text = text;
27092     /** @private */
27093     this.loaded = false;
27094     this.closable = closable;
27095
27096     /**
27097      * The body element for this TabPanelItem.
27098      * @type Roo.Element
27099      */
27100     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27101     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27102     this.bodyEl.setStyle("display", "block");
27103     this.bodyEl.setStyle("zoom", "1");
27104     this.hideAction();
27105
27106     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27107     /** @private */
27108     this.el = Roo.get(els.el, true);
27109     this.inner = Roo.get(els.inner, true);
27110     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27111     this.pnode = Roo.get(els.el.parentNode, true);
27112     this.el.on("mousedown", this.onTabMouseDown, this);
27113     this.el.on("click", this.onTabClick, this);
27114     /** @private */
27115     if(closable){
27116         var c = Roo.get(els.close, true);
27117         c.dom.title = this.closeText;
27118         c.addClassOnOver("close-over");
27119         c.on("click", this.closeClick, this);
27120      }
27121
27122     this.addEvents({
27123          /**
27124          * @event activate
27125          * Fires when this tab becomes the active tab.
27126          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27127          * @param {Roo.TabPanelItem} this
27128          */
27129         "activate": true,
27130         /**
27131          * @event beforeclose
27132          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27133          * @param {Roo.TabPanelItem} this
27134          * @param {Object} e Set cancel to true on this object to cancel the close.
27135          */
27136         "beforeclose": true,
27137         /**
27138          * @event close
27139          * Fires when this tab is closed.
27140          * @param {Roo.TabPanelItem} this
27141          */
27142          "close": true,
27143         /**
27144          * @event deactivate
27145          * Fires when this tab is no longer the active tab.
27146          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27147          * @param {Roo.TabPanelItem} this
27148          */
27149          "deactivate" : true
27150     });
27151     this.hidden = false;
27152
27153     Roo.TabPanelItem.superclass.constructor.call(this);
27154 };
27155
27156 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27157     purgeListeners : function(){
27158        Roo.util.Observable.prototype.purgeListeners.call(this);
27159        this.el.removeAllListeners();
27160     },
27161     /**
27162      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27163      */
27164     show : function(){
27165         this.pnode.addClass("on");
27166         this.showAction();
27167         if(Roo.isOpera){
27168             this.tabPanel.stripWrap.repaint();
27169         }
27170         this.fireEvent("activate", this.tabPanel, this);
27171     },
27172
27173     /**
27174      * Returns true if this tab is the active tab.
27175      * @return {Boolean}
27176      */
27177     isActive : function(){
27178         return this.tabPanel.getActiveTab() == this;
27179     },
27180
27181     /**
27182      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27183      */
27184     hide : function(){
27185         this.pnode.removeClass("on");
27186         this.hideAction();
27187         this.fireEvent("deactivate", this.tabPanel, this);
27188     },
27189
27190     hideAction : function(){
27191         this.bodyEl.hide();
27192         this.bodyEl.setStyle("position", "absolute");
27193         this.bodyEl.setLeft("-20000px");
27194         this.bodyEl.setTop("-20000px");
27195     },
27196
27197     showAction : function(){
27198         this.bodyEl.setStyle("position", "relative");
27199         this.bodyEl.setTop("");
27200         this.bodyEl.setLeft("");
27201         this.bodyEl.show();
27202     },
27203
27204     /**
27205      * Set the tooltip for the tab.
27206      * @param {String} tooltip The tab's tooltip
27207      */
27208     setTooltip : function(text){
27209         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27210             this.textEl.dom.qtip = text;
27211             this.textEl.dom.removeAttribute('title');
27212         }else{
27213             this.textEl.dom.title = text;
27214         }
27215     },
27216
27217     onTabClick : function(e){
27218         e.preventDefault();
27219         this.tabPanel.activate(this.id);
27220     },
27221
27222     onTabMouseDown : function(e){
27223         e.preventDefault();
27224         this.tabPanel.activate(this.id);
27225     },
27226
27227     getWidth : function(){
27228         return this.inner.getWidth();
27229     },
27230
27231     setWidth : function(width){
27232         var iwidth = width - this.pnode.getPadding("lr");
27233         this.inner.setWidth(iwidth);
27234         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27235         this.pnode.setWidth(width);
27236     },
27237
27238     /**
27239      * Show or hide the tab
27240      * @param {Boolean} hidden True to hide or false to show.
27241      */
27242     setHidden : function(hidden){
27243         this.hidden = hidden;
27244         this.pnode.setStyle("display", hidden ? "none" : "");
27245     },
27246
27247     /**
27248      * Returns true if this tab is "hidden"
27249      * @return {Boolean}
27250      */
27251     isHidden : function(){
27252         return this.hidden;
27253     },
27254
27255     /**
27256      * Returns the text for this tab
27257      * @return {String}
27258      */
27259     getText : function(){
27260         return this.text;
27261     },
27262
27263     autoSize : function(){
27264         //this.el.beginMeasure();
27265         this.textEl.setWidth(1);
27266         /*
27267          *  #2804 [new] Tabs in Roojs
27268          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27269          */
27270         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27271         //this.el.endMeasure();
27272     },
27273
27274     /**
27275      * Sets the text for the tab (Note: this also sets the tooltip text)
27276      * @param {String} text The tab's text and tooltip
27277      */
27278     setText : function(text){
27279         this.text = text;
27280         this.textEl.update(text);
27281         this.setTooltip(text);
27282         if(!this.tabPanel.resizeTabs){
27283             this.autoSize();
27284         }
27285     },
27286     /**
27287      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27288      */
27289     activate : function(){
27290         this.tabPanel.activate(this.id);
27291     },
27292
27293     /**
27294      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27295      */
27296     disable : function(){
27297         if(this.tabPanel.active != this){
27298             this.disabled = true;
27299             this.pnode.addClass("disabled");
27300         }
27301     },
27302
27303     /**
27304      * Enables this TabPanelItem if it was previously disabled.
27305      */
27306     enable : function(){
27307         this.disabled = false;
27308         this.pnode.removeClass("disabled");
27309     },
27310
27311     /**
27312      * Sets the content for this TabPanelItem.
27313      * @param {String} content The content
27314      * @param {Boolean} loadScripts true to look for and load scripts
27315      */
27316     setContent : function(content, loadScripts){
27317         this.bodyEl.update(content, loadScripts);
27318     },
27319
27320     /**
27321      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27322      * @return {Roo.UpdateManager} The UpdateManager
27323      */
27324     getUpdateManager : function(){
27325         return this.bodyEl.getUpdateManager();
27326     },
27327
27328     /**
27329      * Set a URL to be used to load the content for this TabPanelItem.
27330      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27331      * @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)
27332      * @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)
27333      * @return {Roo.UpdateManager} The UpdateManager
27334      */
27335     setUrl : function(url, params, loadOnce){
27336         if(this.refreshDelegate){
27337             this.un('activate', this.refreshDelegate);
27338         }
27339         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27340         this.on("activate", this.refreshDelegate);
27341         return this.bodyEl.getUpdateManager();
27342     },
27343
27344     /** @private */
27345     _handleRefresh : function(url, params, loadOnce){
27346         if(!loadOnce || !this.loaded){
27347             var updater = this.bodyEl.getUpdateManager();
27348             updater.update(url, params, this._setLoaded.createDelegate(this));
27349         }
27350     },
27351
27352     /**
27353      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27354      *   Will fail silently if the setUrl method has not been called.
27355      *   This does not activate the panel, just updates its content.
27356      */
27357     refresh : function(){
27358         if(this.refreshDelegate){
27359            this.loaded = false;
27360            this.refreshDelegate();
27361         }
27362     },
27363
27364     /** @private */
27365     _setLoaded : function(){
27366         this.loaded = true;
27367     },
27368
27369     /** @private */
27370     closeClick : function(e){
27371         var o = {};
27372         e.stopEvent();
27373         this.fireEvent("beforeclose", this, o);
27374         if(o.cancel !== true){
27375             this.tabPanel.removeTab(this.id);
27376         }
27377     },
27378     /**
27379      * The text displayed in the tooltip for the close icon.
27380      * @type String
27381      */
27382     closeText : "Close this tab"
27383 });
27384
27385 /** @private */
27386 Roo.TabPanel.prototype.createStrip = function(container){
27387     var strip = document.createElement("div");
27388     strip.className = "x-tabs-wrap";
27389     container.appendChild(strip);
27390     return strip;
27391 };
27392 /** @private */
27393 Roo.TabPanel.prototype.createStripList = function(strip){
27394     // div wrapper for retard IE
27395     // returns the "tr" element.
27396     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27397         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27398         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27399     return strip.firstChild.firstChild.firstChild.firstChild;
27400 };
27401 /** @private */
27402 Roo.TabPanel.prototype.createBody = function(container){
27403     var body = document.createElement("div");
27404     Roo.id(body, "tab-body");
27405     Roo.fly(body).addClass("x-tabs-body");
27406     container.appendChild(body);
27407     return body;
27408 };
27409 /** @private */
27410 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27411     var body = Roo.getDom(id);
27412     if(!body){
27413         body = document.createElement("div");
27414         body.id = id;
27415     }
27416     Roo.fly(body).addClass("x-tabs-item-body");
27417     bodyEl.insertBefore(body, bodyEl.firstChild);
27418     return body;
27419 };
27420 /** @private */
27421 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27422     var td = document.createElement("td");
27423     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27424     //stripEl.appendChild(td);
27425     if(closable){
27426         td.className = "x-tabs-closable";
27427         if(!this.closeTpl){
27428             this.closeTpl = new Roo.Template(
27429                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27430                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27431                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27432             );
27433         }
27434         var el = this.closeTpl.overwrite(td, {"text": text});
27435         var close = el.getElementsByTagName("div")[0];
27436         var inner = el.getElementsByTagName("em")[0];
27437         return {"el": el, "close": close, "inner": inner};
27438     } else {
27439         if(!this.tabTpl){
27440             this.tabTpl = new Roo.Template(
27441                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27442                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27443             );
27444         }
27445         var el = this.tabTpl.overwrite(td, {"text": text});
27446         var inner = el.getElementsByTagName("em")[0];
27447         return {"el": el, "inner": inner};
27448     }
27449 };/*
27450  * Based on:
27451  * Ext JS Library 1.1.1
27452  * Copyright(c) 2006-2007, Ext JS, LLC.
27453  *
27454  * Originally Released Under LGPL - original licence link has changed is not relivant.
27455  *
27456  * Fork - LGPL
27457  * <script type="text/javascript">
27458  */
27459
27460 /**
27461  * @class Roo.Button
27462  * @extends Roo.util.Observable
27463  * Simple Button class
27464  * @cfg {String} text The button text
27465  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27466  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27467  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27468  * @cfg {Object} scope The scope of the handler
27469  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27470  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27471  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27472  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27473  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27474  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27475    applies if enableToggle = true)
27476  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27477  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27478   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27479  * @constructor
27480  * Create a new button
27481  * @param {Object} config The config object
27482  */
27483 Roo.Button = function(renderTo, config)
27484 {
27485     if (!config) {
27486         config = renderTo;
27487         renderTo = config.renderTo || false;
27488     }
27489     
27490     Roo.apply(this, config);
27491     this.addEvents({
27492         /**
27493              * @event click
27494              * Fires when this button is clicked
27495              * @param {Button} this
27496              * @param {EventObject} e The click event
27497              */
27498             "click" : true,
27499         /**
27500              * @event toggle
27501              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27502              * @param {Button} this
27503              * @param {Boolean} pressed
27504              */
27505             "toggle" : true,
27506         /**
27507              * @event mouseover
27508              * Fires when the mouse hovers over the button
27509              * @param {Button} this
27510              * @param {Event} e The event object
27511              */
27512         'mouseover' : true,
27513         /**
27514              * @event mouseout
27515              * Fires when the mouse exits the button
27516              * @param {Button} this
27517              * @param {Event} e The event object
27518              */
27519         'mouseout': true,
27520          /**
27521              * @event render
27522              * Fires when the button is rendered
27523              * @param {Button} this
27524              */
27525         'render': true
27526     });
27527     if(this.menu){
27528         this.menu = Roo.menu.MenuMgr.get(this.menu);
27529     }
27530     // register listeners first!!  - so render can be captured..
27531     Roo.util.Observable.call(this);
27532     if(renderTo){
27533         this.render(renderTo);
27534     }
27535     
27536   
27537 };
27538
27539 Roo.extend(Roo.Button, Roo.util.Observable, {
27540     /**
27541      * 
27542      */
27543     
27544     /**
27545      * Read-only. True if this button is hidden
27546      * @type Boolean
27547      */
27548     hidden : false,
27549     /**
27550      * Read-only. True if this button is disabled
27551      * @type Boolean
27552      */
27553     disabled : false,
27554     /**
27555      * Read-only. True if this button is pressed (only if enableToggle = true)
27556      * @type Boolean
27557      */
27558     pressed : false,
27559
27560     /**
27561      * @cfg {Number} tabIndex 
27562      * The DOM tabIndex for this button (defaults to undefined)
27563      */
27564     tabIndex : undefined,
27565
27566     /**
27567      * @cfg {Boolean} enableToggle
27568      * True to enable pressed/not pressed toggling (defaults to false)
27569      */
27570     enableToggle: false,
27571     /**
27572      * @cfg {Mixed} menu
27573      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27574      */
27575     menu : undefined,
27576     /**
27577      * @cfg {String} menuAlign
27578      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27579      */
27580     menuAlign : "tl-bl?",
27581
27582     /**
27583      * @cfg {String} iconCls
27584      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27585      */
27586     iconCls : undefined,
27587     /**
27588      * @cfg {String} type
27589      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27590      */
27591     type : 'button',
27592
27593     // private
27594     menuClassTarget: 'tr',
27595
27596     /**
27597      * @cfg {String} clickEvent
27598      * The type of event to map to the button's event handler (defaults to 'click')
27599      */
27600     clickEvent : 'click',
27601
27602     /**
27603      * @cfg {Boolean} handleMouseEvents
27604      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27605      */
27606     handleMouseEvents : true,
27607
27608     /**
27609      * @cfg {String} tooltipType
27610      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27611      */
27612     tooltipType : 'qtip',
27613
27614     /**
27615      * @cfg {String} cls
27616      * A CSS class to apply to the button's main element.
27617      */
27618     
27619     /**
27620      * @cfg {Roo.Template} template (Optional)
27621      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27622      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27623      * require code modifications if required elements (e.g. a button) aren't present.
27624      */
27625
27626     // private
27627     render : function(renderTo){
27628         var btn;
27629         if(this.hideParent){
27630             this.parentEl = Roo.get(renderTo);
27631         }
27632         if(!this.dhconfig){
27633             if(!this.template){
27634                 if(!Roo.Button.buttonTemplate){
27635                     // hideous table template
27636                     Roo.Button.buttonTemplate = new Roo.Template(
27637                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27638                         '<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>',
27639                         "</tr></tbody></table>");
27640                 }
27641                 this.template = Roo.Button.buttonTemplate;
27642             }
27643             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27644             var btnEl = btn.child("button:first");
27645             btnEl.on('focus', this.onFocus, this);
27646             btnEl.on('blur', this.onBlur, this);
27647             if(this.cls){
27648                 btn.addClass(this.cls);
27649             }
27650             if(this.icon){
27651                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27652             }
27653             if(this.iconCls){
27654                 btnEl.addClass(this.iconCls);
27655                 if(!this.cls){
27656                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27657                 }
27658             }
27659             if(this.tabIndex !== undefined){
27660                 btnEl.dom.tabIndex = this.tabIndex;
27661             }
27662             if(this.tooltip){
27663                 if(typeof this.tooltip == 'object'){
27664                     Roo.QuickTips.tips(Roo.apply({
27665                           target: btnEl.id
27666                     }, this.tooltip));
27667                 } else {
27668                     btnEl.dom[this.tooltipType] = this.tooltip;
27669                 }
27670             }
27671         }else{
27672             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27673         }
27674         this.el = btn;
27675         if(this.id){
27676             this.el.dom.id = this.el.id = this.id;
27677         }
27678         if(this.menu){
27679             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27680             this.menu.on("show", this.onMenuShow, this);
27681             this.menu.on("hide", this.onMenuHide, this);
27682         }
27683         btn.addClass("x-btn");
27684         if(Roo.isIE && !Roo.isIE7){
27685             this.autoWidth.defer(1, this);
27686         }else{
27687             this.autoWidth();
27688         }
27689         if(this.handleMouseEvents){
27690             btn.on("mouseover", this.onMouseOver, this);
27691             btn.on("mouseout", this.onMouseOut, this);
27692             btn.on("mousedown", this.onMouseDown, this);
27693         }
27694         btn.on(this.clickEvent, this.onClick, this);
27695         //btn.on("mouseup", this.onMouseUp, this);
27696         if(this.hidden){
27697             this.hide();
27698         }
27699         if(this.disabled){
27700             this.disable();
27701         }
27702         Roo.ButtonToggleMgr.register(this);
27703         if(this.pressed){
27704             this.el.addClass("x-btn-pressed");
27705         }
27706         if(this.repeat){
27707             var repeater = new Roo.util.ClickRepeater(btn,
27708                 typeof this.repeat == "object" ? this.repeat : {}
27709             );
27710             repeater.on("click", this.onClick,  this);
27711         }
27712         
27713         this.fireEvent('render', this);
27714         
27715     },
27716     /**
27717      * Returns the button's underlying element
27718      * @return {Roo.Element} The element
27719      */
27720     getEl : function(){
27721         return this.el;  
27722     },
27723     
27724     /**
27725      * Destroys this Button and removes any listeners.
27726      */
27727     destroy : function(){
27728         Roo.ButtonToggleMgr.unregister(this);
27729         this.el.removeAllListeners();
27730         this.purgeListeners();
27731         this.el.remove();
27732     },
27733
27734     // private
27735     autoWidth : function(){
27736         if(this.el){
27737             this.el.setWidth("auto");
27738             if(Roo.isIE7 && Roo.isStrict){
27739                 var ib = this.el.child('button');
27740                 if(ib && ib.getWidth() > 20){
27741                     ib.clip();
27742                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27743                 }
27744             }
27745             if(this.minWidth){
27746                 if(this.hidden){
27747                     this.el.beginMeasure();
27748                 }
27749                 if(this.el.getWidth() < this.minWidth){
27750                     this.el.setWidth(this.minWidth);
27751                 }
27752                 if(this.hidden){
27753                     this.el.endMeasure();
27754                 }
27755             }
27756         }
27757     },
27758
27759     /**
27760      * Assigns this button's click handler
27761      * @param {Function} handler The function to call when the button is clicked
27762      * @param {Object} scope (optional) Scope for the function passed in
27763      */
27764     setHandler : function(handler, scope){
27765         this.handler = handler;
27766         this.scope = scope;  
27767     },
27768     
27769     /**
27770      * Sets this button's text
27771      * @param {String} text The button text
27772      */
27773     setText : function(text){
27774         this.text = text;
27775         if(this.el){
27776             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27777         }
27778         this.autoWidth();
27779     },
27780     
27781     /**
27782      * Gets the text for this button
27783      * @return {String} The button text
27784      */
27785     getText : function(){
27786         return this.text;  
27787     },
27788     
27789     /**
27790      * Show this button
27791      */
27792     show: function(){
27793         this.hidden = false;
27794         if(this.el){
27795             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27796         }
27797     },
27798     
27799     /**
27800      * Hide this button
27801      */
27802     hide: function(){
27803         this.hidden = true;
27804         if(this.el){
27805             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27806         }
27807     },
27808     
27809     /**
27810      * Convenience function for boolean show/hide
27811      * @param {Boolean} visible True to show, false to hide
27812      */
27813     setVisible: function(visible){
27814         if(visible) {
27815             this.show();
27816         }else{
27817             this.hide();
27818         }
27819     },
27820     
27821     /**
27822      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27823      * @param {Boolean} state (optional) Force a particular state
27824      */
27825     toggle : function(state){
27826         state = state === undefined ? !this.pressed : state;
27827         if(state != this.pressed){
27828             if(state){
27829                 this.el.addClass("x-btn-pressed");
27830                 this.pressed = true;
27831                 this.fireEvent("toggle", this, true);
27832             }else{
27833                 this.el.removeClass("x-btn-pressed");
27834                 this.pressed = false;
27835                 this.fireEvent("toggle", this, false);
27836             }
27837             if(this.toggleHandler){
27838                 this.toggleHandler.call(this.scope || this, this, state);
27839             }
27840         }
27841     },
27842     
27843     /**
27844      * Focus the button
27845      */
27846     focus : function(){
27847         this.el.child('button:first').focus();
27848     },
27849     
27850     /**
27851      * Disable this button
27852      */
27853     disable : function(){
27854         if(this.el){
27855             this.el.addClass("x-btn-disabled");
27856         }
27857         this.disabled = true;
27858     },
27859     
27860     /**
27861      * Enable this button
27862      */
27863     enable : function(){
27864         if(this.el){
27865             this.el.removeClass("x-btn-disabled");
27866         }
27867         this.disabled = false;
27868     },
27869
27870     /**
27871      * Convenience function for boolean enable/disable
27872      * @param {Boolean} enabled True to enable, false to disable
27873      */
27874     setDisabled : function(v){
27875         this[v !== true ? "enable" : "disable"]();
27876     },
27877
27878     // private
27879     onClick : function(e)
27880     {
27881         if(e){
27882             e.preventDefault();
27883         }
27884         if(e.button != 0){
27885             return;
27886         }
27887         if(!this.disabled){
27888             if(this.enableToggle){
27889                 this.toggle();
27890             }
27891             if(this.menu && !this.menu.isVisible()){
27892                 this.menu.show(this.el, this.menuAlign);
27893             }
27894             this.fireEvent("click", this, e);
27895             if(this.handler){
27896                 this.el.removeClass("x-btn-over");
27897                 this.handler.call(this.scope || this, this, e);
27898             }
27899         }
27900     },
27901     // private
27902     onMouseOver : function(e){
27903         if(!this.disabled){
27904             this.el.addClass("x-btn-over");
27905             this.fireEvent('mouseover', this, e);
27906         }
27907     },
27908     // private
27909     onMouseOut : function(e){
27910         if(!e.within(this.el,  true)){
27911             this.el.removeClass("x-btn-over");
27912             this.fireEvent('mouseout', this, e);
27913         }
27914     },
27915     // private
27916     onFocus : function(e){
27917         if(!this.disabled){
27918             this.el.addClass("x-btn-focus");
27919         }
27920     },
27921     // private
27922     onBlur : function(e){
27923         this.el.removeClass("x-btn-focus");
27924     },
27925     // private
27926     onMouseDown : function(e){
27927         if(!this.disabled && e.button == 0){
27928             this.el.addClass("x-btn-click");
27929             Roo.get(document).on('mouseup', this.onMouseUp, this);
27930         }
27931     },
27932     // private
27933     onMouseUp : function(e){
27934         if(e.button == 0){
27935             this.el.removeClass("x-btn-click");
27936             Roo.get(document).un('mouseup', this.onMouseUp, this);
27937         }
27938     },
27939     // private
27940     onMenuShow : function(e){
27941         this.el.addClass("x-btn-menu-active");
27942     },
27943     // private
27944     onMenuHide : function(e){
27945         this.el.removeClass("x-btn-menu-active");
27946     }   
27947 });
27948
27949 // Private utility class used by Button
27950 Roo.ButtonToggleMgr = function(){
27951    var groups = {};
27952    
27953    function toggleGroup(btn, state){
27954        if(state){
27955            var g = groups[btn.toggleGroup];
27956            for(var i = 0, l = g.length; i < l; i++){
27957                if(g[i] != btn){
27958                    g[i].toggle(false);
27959                }
27960            }
27961        }
27962    }
27963    
27964    return {
27965        register : function(btn){
27966            if(!btn.toggleGroup){
27967                return;
27968            }
27969            var g = groups[btn.toggleGroup];
27970            if(!g){
27971                g = groups[btn.toggleGroup] = [];
27972            }
27973            g.push(btn);
27974            btn.on("toggle", toggleGroup);
27975        },
27976        
27977        unregister : function(btn){
27978            if(!btn.toggleGroup){
27979                return;
27980            }
27981            var g = groups[btn.toggleGroup];
27982            if(g){
27983                g.remove(btn);
27984                btn.un("toggle", toggleGroup);
27985            }
27986        }
27987    };
27988 }();/*
27989  * Based on:
27990  * Ext JS Library 1.1.1
27991  * Copyright(c) 2006-2007, Ext JS, LLC.
27992  *
27993  * Originally Released Under LGPL - original licence link has changed is not relivant.
27994  *
27995  * Fork - LGPL
27996  * <script type="text/javascript">
27997  */
27998  
27999 /**
28000  * @class Roo.SplitButton
28001  * @extends Roo.Button
28002  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28003  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28004  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28005  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28006  * @cfg {String} arrowTooltip The title attribute of the arrow
28007  * @constructor
28008  * Create a new menu button
28009  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28010  * @param {Object} config The config object
28011  */
28012 Roo.SplitButton = function(renderTo, config){
28013     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28014     /**
28015      * @event arrowclick
28016      * Fires when this button's arrow is clicked
28017      * @param {SplitButton} this
28018      * @param {EventObject} e The click event
28019      */
28020     this.addEvents({"arrowclick":true});
28021 };
28022
28023 Roo.extend(Roo.SplitButton, Roo.Button, {
28024     render : function(renderTo){
28025         // this is one sweet looking template!
28026         var tpl = new Roo.Template(
28027             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28028             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28029             '<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>',
28030             "</tbody></table></td><td>",
28031             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28032             '<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>',
28033             "</tbody></table></td></tr></table>"
28034         );
28035         var btn = tpl.append(renderTo, [this.text, this.type], true);
28036         var btnEl = btn.child("button");
28037         if(this.cls){
28038             btn.addClass(this.cls);
28039         }
28040         if(this.icon){
28041             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28042         }
28043         if(this.iconCls){
28044             btnEl.addClass(this.iconCls);
28045             if(!this.cls){
28046                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28047             }
28048         }
28049         this.el = btn;
28050         if(this.handleMouseEvents){
28051             btn.on("mouseover", this.onMouseOver, this);
28052             btn.on("mouseout", this.onMouseOut, this);
28053             btn.on("mousedown", this.onMouseDown, this);
28054             btn.on("mouseup", this.onMouseUp, this);
28055         }
28056         btn.on(this.clickEvent, this.onClick, this);
28057         if(this.tooltip){
28058             if(typeof this.tooltip == 'object'){
28059                 Roo.QuickTips.tips(Roo.apply({
28060                       target: btnEl.id
28061                 }, this.tooltip));
28062             } else {
28063                 btnEl.dom[this.tooltipType] = this.tooltip;
28064             }
28065         }
28066         if(this.arrowTooltip){
28067             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28068         }
28069         if(this.hidden){
28070             this.hide();
28071         }
28072         if(this.disabled){
28073             this.disable();
28074         }
28075         if(this.pressed){
28076             this.el.addClass("x-btn-pressed");
28077         }
28078         if(Roo.isIE && !Roo.isIE7){
28079             this.autoWidth.defer(1, this);
28080         }else{
28081             this.autoWidth();
28082         }
28083         if(this.menu){
28084             this.menu.on("show", this.onMenuShow, this);
28085             this.menu.on("hide", this.onMenuHide, this);
28086         }
28087         this.fireEvent('render', this);
28088     },
28089
28090     // private
28091     autoWidth : function(){
28092         if(this.el){
28093             var tbl = this.el.child("table:first");
28094             var tbl2 = this.el.child("table:last");
28095             this.el.setWidth("auto");
28096             tbl.setWidth("auto");
28097             if(Roo.isIE7 && Roo.isStrict){
28098                 var ib = this.el.child('button:first');
28099                 if(ib && ib.getWidth() > 20){
28100                     ib.clip();
28101                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28102                 }
28103             }
28104             if(this.minWidth){
28105                 if(this.hidden){
28106                     this.el.beginMeasure();
28107                 }
28108                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28109                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28110                 }
28111                 if(this.hidden){
28112                     this.el.endMeasure();
28113                 }
28114             }
28115             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28116         } 
28117     },
28118     /**
28119      * Sets this button's click handler
28120      * @param {Function} handler The function to call when the button is clicked
28121      * @param {Object} scope (optional) Scope for the function passed above
28122      */
28123     setHandler : function(handler, scope){
28124         this.handler = handler;
28125         this.scope = scope;  
28126     },
28127     
28128     /**
28129      * Sets this button's arrow click handler
28130      * @param {Function} handler The function to call when the arrow is clicked
28131      * @param {Object} scope (optional) Scope for the function passed above
28132      */
28133     setArrowHandler : function(handler, scope){
28134         this.arrowHandler = handler;
28135         this.scope = scope;  
28136     },
28137     
28138     /**
28139      * Focus the button
28140      */
28141     focus : function(){
28142         if(this.el){
28143             this.el.child("button:first").focus();
28144         }
28145     },
28146
28147     // private
28148     onClick : function(e){
28149         e.preventDefault();
28150         if(!this.disabled){
28151             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28152                 if(this.menu && !this.menu.isVisible()){
28153                     this.menu.show(this.el, this.menuAlign);
28154                 }
28155                 this.fireEvent("arrowclick", this, e);
28156                 if(this.arrowHandler){
28157                     this.arrowHandler.call(this.scope || this, this, e);
28158                 }
28159             }else{
28160                 this.fireEvent("click", this, e);
28161                 if(this.handler){
28162                     this.handler.call(this.scope || this, this, e);
28163                 }
28164             }
28165         }
28166     },
28167     // private
28168     onMouseDown : function(e){
28169         if(!this.disabled){
28170             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28171         }
28172     },
28173     // private
28174     onMouseUp : function(e){
28175         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28176     }   
28177 });
28178
28179
28180 // backwards compat
28181 Roo.MenuButton = Roo.SplitButton;/*
28182  * Based on:
28183  * Ext JS Library 1.1.1
28184  * Copyright(c) 2006-2007, Ext JS, LLC.
28185  *
28186  * Originally Released Under LGPL - original licence link has changed is not relivant.
28187  *
28188  * Fork - LGPL
28189  * <script type="text/javascript">
28190  */
28191
28192 /**
28193  * @class Roo.Toolbar
28194  * Basic Toolbar class.
28195  * @constructor
28196  * Creates a new Toolbar
28197  * @param {Object} container The config object
28198  */ 
28199 Roo.Toolbar = function(container, buttons, config)
28200 {
28201     /// old consturctor format still supported..
28202     if(container instanceof Array){ // omit the container for later rendering
28203         buttons = container;
28204         config = buttons;
28205         container = null;
28206     }
28207     if (typeof(container) == 'object' && container.xtype) {
28208         config = container;
28209         container = config.container;
28210         buttons = config.buttons || []; // not really - use items!!
28211     }
28212     var xitems = [];
28213     if (config && config.items) {
28214         xitems = config.items;
28215         delete config.items;
28216     }
28217     Roo.apply(this, config);
28218     this.buttons = buttons;
28219     
28220     if(container){
28221         this.render(container);
28222     }
28223     this.xitems = xitems;
28224     Roo.each(xitems, function(b) {
28225         this.add(b);
28226     }, this);
28227     
28228 };
28229
28230 Roo.Toolbar.prototype = {
28231     /**
28232      * @cfg {Array} items
28233      * array of button configs or elements to add (will be converted to a MixedCollection)
28234      */
28235     
28236     /**
28237      * @cfg {String/HTMLElement/Element} container
28238      * The id or element that will contain the toolbar
28239      */
28240     // private
28241     render : function(ct){
28242         this.el = Roo.get(ct);
28243         if(this.cls){
28244             this.el.addClass(this.cls);
28245         }
28246         // using a table allows for vertical alignment
28247         // 100% width is needed by Safari...
28248         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28249         this.tr = this.el.child("tr", true);
28250         var autoId = 0;
28251         this.items = new Roo.util.MixedCollection(false, function(o){
28252             return o.id || ("item" + (++autoId));
28253         });
28254         if(this.buttons){
28255             this.add.apply(this, this.buttons);
28256             delete this.buttons;
28257         }
28258     },
28259
28260     /**
28261      * Adds element(s) to the toolbar -- this function takes a variable number of 
28262      * arguments of mixed type and adds them to the toolbar.
28263      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28264      * <ul>
28265      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28266      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28267      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28268      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28269      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28270      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28271      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28272      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28273      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28274      * </ul>
28275      * @param {Mixed} arg2
28276      * @param {Mixed} etc.
28277      */
28278     add : function(){
28279         var a = arguments, l = a.length;
28280         for(var i = 0; i < l; i++){
28281             this._add(a[i]);
28282         }
28283     },
28284     // private..
28285     _add : function(el) {
28286         
28287         if (el.xtype) {
28288             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28289         }
28290         
28291         if (el.applyTo){ // some kind of form field
28292             return this.addField(el);
28293         } 
28294         if (el.render){ // some kind of Toolbar.Item
28295             return this.addItem(el);
28296         }
28297         if (typeof el == "string"){ // string
28298             if(el == "separator" || el == "-"){
28299                 return this.addSeparator();
28300             }
28301             if (el == " "){
28302                 return this.addSpacer();
28303             }
28304             if(el == "->"){
28305                 return this.addFill();
28306             }
28307             return this.addText(el);
28308             
28309         }
28310         if(el.tagName){ // element
28311             return this.addElement(el);
28312         }
28313         if(typeof el == "object"){ // must be button config?
28314             return this.addButton(el);
28315         }
28316         // and now what?!?!
28317         return false;
28318         
28319     },
28320     
28321     /**
28322      * Add an Xtype element
28323      * @param {Object} xtype Xtype Object
28324      * @return {Object} created Object
28325      */
28326     addxtype : function(e){
28327         return this.add(e);  
28328     },
28329     
28330     /**
28331      * Returns the Element for this toolbar.
28332      * @return {Roo.Element}
28333      */
28334     getEl : function(){
28335         return this.el;  
28336     },
28337     
28338     /**
28339      * Adds a separator
28340      * @return {Roo.Toolbar.Item} The separator item
28341      */
28342     addSeparator : function(){
28343         return this.addItem(new Roo.Toolbar.Separator());
28344     },
28345
28346     /**
28347      * Adds a spacer element
28348      * @return {Roo.Toolbar.Spacer} The spacer item
28349      */
28350     addSpacer : function(){
28351         return this.addItem(new Roo.Toolbar.Spacer());
28352     },
28353
28354     /**
28355      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28356      * @return {Roo.Toolbar.Fill} The fill item
28357      */
28358     addFill : function(){
28359         return this.addItem(new Roo.Toolbar.Fill());
28360     },
28361
28362     /**
28363      * Adds any standard HTML element to the toolbar
28364      * @param {String/HTMLElement/Element} el The element or id of the element to add
28365      * @return {Roo.Toolbar.Item} The element's item
28366      */
28367     addElement : function(el){
28368         return this.addItem(new Roo.Toolbar.Item(el));
28369     },
28370     /**
28371      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28372      * @type Roo.util.MixedCollection  
28373      */
28374     items : false,
28375      
28376     /**
28377      * Adds any Toolbar.Item or subclass
28378      * @param {Roo.Toolbar.Item} item
28379      * @return {Roo.Toolbar.Item} The item
28380      */
28381     addItem : function(item){
28382         var td = this.nextBlock();
28383         item.render(td);
28384         this.items.add(item);
28385         return item;
28386     },
28387     
28388     /**
28389      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28390      * @param {Object/Array} config A button config or array of configs
28391      * @return {Roo.Toolbar.Button/Array}
28392      */
28393     addButton : function(config){
28394         if(config instanceof Array){
28395             var buttons = [];
28396             for(var i = 0, len = config.length; i < len; i++) {
28397                 buttons.push(this.addButton(config[i]));
28398             }
28399             return buttons;
28400         }
28401         var b = config;
28402         if(!(config instanceof Roo.Toolbar.Button)){
28403             b = config.split ?
28404                 new Roo.Toolbar.SplitButton(config) :
28405                 new Roo.Toolbar.Button(config);
28406         }
28407         var td = this.nextBlock();
28408         b.render(td);
28409         this.items.add(b);
28410         return b;
28411     },
28412     
28413     /**
28414      * Adds text to the toolbar
28415      * @param {String} text The text to add
28416      * @return {Roo.Toolbar.Item} The element's item
28417      */
28418     addText : function(text){
28419         return this.addItem(new Roo.Toolbar.TextItem(text));
28420     },
28421     
28422     /**
28423      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28424      * @param {Number} index The index where the item is to be inserted
28425      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28426      * @return {Roo.Toolbar.Button/Item}
28427      */
28428     insertButton : function(index, item){
28429         if(item instanceof Array){
28430             var buttons = [];
28431             for(var i = 0, len = item.length; i < len; i++) {
28432                buttons.push(this.insertButton(index + i, item[i]));
28433             }
28434             return buttons;
28435         }
28436         if (!(item instanceof Roo.Toolbar.Button)){
28437            item = new Roo.Toolbar.Button(item);
28438         }
28439         var td = document.createElement("td");
28440         this.tr.insertBefore(td, this.tr.childNodes[index]);
28441         item.render(td);
28442         this.items.insert(index, item);
28443         return item;
28444     },
28445     
28446     /**
28447      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28448      * @param {Object} config
28449      * @return {Roo.Toolbar.Item} The element's item
28450      */
28451     addDom : function(config, returnEl){
28452         var td = this.nextBlock();
28453         Roo.DomHelper.overwrite(td, config);
28454         var ti = new Roo.Toolbar.Item(td.firstChild);
28455         ti.render(td);
28456         this.items.add(ti);
28457         return ti;
28458     },
28459
28460     /**
28461      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28462      * @type Roo.util.MixedCollection  
28463      */
28464     fields : false,
28465     
28466     /**
28467      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28468      * Note: the field should not have been rendered yet. For a field that has already been
28469      * rendered, use {@link #addElement}.
28470      * @param {Roo.form.Field} field
28471      * @return {Roo.ToolbarItem}
28472      */
28473      
28474       
28475     addField : function(field) {
28476         if (!this.fields) {
28477             var autoId = 0;
28478             this.fields = new Roo.util.MixedCollection(false, function(o){
28479                 return o.id || ("item" + (++autoId));
28480             });
28481
28482         }
28483         
28484         var td = this.nextBlock();
28485         field.render(td);
28486         var ti = new Roo.Toolbar.Item(td.firstChild);
28487         ti.render(td);
28488         this.items.add(ti);
28489         this.fields.add(field);
28490         return ti;
28491     },
28492     /**
28493      * Hide the toolbar
28494      * @method hide
28495      */
28496      
28497       
28498     hide : function()
28499     {
28500         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28501         this.el.child('div').hide();
28502     },
28503     /**
28504      * Show the toolbar
28505      * @method show
28506      */
28507     show : function()
28508     {
28509         this.el.child('div').show();
28510     },
28511       
28512     // private
28513     nextBlock : function(){
28514         var td = document.createElement("td");
28515         this.tr.appendChild(td);
28516         return td;
28517     },
28518
28519     // private
28520     destroy : function(){
28521         if(this.items){ // rendered?
28522             Roo.destroy.apply(Roo, this.items.items);
28523         }
28524         if(this.fields){ // rendered?
28525             Roo.destroy.apply(Roo, this.fields.items);
28526         }
28527         Roo.Element.uncache(this.el, this.tr);
28528     }
28529 };
28530
28531 /**
28532  * @class Roo.Toolbar.Item
28533  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28534  * @constructor
28535  * Creates a new Item
28536  * @param {HTMLElement} el 
28537  */
28538 Roo.Toolbar.Item = function(el){
28539     var cfg = {};
28540     if (typeof (el.xtype) != 'undefined') {
28541         cfg = el;
28542         el = cfg.el;
28543     }
28544     
28545     this.el = Roo.getDom(el);
28546     this.id = Roo.id(this.el);
28547     this.hidden = false;
28548     
28549     this.addEvents({
28550          /**
28551              * @event render
28552              * Fires when the button is rendered
28553              * @param {Button} this
28554              */
28555         'render': true
28556     });
28557     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28558 };
28559 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28560 //Roo.Toolbar.Item.prototype = {
28561     
28562     /**
28563      * Get this item's HTML Element
28564      * @return {HTMLElement}
28565      */
28566     getEl : function(){
28567        return this.el;  
28568     },
28569
28570     // private
28571     render : function(td){
28572         
28573          this.td = td;
28574         td.appendChild(this.el);
28575         
28576         this.fireEvent('render', this);
28577     },
28578     
28579     /**
28580      * Removes and destroys this item.
28581      */
28582     destroy : function(){
28583         this.td.parentNode.removeChild(this.td);
28584     },
28585     
28586     /**
28587      * Shows this item.
28588      */
28589     show: function(){
28590         this.hidden = false;
28591         this.td.style.display = "";
28592     },
28593     
28594     /**
28595      * Hides this item.
28596      */
28597     hide: function(){
28598         this.hidden = true;
28599         this.td.style.display = "none";
28600     },
28601     
28602     /**
28603      * Convenience function for boolean show/hide.
28604      * @param {Boolean} visible true to show/false to hide
28605      */
28606     setVisible: function(visible){
28607         if(visible) {
28608             this.show();
28609         }else{
28610             this.hide();
28611         }
28612     },
28613     
28614     /**
28615      * Try to focus this item.
28616      */
28617     focus : function(){
28618         Roo.fly(this.el).focus();
28619     },
28620     
28621     /**
28622      * Disables this item.
28623      */
28624     disable : function(){
28625         Roo.fly(this.td).addClass("x-item-disabled");
28626         this.disabled = true;
28627         this.el.disabled = true;
28628     },
28629     
28630     /**
28631      * Enables this item.
28632      */
28633     enable : function(){
28634         Roo.fly(this.td).removeClass("x-item-disabled");
28635         this.disabled = false;
28636         this.el.disabled = false;
28637     }
28638 });
28639
28640
28641 /**
28642  * @class Roo.Toolbar.Separator
28643  * @extends Roo.Toolbar.Item
28644  * A simple toolbar separator class
28645  * @constructor
28646  * Creates a new Separator
28647  */
28648 Roo.Toolbar.Separator = function(cfg){
28649     
28650     var s = document.createElement("span");
28651     s.className = "ytb-sep";
28652     if (cfg) {
28653         cfg.el = s;
28654     }
28655     
28656     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28657 };
28658 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28659     enable:Roo.emptyFn,
28660     disable:Roo.emptyFn,
28661     focus:Roo.emptyFn
28662 });
28663
28664 /**
28665  * @class Roo.Toolbar.Spacer
28666  * @extends Roo.Toolbar.Item
28667  * A simple element that adds extra horizontal space to a toolbar.
28668  * @constructor
28669  * Creates a new Spacer
28670  */
28671 Roo.Toolbar.Spacer = function(cfg){
28672     var s = document.createElement("div");
28673     s.className = "ytb-spacer";
28674     if (cfg) {
28675         cfg.el = s;
28676     }
28677     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28678 };
28679 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28680     enable:Roo.emptyFn,
28681     disable:Roo.emptyFn,
28682     focus:Roo.emptyFn
28683 });
28684
28685 /**
28686  * @class Roo.Toolbar.Fill
28687  * @extends Roo.Toolbar.Spacer
28688  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28689  * @constructor
28690  * Creates a new Spacer
28691  */
28692 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28693     // private
28694     render : function(td){
28695         td.style.width = '100%';
28696         Roo.Toolbar.Fill.superclass.render.call(this, td);
28697     }
28698 });
28699
28700 /**
28701  * @class Roo.Toolbar.TextItem
28702  * @extends Roo.Toolbar.Item
28703  * A simple class that renders text directly into a toolbar.
28704  * @constructor
28705  * Creates a new TextItem
28706  * @param {String} text
28707  */
28708 Roo.Toolbar.TextItem = function(cfg){
28709     var  text = cfg || "";
28710     if (typeof(cfg) == 'object') {
28711         text = cfg.text || "";
28712     }  else {
28713         cfg = null;
28714     }
28715     var s = document.createElement("span");
28716     s.className = "ytb-text";
28717     s.innerHTML = text;
28718     if (cfg) {
28719         cfg.el  = s;
28720     }
28721     
28722     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28723 };
28724 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28725     
28726      
28727     enable:Roo.emptyFn,
28728     disable:Roo.emptyFn,
28729     focus:Roo.emptyFn
28730 });
28731
28732 /**
28733  * @class Roo.Toolbar.Button
28734  * @extends Roo.Button
28735  * A button that renders into a toolbar.
28736  * @constructor
28737  * Creates a new Button
28738  * @param {Object} config A standard {@link Roo.Button} config object
28739  */
28740 Roo.Toolbar.Button = function(config){
28741     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28742 };
28743 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28744     render : function(td){
28745         this.td = td;
28746         Roo.Toolbar.Button.superclass.render.call(this, td);
28747     },
28748     
28749     /**
28750      * Removes and destroys this button
28751      */
28752     destroy : function(){
28753         Roo.Toolbar.Button.superclass.destroy.call(this);
28754         this.td.parentNode.removeChild(this.td);
28755     },
28756     
28757     /**
28758      * Shows this button
28759      */
28760     show: function(){
28761         this.hidden = false;
28762         this.td.style.display = "";
28763     },
28764     
28765     /**
28766      * Hides this button
28767      */
28768     hide: function(){
28769         this.hidden = true;
28770         this.td.style.display = "none";
28771     },
28772
28773     /**
28774      * Disables this item
28775      */
28776     disable : function(){
28777         Roo.fly(this.td).addClass("x-item-disabled");
28778         this.disabled = true;
28779     },
28780
28781     /**
28782      * Enables this item
28783      */
28784     enable : function(){
28785         Roo.fly(this.td).removeClass("x-item-disabled");
28786         this.disabled = false;
28787     }
28788 });
28789 // backwards compat
28790 Roo.ToolbarButton = Roo.Toolbar.Button;
28791
28792 /**
28793  * @class Roo.Toolbar.SplitButton
28794  * @extends Roo.SplitButton
28795  * A menu button that renders into a toolbar.
28796  * @constructor
28797  * Creates a new SplitButton
28798  * @param {Object} config A standard {@link Roo.SplitButton} config object
28799  */
28800 Roo.Toolbar.SplitButton = function(config){
28801     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28802 };
28803 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28804     render : function(td){
28805         this.td = td;
28806         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28807     },
28808     
28809     /**
28810      * Removes and destroys this button
28811      */
28812     destroy : function(){
28813         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28814         this.td.parentNode.removeChild(this.td);
28815     },
28816     
28817     /**
28818      * Shows this button
28819      */
28820     show: function(){
28821         this.hidden = false;
28822         this.td.style.display = "";
28823     },
28824     
28825     /**
28826      * Hides this button
28827      */
28828     hide: function(){
28829         this.hidden = true;
28830         this.td.style.display = "none";
28831     }
28832 });
28833
28834 // backwards compat
28835 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28836  * Based on:
28837  * Ext JS Library 1.1.1
28838  * Copyright(c) 2006-2007, Ext JS, LLC.
28839  *
28840  * Originally Released Under LGPL - original licence link has changed is not relivant.
28841  *
28842  * Fork - LGPL
28843  * <script type="text/javascript">
28844  */
28845  
28846 /**
28847  * @class Roo.PagingToolbar
28848  * @extends Roo.Toolbar
28849  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28850  * @constructor
28851  * Create a new PagingToolbar
28852  * @param {Object} config The config object
28853  */
28854 Roo.PagingToolbar = function(el, ds, config)
28855 {
28856     // old args format still supported... - xtype is prefered..
28857     if (typeof(el) == 'object' && el.xtype) {
28858         // created from xtype...
28859         config = el;
28860         ds = el.dataSource;
28861         el = config.container;
28862     }
28863     var items = [];
28864     if (config.items) {
28865         items = config.items;
28866         config.items = [];
28867     }
28868     
28869     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28870     this.ds = ds;
28871     this.cursor = 0;
28872     this.renderButtons(this.el);
28873     this.bind(ds);
28874     
28875     // supprot items array.
28876    
28877     Roo.each(items, function(e) {
28878         this.add(Roo.factory(e));
28879     },this);
28880     
28881 };
28882
28883 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28884     /**
28885      * @cfg {Roo.data.Store} dataSource
28886      * The underlying data store providing the paged data
28887      */
28888     /**
28889      * @cfg {String/HTMLElement/Element} container
28890      * container The id or element that will contain the toolbar
28891      */
28892     /**
28893      * @cfg {Boolean} displayInfo
28894      * True to display the displayMsg (defaults to false)
28895      */
28896     /**
28897      * @cfg {Number} pageSize
28898      * The number of records to display per page (defaults to 20)
28899      */
28900     pageSize: 20,
28901     /**
28902      * @cfg {String} displayMsg
28903      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28904      */
28905     displayMsg : 'Displaying {0} - {1} of {2}',
28906     /**
28907      * @cfg {String} emptyMsg
28908      * The message to display when no records are found (defaults to "No data to display")
28909      */
28910     emptyMsg : 'No data to display',
28911     /**
28912      * Customizable piece of the default paging text (defaults to "Page")
28913      * @type String
28914      */
28915     beforePageText : "Page",
28916     /**
28917      * Customizable piece of the default paging text (defaults to "of %0")
28918      * @type String
28919      */
28920     afterPageText : "of {0}",
28921     /**
28922      * Customizable piece of the default paging text (defaults to "First Page")
28923      * @type String
28924      */
28925     firstText : "First Page",
28926     /**
28927      * Customizable piece of the default paging text (defaults to "Previous Page")
28928      * @type String
28929      */
28930     prevText : "Previous Page",
28931     /**
28932      * Customizable piece of the default paging text (defaults to "Next Page")
28933      * @type String
28934      */
28935     nextText : "Next Page",
28936     /**
28937      * Customizable piece of the default paging text (defaults to "Last Page")
28938      * @type String
28939      */
28940     lastText : "Last Page",
28941     /**
28942      * Customizable piece of the default paging text (defaults to "Refresh")
28943      * @type String
28944      */
28945     refreshText : "Refresh",
28946
28947     // private
28948     renderButtons : function(el){
28949         Roo.PagingToolbar.superclass.render.call(this, el);
28950         this.first = this.addButton({
28951             tooltip: this.firstText,
28952             cls: "x-btn-icon x-grid-page-first",
28953             disabled: true,
28954             handler: this.onClick.createDelegate(this, ["first"])
28955         });
28956         this.prev = this.addButton({
28957             tooltip: this.prevText,
28958             cls: "x-btn-icon x-grid-page-prev",
28959             disabled: true,
28960             handler: this.onClick.createDelegate(this, ["prev"])
28961         });
28962         //this.addSeparator();
28963         this.add(this.beforePageText);
28964         this.field = Roo.get(this.addDom({
28965            tag: "input",
28966            type: "text",
28967            size: "3",
28968            value: "1",
28969            cls: "x-grid-page-number"
28970         }).el);
28971         this.field.on("keydown", this.onPagingKeydown, this);
28972         this.field.on("focus", function(){this.dom.select();});
28973         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28974         this.field.setHeight(18);
28975         //this.addSeparator();
28976         this.next = this.addButton({
28977             tooltip: this.nextText,
28978             cls: "x-btn-icon x-grid-page-next",
28979             disabled: true,
28980             handler: this.onClick.createDelegate(this, ["next"])
28981         });
28982         this.last = this.addButton({
28983             tooltip: this.lastText,
28984             cls: "x-btn-icon x-grid-page-last",
28985             disabled: true,
28986             handler: this.onClick.createDelegate(this, ["last"])
28987         });
28988         //this.addSeparator();
28989         this.loading = this.addButton({
28990             tooltip: this.refreshText,
28991             cls: "x-btn-icon x-grid-loading",
28992             handler: this.onClick.createDelegate(this, ["refresh"])
28993         });
28994
28995         if(this.displayInfo){
28996             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28997         }
28998     },
28999
29000     // private
29001     updateInfo : function(){
29002         if(this.displayEl){
29003             var count = this.ds.getCount();
29004             var msg = count == 0 ?
29005                 this.emptyMsg :
29006                 String.format(
29007                     this.displayMsg,
29008                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29009                 );
29010             this.displayEl.update(msg);
29011         }
29012     },
29013
29014     // private
29015     onLoad : function(ds, r, o){
29016        this.cursor = o.params ? o.params.start : 0;
29017        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29018
29019        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29020        this.field.dom.value = ap;
29021        this.first.setDisabled(ap == 1);
29022        this.prev.setDisabled(ap == 1);
29023        this.next.setDisabled(ap == ps);
29024        this.last.setDisabled(ap == ps);
29025        this.loading.enable();
29026        this.updateInfo();
29027     },
29028
29029     // private
29030     getPageData : function(){
29031         var total = this.ds.getTotalCount();
29032         return {
29033             total : total,
29034             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29035             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29036         };
29037     },
29038
29039     // private
29040     onLoadError : function(){
29041         this.loading.enable();
29042     },
29043
29044     // private
29045     onPagingKeydown : function(e){
29046         var k = e.getKey();
29047         var d = this.getPageData();
29048         if(k == e.RETURN){
29049             var v = this.field.dom.value, pageNum;
29050             if(!v || isNaN(pageNum = parseInt(v, 10))){
29051                 this.field.dom.value = d.activePage;
29052                 return;
29053             }
29054             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29055             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29056             e.stopEvent();
29057         }
29058         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))
29059         {
29060           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29061           this.field.dom.value = pageNum;
29062           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29063           e.stopEvent();
29064         }
29065         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29066         {
29067           var v = this.field.dom.value, pageNum; 
29068           var increment = (e.shiftKey) ? 10 : 1;
29069           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29070             increment *= -1;
29071           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29072             this.field.dom.value = d.activePage;
29073             return;
29074           }
29075           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29076           {
29077             this.field.dom.value = parseInt(v, 10) + increment;
29078             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29079             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29080           }
29081           e.stopEvent();
29082         }
29083     },
29084
29085     // private
29086     beforeLoad : function(){
29087         if(this.loading){
29088             this.loading.disable();
29089         }
29090     },
29091
29092     // private
29093     onClick : function(which){
29094         var ds = this.ds;
29095         switch(which){
29096             case "first":
29097                 ds.load({params:{start: 0, limit: this.pageSize}});
29098             break;
29099             case "prev":
29100                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29101             break;
29102             case "next":
29103                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29104             break;
29105             case "last":
29106                 var total = ds.getTotalCount();
29107                 var extra = total % this.pageSize;
29108                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29109                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29110             break;
29111             case "refresh":
29112                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29113             break;
29114         }
29115     },
29116
29117     /**
29118      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29119      * @param {Roo.data.Store} store The data store to unbind
29120      */
29121     unbind : function(ds){
29122         ds.un("beforeload", this.beforeLoad, this);
29123         ds.un("load", this.onLoad, this);
29124         ds.un("loadexception", this.onLoadError, this);
29125         ds.un("remove", this.updateInfo, this);
29126         ds.un("add", this.updateInfo, this);
29127         this.ds = undefined;
29128     },
29129
29130     /**
29131      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29132      * @param {Roo.data.Store} store The data store to bind
29133      */
29134     bind : function(ds){
29135         ds.on("beforeload", this.beforeLoad, this);
29136         ds.on("load", this.onLoad, this);
29137         ds.on("loadexception", this.onLoadError, this);
29138         ds.on("remove", this.updateInfo, this);
29139         ds.on("add", this.updateInfo, this);
29140         this.ds = ds;
29141     }
29142 });/*
29143  * Based on:
29144  * Ext JS Library 1.1.1
29145  * Copyright(c) 2006-2007, Ext JS, LLC.
29146  *
29147  * Originally Released Under LGPL - original licence link has changed is not relivant.
29148  *
29149  * Fork - LGPL
29150  * <script type="text/javascript">
29151  */
29152
29153 /**
29154  * @class Roo.Resizable
29155  * @extends Roo.util.Observable
29156  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29157  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29158  * 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
29159  * the element will be wrapped for you automatically.</p>
29160  * <p>Here is the list of valid resize handles:</p>
29161  * <pre>
29162 Value   Description
29163 ------  -------------------
29164  'n'     north
29165  's'     south
29166  'e'     east
29167  'w'     west
29168  'nw'    northwest
29169  'sw'    southwest
29170  'se'    southeast
29171  'ne'    northeast
29172  'hd'    horizontal drag
29173  'all'   all
29174 </pre>
29175  * <p>Here's an example showing the creation of a typical Resizable:</p>
29176  * <pre><code>
29177 var resizer = new Roo.Resizable("element-id", {
29178     handles: 'all',
29179     minWidth: 200,
29180     minHeight: 100,
29181     maxWidth: 500,
29182     maxHeight: 400,
29183     pinned: true
29184 });
29185 resizer.on("resize", myHandler);
29186 </code></pre>
29187  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29188  * resizer.east.setDisplayed(false);</p>
29189  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29190  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29191  * resize operation's new size (defaults to [0, 0])
29192  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29193  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29194  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29195  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29196  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29197  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29198  * @cfg {Number} width The width of the element in pixels (defaults to null)
29199  * @cfg {Number} height The height of the element in pixels (defaults to null)
29200  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29201  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29202  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29203  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29204  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29205  * in favor of the handles config option (defaults to false)
29206  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29207  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29208  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29209  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29210  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29211  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29212  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29213  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29214  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29215  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29216  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29217  * @constructor
29218  * Create a new resizable component
29219  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29220  * @param {Object} config configuration options
29221   */
29222 Roo.Resizable = function(el, config)
29223 {
29224     this.el = Roo.get(el);
29225
29226     if(config && config.wrap){
29227         config.resizeChild = this.el;
29228         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29229         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29230         this.el.setStyle("overflow", "hidden");
29231         this.el.setPositioning(config.resizeChild.getPositioning());
29232         config.resizeChild.clearPositioning();
29233         if(!config.width || !config.height){
29234             var csize = config.resizeChild.getSize();
29235             this.el.setSize(csize.width, csize.height);
29236         }
29237         if(config.pinned && !config.adjustments){
29238             config.adjustments = "auto";
29239         }
29240     }
29241
29242     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29243     this.proxy.unselectable();
29244     this.proxy.enableDisplayMode('block');
29245
29246     Roo.apply(this, config);
29247
29248     if(this.pinned){
29249         this.disableTrackOver = true;
29250         this.el.addClass("x-resizable-pinned");
29251     }
29252     // if the element isn't positioned, make it relative
29253     var position = this.el.getStyle("position");
29254     if(position != "absolute" && position != "fixed"){
29255         this.el.setStyle("position", "relative");
29256     }
29257     if(!this.handles){ // no handles passed, must be legacy style
29258         this.handles = 's,e,se';
29259         if(this.multiDirectional){
29260             this.handles += ',n,w';
29261         }
29262     }
29263     if(this.handles == "all"){
29264         this.handles = "n s e w ne nw se sw";
29265     }
29266     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29267     var ps = Roo.Resizable.positions;
29268     for(var i = 0, len = hs.length; i < len; i++){
29269         if(hs[i] && ps[hs[i]]){
29270             var pos = ps[hs[i]];
29271             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29272         }
29273     }
29274     // legacy
29275     this.corner = this.southeast;
29276     
29277     // updateBox = the box can move..
29278     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29279         this.updateBox = true;
29280     }
29281
29282     this.activeHandle = null;
29283
29284     if(this.resizeChild){
29285         if(typeof this.resizeChild == "boolean"){
29286             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29287         }else{
29288             this.resizeChild = Roo.get(this.resizeChild, true);
29289         }
29290     }
29291     
29292     if(this.adjustments == "auto"){
29293         var rc = this.resizeChild;
29294         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29295         if(rc && (hw || hn)){
29296             rc.position("relative");
29297             rc.setLeft(hw ? hw.el.getWidth() : 0);
29298             rc.setTop(hn ? hn.el.getHeight() : 0);
29299         }
29300         this.adjustments = [
29301             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29302             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29303         ];
29304     }
29305
29306     if(this.draggable){
29307         this.dd = this.dynamic ?
29308             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29309         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29310     }
29311
29312     // public events
29313     this.addEvents({
29314         /**
29315          * @event beforeresize
29316          * Fired before resize is allowed. Set enabled to false to cancel resize.
29317          * @param {Roo.Resizable} this
29318          * @param {Roo.EventObject} e The mousedown event
29319          */
29320         "beforeresize" : true,
29321         /**
29322          * @event resizing
29323          * Fired a resizing.
29324          * @param {Roo.Resizable} this
29325          * @param {Number} x The new x position
29326          * @param {Number} y The new y position
29327          * @param {Number} w The new w width
29328          * @param {Number} h The new h hight
29329          * @param {Roo.EventObject} e The mouseup event
29330          */
29331         "resizing" : true,
29332         /**
29333          * @event resize
29334          * Fired after a resize.
29335          * @param {Roo.Resizable} this
29336          * @param {Number} width The new width
29337          * @param {Number} height The new height
29338          * @param {Roo.EventObject} e The mouseup event
29339          */
29340         "resize" : true
29341     });
29342
29343     if(this.width !== null && this.height !== null){
29344         this.resizeTo(this.width, this.height);
29345     }else{
29346         this.updateChildSize();
29347     }
29348     if(Roo.isIE){
29349         this.el.dom.style.zoom = 1;
29350     }
29351     Roo.Resizable.superclass.constructor.call(this);
29352 };
29353
29354 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29355         resizeChild : false,
29356         adjustments : [0, 0],
29357         minWidth : 5,
29358         minHeight : 5,
29359         maxWidth : 10000,
29360         maxHeight : 10000,
29361         enabled : true,
29362         animate : false,
29363         duration : .35,
29364         dynamic : false,
29365         handles : false,
29366         multiDirectional : false,
29367         disableTrackOver : false,
29368         easing : 'easeOutStrong',
29369         widthIncrement : 0,
29370         heightIncrement : 0,
29371         pinned : false,
29372         width : null,
29373         height : null,
29374         preserveRatio : false,
29375         transparent: false,
29376         minX: 0,
29377         minY: 0,
29378         draggable: false,
29379
29380         /**
29381          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29382          */
29383         constrainTo: undefined,
29384         /**
29385          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29386          */
29387         resizeRegion: undefined,
29388
29389
29390     /**
29391      * Perform a manual resize
29392      * @param {Number} width
29393      * @param {Number} height
29394      */
29395     resizeTo : function(width, height){
29396         this.el.setSize(width, height);
29397         this.updateChildSize();
29398         this.fireEvent("resize", this, width, height, null);
29399     },
29400
29401     // private
29402     startSizing : function(e, handle){
29403         this.fireEvent("beforeresize", this, e);
29404         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29405
29406             if(!this.overlay){
29407                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29408                 this.overlay.unselectable();
29409                 this.overlay.enableDisplayMode("block");
29410                 this.overlay.on("mousemove", this.onMouseMove, this);
29411                 this.overlay.on("mouseup", this.onMouseUp, this);
29412             }
29413             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29414
29415             this.resizing = true;
29416             this.startBox = this.el.getBox();
29417             this.startPoint = e.getXY();
29418             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29419                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29420
29421             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29422             this.overlay.show();
29423
29424             if(this.constrainTo) {
29425                 var ct = Roo.get(this.constrainTo);
29426                 this.resizeRegion = ct.getRegion().adjust(
29427                     ct.getFrameWidth('t'),
29428                     ct.getFrameWidth('l'),
29429                     -ct.getFrameWidth('b'),
29430                     -ct.getFrameWidth('r')
29431                 );
29432             }
29433
29434             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29435             this.proxy.show();
29436             this.proxy.setBox(this.startBox);
29437             if(!this.dynamic){
29438                 this.proxy.setStyle('visibility', 'visible');
29439             }
29440         }
29441     },
29442
29443     // private
29444     onMouseDown : function(handle, e){
29445         if(this.enabled){
29446             e.stopEvent();
29447             this.activeHandle = handle;
29448             this.startSizing(e, handle);
29449         }
29450     },
29451
29452     // private
29453     onMouseUp : function(e){
29454         var size = this.resizeElement();
29455         this.resizing = false;
29456         this.handleOut();
29457         this.overlay.hide();
29458         this.proxy.hide();
29459         this.fireEvent("resize", this, size.width, size.height, e);
29460     },
29461
29462     // private
29463     updateChildSize : function(){
29464         
29465         if(this.resizeChild){
29466             var el = this.el;
29467             var child = this.resizeChild;
29468             var adj = this.adjustments;
29469             if(el.dom.offsetWidth){
29470                 var b = el.getSize(true);
29471                 child.setSize(b.width+adj[0], b.height+adj[1]);
29472             }
29473             // Second call here for IE
29474             // The first call enables instant resizing and
29475             // the second call corrects scroll bars if they
29476             // exist
29477             if(Roo.isIE){
29478                 setTimeout(function(){
29479                     if(el.dom.offsetWidth){
29480                         var b = el.getSize(true);
29481                         child.setSize(b.width+adj[0], b.height+adj[1]);
29482                     }
29483                 }, 10);
29484             }
29485         }
29486     },
29487
29488     // private
29489     snap : function(value, inc, min){
29490         if(!inc || !value) return value;
29491         var newValue = value;
29492         var m = value % inc;
29493         if(m > 0){
29494             if(m > (inc/2)){
29495                 newValue = value + (inc-m);
29496             }else{
29497                 newValue = value - m;
29498             }
29499         }
29500         return Math.max(min, newValue);
29501     },
29502
29503     // private
29504     resizeElement : function(){
29505         var box = this.proxy.getBox();
29506         if(this.updateBox){
29507             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29508         }else{
29509             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29510         }
29511         this.updateChildSize();
29512         if(!this.dynamic){
29513             this.proxy.hide();
29514         }
29515         return box;
29516     },
29517
29518     // private
29519     constrain : function(v, diff, m, mx){
29520         if(v - diff < m){
29521             diff = v - m;
29522         }else if(v - diff > mx){
29523             diff = mx - v;
29524         }
29525         return diff;
29526     },
29527
29528     // private
29529     onMouseMove : function(e){
29530         
29531         if(this.enabled){
29532             try{// try catch so if something goes wrong the user doesn't get hung
29533
29534             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29535                 return;
29536             }
29537
29538             //var curXY = this.startPoint;
29539             var curSize = this.curSize || this.startBox;
29540             var x = this.startBox.x, y = this.startBox.y;
29541             var ox = x, oy = y;
29542             var w = curSize.width, h = curSize.height;
29543             var ow = w, oh = h;
29544             var mw = this.minWidth, mh = this.minHeight;
29545             var mxw = this.maxWidth, mxh = this.maxHeight;
29546             var wi = this.widthIncrement;
29547             var hi = this.heightIncrement;
29548
29549             var eventXY = e.getXY();
29550             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29551             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29552
29553             var pos = this.activeHandle.position;
29554
29555             switch(pos){
29556                 case "east":
29557                     w += diffX;
29558                     w = Math.min(Math.max(mw, w), mxw);
29559                     break;
29560              
29561                 case "south":
29562                     h += diffY;
29563                     h = Math.min(Math.max(mh, h), mxh);
29564                     break;
29565                 case "southeast":
29566                     w += diffX;
29567                     h += diffY;
29568                     w = Math.min(Math.max(mw, w), mxw);
29569                     h = Math.min(Math.max(mh, h), mxh);
29570                     break;
29571                 case "north":
29572                     diffY = this.constrain(h, diffY, mh, mxh);
29573                     y += diffY;
29574                     h -= diffY;
29575                     break;
29576                 case "hdrag":
29577                     
29578                     if (wi) {
29579                         var adiffX = Math.abs(diffX);
29580                         var sub = (adiffX % wi); // how much 
29581                         if (sub > (wi/2)) { // far enough to snap
29582                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29583                         } else {
29584                             // remove difference.. 
29585                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29586                         }
29587                     }
29588                     x += diffX;
29589                     x = Math.max(this.minX, x);
29590                     break;
29591                 case "west":
29592                     diffX = this.constrain(w, diffX, mw, mxw);
29593                     x += diffX;
29594                     w -= diffX;
29595                     break;
29596                 case "northeast":
29597                     w += diffX;
29598                     w = Math.min(Math.max(mw, w), mxw);
29599                     diffY = this.constrain(h, diffY, mh, mxh);
29600                     y += diffY;
29601                     h -= diffY;
29602                     break;
29603                 case "northwest":
29604                     diffX = this.constrain(w, diffX, mw, mxw);
29605                     diffY = this.constrain(h, diffY, mh, mxh);
29606                     y += diffY;
29607                     h -= diffY;
29608                     x += diffX;
29609                     w -= diffX;
29610                     break;
29611                case "southwest":
29612                     diffX = this.constrain(w, diffX, mw, mxw);
29613                     h += diffY;
29614                     h = Math.min(Math.max(mh, h), mxh);
29615                     x += diffX;
29616                     w -= diffX;
29617                     break;
29618             }
29619
29620             var sw = this.snap(w, wi, mw);
29621             var sh = this.snap(h, hi, mh);
29622             if(sw != w || sh != h){
29623                 switch(pos){
29624                     case "northeast":
29625                         y -= sh - h;
29626                     break;
29627                     case "north":
29628                         y -= sh - h;
29629                         break;
29630                     case "southwest":
29631                         x -= sw - w;
29632                     break;
29633                     case "west":
29634                         x -= sw - w;
29635                         break;
29636                     case "northwest":
29637                         x -= sw - w;
29638                         y -= sh - h;
29639                     break;
29640                 }
29641                 w = sw;
29642                 h = sh;
29643             }
29644
29645             if(this.preserveRatio){
29646                 switch(pos){
29647                     case "southeast":
29648                     case "east":
29649                         h = oh * (w/ow);
29650                         h = Math.min(Math.max(mh, h), mxh);
29651                         w = ow * (h/oh);
29652                        break;
29653                     case "south":
29654                         w = ow * (h/oh);
29655                         w = Math.min(Math.max(mw, w), mxw);
29656                         h = oh * (w/ow);
29657                         break;
29658                     case "northeast":
29659                         w = ow * (h/oh);
29660                         w = Math.min(Math.max(mw, w), mxw);
29661                         h = oh * (w/ow);
29662                     break;
29663                     case "north":
29664                         var tw = w;
29665                         w = ow * (h/oh);
29666                         w = Math.min(Math.max(mw, w), mxw);
29667                         h = oh * (w/ow);
29668                         x += (tw - w) / 2;
29669                         break;
29670                     case "southwest":
29671                         h = oh * (w/ow);
29672                         h = Math.min(Math.max(mh, h), mxh);
29673                         var tw = w;
29674                         w = ow * (h/oh);
29675                         x += tw - w;
29676                         break;
29677                     case "west":
29678                         var th = h;
29679                         h = oh * (w/ow);
29680                         h = Math.min(Math.max(mh, h), mxh);
29681                         y += (th - h) / 2;
29682                         var tw = w;
29683                         w = ow * (h/oh);
29684                         x += tw - w;
29685                        break;
29686                     case "northwest":
29687                         var tw = w;
29688                         var th = h;
29689                         h = oh * (w/ow);
29690                         h = Math.min(Math.max(mh, h), mxh);
29691                         w = ow * (h/oh);
29692                         y += th - h;
29693                         x += tw - w;
29694                        break;
29695
29696                 }
29697             }
29698             if (pos == 'hdrag') {
29699                 w = ow;
29700             }
29701             this.proxy.setBounds(x, y, w, h);
29702             if(this.dynamic){
29703                 this.resizeElement();
29704             }
29705             }catch(e){}
29706         }
29707         this.fireEvent("resizing", this, x, y, w, h, e);
29708     },
29709
29710     // private
29711     handleOver : function(){
29712         if(this.enabled){
29713             this.el.addClass("x-resizable-over");
29714         }
29715     },
29716
29717     // private
29718     handleOut : function(){
29719         if(!this.resizing){
29720             this.el.removeClass("x-resizable-over");
29721         }
29722     },
29723
29724     /**
29725      * Returns the element this component is bound to.
29726      * @return {Roo.Element}
29727      */
29728     getEl : function(){
29729         return this.el;
29730     },
29731
29732     /**
29733      * Returns the resizeChild element (or null).
29734      * @return {Roo.Element}
29735      */
29736     getResizeChild : function(){
29737         return this.resizeChild;
29738     },
29739     groupHandler : function()
29740     {
29741         
29742     },
29743     /**
29744      * Destroys this resizable. If the element was wrapped and
29745      * removeEl is not true then the element remains.
29746      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29747      */
29748     destroy : function(removeEl){
29749         this.proxy.remove();
29750         if(this.overlay){
29751             this.overlay.removeAllListeners();
29752             this.overlay.remove();
29753         }
29754         var ps = Roo.Resizable.positions;
29755         for(var k in ps){
29756             if(typeof ps[k] != "function" && this[ps[k]]){
29757                 var h = this[ps[k]];
29758                 h.el.removeAllListeners();
29759                 h.el.remove();
29760             }
29761         }
29762         if(removeEl){
29763             this.el.update("");
29764             this.el.remove();
29765         }
29766     }
29767 });
29768
29769 // private
29770 // hash to map config positions to true positions
29771 Roo.Resizable.positions = {
29772     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29773     hd: "hdrag"
29774 };
29775
29776 // private
29777 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29778     if(!this.tpl){
29779         // only initialize the template if resizable is used
29780         var tpl = Roo.DomHelper.createTemplate(
29781             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29782         );
29783         tpl.compile();
29784         Roo.Resizable.Handle.prototype.tpl = tpl;
29785     }
29786     this.position = pos;
29787     this.rz = rz;
29788     // show north drag fro topdra
29789     var handlepos = pos == 'hdrag' ? 'north' : pos;
29790     
29791     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29792     if (pos == 'hdrag') {
29793         this.el.setStyle('cursor', 'pointer');
29794     }
29795     this.el.unselectable();
29796     if(transparent){
29797         this.el.setOpacity(0);
29798     }
29799     this.el.on("mousedown", this.onMouseDown, this);
29800     if(!disableTrackOver){
29801         this.el.on("mouseover", this.onMouseOver, this);
29802         this.el.on("mouseout", this.onMouseOut, this);
29803     }
29804 };
29805
29806 // private
29807 Roo.Resizable.Handle.prototype = {
29808     afterResize : function(rz){
29809         Roo.log('after?');
29810         // do nothing
29811     },
29812     // private
29813     onMouseDown : function(e){
29814         this.rz.onMouseDown(this, e);
29815     },
29816     // private
29817     onMouseOver : function(e){
29818         this.rz.handleOver(this, e);
29819     },
29820     // private
29821     onMouseOut : function(e){
29822         this.rz.handleOut(this, e);
29823     }
29824 };/*
29825  * Based on:
29826  * Ext JS Library 1.1.1
29827  * Copyright(c) 2006-2007, Ext JS, LLC.
29828  *
29829  * Originally Released Under LGPL - original licence link has changed is not relivant.
29830  *
29831  * Fork - LGPL
29832  * <script type="text/javascript">
29833  */
29834
29835 /**
29836  * @class Roo.Editor
29837  * @extends Roo.Component
29838  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29839  * @constructor
29840  * Create a new Editor
29841  * @param {Roo.form.Field} field The Field object (or descendant)
29842  * @param {Object} config The config object
29843  */
29844 Roo.Editor = function(field, config){
29845     Roo.Editor.superclass.constructor.call(this, config);
29846     this.field = field;
29847     this.addEvents({
29848         /**
29849              * @event beforestartedit
29850              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29851              * false from the handler of this event.
29852              * @param {Editor} this
29853              * @param {Roo.Element} boundEl The underlying element bound to this editor
29854              * @param {Mixed} value The field value being set
29855              */
29856         "beforestartedit" : true,
29857         /**
29858              * @event startedit
29859              * Fires when this editor is displayed
29860              * @param {Roo.Element} boundEl The underlying element bound to this editor
29861              * @param {Mixed} value The starting field value
29862              */
29863         "startedit" : true,
29864         /**
29865              * @event beforecomplete
29866              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29867              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29868              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29869              * event will not fire since no edit actually occurred.
29870              * @param {Editor} this
29871              * @param {Mixed} value The current field value
29872              * @param {Mixed} startValue The original field value
29873              */
29874         "beforecomplete" : true,
29875         /**
29876              * @event complete
29877              * Fires after editing is complete and any changed value has been written to the underlying field.
29878              * @param {Editor} this
29879              * @param {Mixed} value The current field value
29880              * @param {Mixed} startValue The original field value
29881              */
29882         "complete" : true,
29883         /**
29884          * @event specialkey
29885          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29886          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29887          * @param {Roo.form.Field} this
29888          * @param {Roo.EventObject} e The event object
29889          */
29890         "specialkey" : true
29891     });
29892 };
29893
29894 Roo.extend(Roo.Editor, Roo.Component, {
29895     /**
29896      * @cfg {Boolean/String} autosize
29897      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29898      * or "height" to adopt the height only (defaults to false)
29899      */
29900     /**
29901      * @cfg {Boolean} revertInvalid
29902      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29903      * validation fails (defaults to true)
29904      */
29905     /**
29906      * @cfg {Boolean} ignoreNoChange
29907      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29908      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29909      * will never be ignored.
29910      */
29911     /**
29912      * @cfg {Boolean} hideEl
29913      * False to keep the bound element visible while the editor is displayed (defaults to true)
29914      */
29915     /**
29916      * @cfg {Mixed} value
29917      * The data value of the underlying field (defaults to "")
29918      */
29919     value : "",
29920     /**
29921      * @cfg {String} alignment
29922      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29923      */
29924     alignment: "c-c?",
29925     /**
29926      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29927      * for bottom-right shadow (defaults to "frame")
29928      */
29929     shadow : "frame",
29930     /**
29931      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29932      */
29933     constrain : false,
29934     /**
29935      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29936      */
29937     completeOnEnter : false,
29938     /**
29939      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29940      */
29941     cancelOnEsc : false,
29942     /**
29943      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29944      */
29945     updateEl : false,
29946
29947     // private
29948     onRender : function(ct, position){
29949         this.el = new Roo.Layer({
29950             shadow: this.shadow,
29951             cls: "x-editor",
29952             parentEl : ct,
29953             shim : this.shim,
29954             shadowOffset:4,
29955             id: this.id,
29956             constrain: this.constrain
29957         });
29958         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29959         if(this.field.msgTarget != 'title'){
29960             this.field.msgTarget = 'qtip';
29961         }
29962         this.field.render(this.el);
29963         if(Roo.isGecko){
29964             this.field.el.dom.setAttribute('autocomplete', 'off');
29965         }
29966         this.field.on("specialkey", this.onSpecialKey, this);
29967         if(this.swallowKeys){
29968             this.field.el.swallowEvent(['keydown','keypress']);
29969         }
29970         this.field.show();
29971         this.field.on("blur", this.onBlur, this);
29972         if(this.field.grow){
29973             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29974         }
29975     },
29976
29977     onSpecialKey : function(field, e)
29978     {
29979         //Roo.log('editor onSpecialKey');
29980         if(this.completeOnEnter && e.getKey() == e.ENTER){
29981             e.stopEvent();
29982             this.completeEdit();
29983             return;
29984         }
29985         // do not fire special key otherwise it might hide close the editor...
29986         if(e.getKey() == e.ENTER){    
29987             return;
29988         }
29989         if(this.cancelOnEsc && e.getKey() == e.ESC){
29990             this.cancelEdit();
29991             return;
29992         } 
29993         this.fireEvent('specialkey', field, e);
29994     
29995     },
29996
29997     /**
29998      * Starts the editing process and shows the editor.
29999      * @param {String/HTMLElement/Element} el The element to edit
30000      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30001       * to the innerHTML of el.
30002      */
30003     startEdit : function(el, value){
30004         if(this.editing){
30005             this.completeEdit();
30006         }
30007         this.boundEl = Roo.get(el);
30008         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30009         if(!this.rendered){
30010             this.render(this.parentEl || document.body);
30011         }
30012         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30013             return;
30014         }
30015         this.startValue = v;
30016         this.field.setValue(v);
30017         if(this.autoSize){
30018             var sz = this.boundEl.getSize();
30019             switch(this.autoSize){
30020                 case "width":
30021                 this.setSize(sz.width,  "");
30022                 break;
30023                 case "height":
30024                 this.setSize("",  sz.height);
30025                 break;
30026                 default:
30027                 this.setSize(sz.width,  sz.height);
30028             }
30029         }
30030         this.el.alignTo(this.boundEl, this.alignment);
30031         this.editing = true;
30032         if(Roo.QuickTips){
30033             Roo.QuickTips.disable();
30034         }
30035         this.show();
30036     },
30037
30038     /**
30039      * Sets the height and width of this editor.
30040      * @param {Number} width The new width
30041      * @param {Number} height The new height
30042      */
30043     setSize : function(w, h){
30044         this.field.setSize(w, h);
30045         if(this.el){
30046             this.el.sync();
30047         }
30048     },
30049
30050     /**
30051      * Realigns the editor to the bound field based on the current alignment config value.
30052      */
30053     realign : function(){
30054         this.el.alignTo(this.boundEl, this.alignment);
30055     },
30056
30057     /**
30058      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30059      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30060      */
30061     completeEdit : function(remainVisible){
30062         if(!this.editing){
30063             return;
30064         }
30065         var v = this.getValue();
30066         if(this.revertInvalid !== false && !this.field.isValid()){
30067             v = this.startValue;
30068             this.cancelEdit(true);
30069         }
30070         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30071             this.editing = false;
30072             this.hide();
30073             return;
30074         }
30075         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30076             this.editing = false;
30077             if(this.updateEl && this.boundEl){
30078                 this.boundEl.update(v);
30079             }
30080             if(remainVisible !== true){
30081                 this.hide();
30082             }
30083             this.fireEvent("complete", this, v, this.startValue);
30084         }
30085     },
30086
30087     // private
30088     onShow : function(){
30089         this.el.show();
30090         if(this.hideEl !== false){
30091             this.boundEl.hide();
30092         }
30093         this.field.show();
30094         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30095             this.fixIEFocus = true;
30096             this.deferredFocus.defer(50, this);
30097         }else{
30098             this.field.focus();
30099         }
30100         this.fireEvent("startedit", this.boundEl, this.startValue);
30101     },
30102
30103     deferredFocus : function(){
30104         if(this.editing){
30105             this.field.focus();
30106         }
30107     },
30108
30109     /**
30110      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30111      * reverted to the original starting value.
30112      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30113      * cancel (defaults to false)
30114      */
30115     cancelEdit : function(remainVisible){
30116         if(this.editing){
30117             this.setValue(this.startValue);
30118             if(remainVisible !== true){
30119                 this.hide();
30120             }
30121         }
30122     },
30123
30124     // private
30125     onBlur : function(){
30126         if(this.allowBlur !== true && this.editing){
30127             this.completeEdit();
30128         }
30129     },
30130
30131     // private
30132     onHide : function(){
30133         if(this.editing){
30134             this.completeEdit();
30135             return;
30136         }
30137         this.field.blur();
30138         if(this.field.collapse){
30139             this.field.collapse();
30140         }
30141         this.el.hide();
30142         if(this.hideEl !== false){
30143             this.boundEl.show();
30144         }
30145         if(Roo.QuickTips){
30146             Roo.QuickTips.enable();
30147         }
30148     },
30149
30150     /**
30151      * Sets the data value of the editor
30152      * @param {Mixed} value Any valid value supported by the underlying field
30153      */
30154     setValue : function(v){
30155         this.field.setValue(v);
30156     },
30157
30158     /**
30159      * Gets the data value of the editor
30160      * @return {Mixed} The data value
30161      */
30162     getValue : function(){
30163         return this.field.getValue();
30164     }
30165 });/*
30166  * Based on:
30167  * Ext JS Library 1.1.1
30168  * Copyright(c) 2006-2007, Ext JS, LLC.
30169  *
30170  * Originally Released Under LGPL - original licence link has changed is not relivant.
30171  *
30172  * Fork - LGPL
30173  * <script type="text/javascript">
30174  */
30175  
30176 /**
30177  * @class Roo.BasicDialog
30178  * @extends Roo.util.Observable
30179  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30180  * <pre><code>
30181 var dlg = new Roo.BasicDialog("my-dlg", {
30182     height: 200,
30183     width: 300,
30184     minHeight: 100,
30185     minWidth: 150,
30186     modal: true,
30187     proxyDrag: true,
30188     shadow: true
30189 });
30190 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30191 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30192 dlg.addButton('Cancel', dlg.hide, dlg);
30193 dlg.show();
30194 </code></pre>
30195   <b>A Dialog should always be a direct child of the body element.</b>
30196  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30197  * @cfg {String} title Default text to display in the title bar (defaults to null)
30198  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30199  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30200  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30201  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30202  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30203  * (defaults to null with no animation)
30204  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30205  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30206  * property for valid values (defaults to 'all')
30207  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30208  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30209  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30210  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30211  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30212  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30213  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30214  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30215  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30216  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30217  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30218  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30219  * draggable = true (defaults to false)
30220  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30221  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30222  * shadow (defaults to false)
30223  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30224  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30225  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30226  * @cfg {Array} buttons Array of buttons
30227  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30228  * @constructor
30229  * Create a new BasicDialog.
30230  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30231  * @param {Object} config Configuration options
30232  */
30233 Roo.BasicDialog = function(el, config){
30234     this.el = Roo.get(el);
30235     var dh = Roo.DomHelper;
30236     if(!this.el && config && config.autoCreate){
30237         if(typeof config.autoCreate == "object"){
30238             if(!config.autoCreate.id){
30239                 config.autoCreate.id = el;
30240             }
30241             this.el = dh.append(document.body,
30242                         config.autoCreate, true);
30243         }else{
30244             this.el = dh.append(document.body,
30245                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30246         }
30247     }
30248     el = this.el;
30249     el.setDisplayed(true);
30250     el.hide = this.hideAction;
30251     this.id = el.id;
30252     el.addClass("x-dlg");
30253
30254     Roo.apply(this, config);
30255
30256     this.proxy = el.createProxy("x-dlg-proxy");
30257     this.proxy.hide = this.hideAction;
30258     this.proxy.setOpacity(.5);
30259     this.proxy.hide();
30260
30261     if(config.width){
30262         el.setWidth(config.width);
30263     }
30264     if(config.height){
30265         el.setHeight(config.height);
30266     }
30267     this.size = el.getSize();
30268     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30269         this.xy = [config.x,config.y];
30270     }else{
30271         this.xy = el.getCenterXY(true);
30272     }
30273     /** The header element @type Roo.Element */
30274     this.header = el.child("> .x-dlg-hd");
30275     /** The body element @type Roo.Element */
30276     this.body = el.child("> .x-dlg-bd");
30277     /** The footer element @type Roo.Element */
30278     this.footer = el.child("> .x-dlg-ft");
30279
30280     if(!this.header){
30281         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30282     }
30283     if(!this.body){
30284         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30285     }
30286
30287     this.header.unselectable();
30288     if(this.title){
30289         this.header.update(this.title);
30290     }
30291     // this element allows the dialog to be focused for keyboard event
30292     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30293     this.focusEl.swallowEvent("click", true);
30294
30295     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30296
30297     // wrap the body and footer for special rendering
30298     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30299     if(this.footer){
30300         this.bwrap.dom.appendChild(this.footer.dom);
30301     }
30302
30303     this.bg = this.el.createChild({
30304         tag: "div", cls:"x-dlg-bg",
30305         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30306     });
30307     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30308
30309
30310     if(this.autoScroll !== false && !this.autoTabs){
30311         this.body.setStyle("overflow", "auto");
30312     }
30313
30314     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30315
30316     if(this.closable !== false){
30317         this.el.addClass("x-dlg-closable");
30318         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30319         this.close.on("click", this.closeClick, this);
30320         this.close.addClassOnOver("x-dlg-close-over");
30321     }
30322     if(this.collapsible !== false){
30323         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30324         this.collapseBtn.on("click", this.collapseClick, this);
30325         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30326         this.header.on("dblclick", this.collapseClick, this);
30327     }
30328     if(this.resizable !== false){
30329         this.el.addClass("x-dlg-resizable");
30330         this.resizer = new Roo.Resizable(el, {
30331             minWidth: this.minWidth || 80,
30332             minHeight:this.minHeight || 80,
30333             handles: this.resizeHandles || "all",
30334             pinned: true
30335         });
30336         this.resizer.on("beforeresize", this.beforeResize, this);
30337         this.resizer.on("resize", this.onResize, this);
30338     }
30339     if(this.draggable !== false){
30340         el.addClass("x-dlg-draggable");
30341         if (!this.proxyDrag) {
30342             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30343         }
30344         else {
30345             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30346         }
30347         dd.setHandleElId(this.header.id);
30348         dd.endDrag = this.endMove.createDelegate(this);
30349         dd.startDrag = this.startMove.createDelegate(this);
30350         dd.onDrag = this.onDrag.createDelegate(this);
30351         dd.scroll = false;
30352         this.dd = dd;
30353     }
30354     if(this.modal){
30355         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30356         this.mask.enableDisplayMode("block");
30357         this.mask.hide();
30358         this.el.addClass("x-dlg-modal");
30359     }
30360     if(this.shadow){
30361         this.shadow = new Roo.Shadow({
30362             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30363             offset : this.shadowOffset
30364         });
30365     }else{
30366         this.shadowOffset = 0;
30367     }
30368     if(Roo.useShims && this.shim !== false){
30369         this.shim = this.el.createShim();
30370         this.shim.hide = this.hideAction;
30371         this.shim.hide();
30372     }else{
30373         this.shim = false;
30374     }
30375     if(this.autoTabs){
30376         this.initTabs();
30377     }
30378     if (this.buttons) { 
30379         var bts= this.buttons;
30380         this.buttons = [];
30381         Roo.each(bts, function(b) {
30382             this.addButton(b);
30383         }, this);
30384     }
30385     
30386     
30387     this.addEvents({
30388         /**
30389          * @event keydown
30390          * Fires when a key is pressed
30391          * @param {Roo.BasicDialog} this
30392          * @param {Roo.EventObject} e
30393          */
30394         "keydown" : true,
30395         /**
30396          * @event move
30397          * Fires when this dialog is moved by the user.
30398          * @param {Roo.BasicDialog} this
30399          * @param {Number} x The new page X
30400          * @param {Number} y The new page Y
30401          */
30402         "move" : true,
30403         /**
30404          * @event resize
30405          * Fires when this dialog is resized by the user.
30406          * @param {Roo.BasicDialog} this
30407          * @param {Number} width The new width
30408          * @param {Number} height The new height
30409          */
30410         "resize" : true,
30411         /**
30412          * @event beforehide
30413          * Fires before this dialog is hidden.
30414          * @param {Roo.BasicDialog} this
30415          */
30416         "beforehide" : true,
30417         /**
30418          * @event hide
30419          * Fires when this dialog is hidden.
30420          * @param {Roo.BasicDialog} this
30421          */
30422         "hide" : true,
30423         /**
30424          * @event beforeshow
30425          * Fires before this dialog is shown.
30426          * @param {Roo.BasicDialog} this
30427          */
30428         "beforeshow" : true,
30429         /**
30430          * @event show
30431          * Fires when this dialog is shown.
30432          * @param {Roo.BasicDialog} this
30433          */
30434         "show" : true
30435     });
30436     el.on("keydown", this.onKeyDown, this);
30437     el.on("mousedown", this.toFront, this);
30438     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30439     this.el.hide();
30440     Roo.DialogManager.register(this);
30441     Roo.BasicDialog.superclass.constructor.call(this);
30442 };
30443
30444 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30445     shadowOffset: Roo.isIE ? 6 : 5,
30446     minHeight: 80,
30447     minWidth: 200,
30448     minButtonWidth: 75,
30449     defaultButton: null,
30450     buttonAlign: "right",
30451     tabTag: 'div',
30452     firstShow: true,
30453
30454     /**
30455      * Sets the dialog title text
30456      * @param {String} text The title text to display
30457      * @return {Roo.BasicDialog} this
30458      */
30459     setTitle : function(text){
30460         this.header.update(text);
30461         return this;
30462     },
30463
30464     // private
30465     closeClick : function(){
30466         this.hide();
30467     },
30468
30469     // private
30470     collapseClick : function(){
30471         this[this.collapsed ? "expand" : "collapse"]();
30472     },
30473
30474     /**
30475      * Collapses the dialog to its minimized state (only the title bar is visible).
30476      * Equivalent to the user clicking the collapse dialog button.
30477      */
30478     collapse : function(){
30479         if(!this.collapsed){
30480             this.collapsed = true;
30481             this.el.addClass("x-dlg-collapsed");
30482             this.restoreHeight = this.el.getHeight();
30483             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30484         }
30485     },
30486
30487     /**
30488      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30489      * clicking the expand dialog button.
30490      */
30491     expand : function(){
30492         if(this.collapsed){
30493             this.collapsed = false;
30494             this.el.removeClass("x-dlg-collapsed");
30495             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30496         }
30497     },
30498
30499     /**
30500      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30501      * @return {Roo.TabPanel} The tabs component
30502      */
30503     initTabs : function(){
30504         var tabs = this.getTabs();
30505         while(tabs.getTab(0)){
30506             tabs.removeTab(0);
30507         }
30508         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30509             var dom = el.dom;
30510             tabs.addTab(Roo.id(dom), dom.title);
30511             dom.title = "";
30512         });
30513         tabs.activate(0);
30514         return tabs;
30515     },
30516
30517     // private
30518     beforeResize : function(){
30519         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30520     },
30521
30522     // private
30523     onResize : function(){
30524         this.refreshSize();
30525         this.syncBodyHeight();
30526         this.adjustAssets();
30527         this.focus();
30528         this.fireEvent("resize", this, this.size.width, this.size.height);
30529     },
30530
30531     // private
30532     onKeyDown : function(e){
30533         if(this.isVisible()){
30534             this.fireEvent("keydown", this, e);
30535         }
30536     },
30537
30538     /**
30539      * Resizes the dialog.
30540      * @param {Number} width
30541      * @param {Number} height
30542      * @return {Roo.BasicDialog} this
30543      */
30544     resizeTo : function(width, height){
30545         this.el.setSize(width, height);
30546         this.size = {width: width, height: height};
30547         this.syncBodyHeight();
30548         if(this.fixedcenter){
30549             this.center();
30550         }
30551         if(this.isVisible()){
30552             this.constrainXY();
30553             this.adjustAssets();
30554         }
30555         this.fireEvent("resize", this, width, height);
30556         return this;
30557     },
30558
30559
30560     /**
30561      * Resizes the dialog to fit the specified content size.
30562      * @param {Number} width
30563      * @param {Number} height
30564      * @return {Roo.BasicDialog} this
30565      */
30566     setContentSize : function(w, h){
30567         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30568         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30569         //if(!this.el.isBorderBox()){
30570             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30571             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30572         //}
30573         if(this.tabs){
30574             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30575             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30576         }
30577         this.resizeTo(w, h);
30578         return this;
30579     },
30580
30581     /**
30582      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30583      * executed in response to a particular key being pressed while the dialog is active.
30584      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30585      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30586      * @param {Function} fn The function to call
30587      * @param {Object} scope (optional) The scope of the function
30588      * @return {Roo.BasicDialog} this
30589      */
30590     addKeyListener : function(key, fn, scope){
30591         var keyCode, shift, ctrl, alt;
30592         if(typeof key == "object" && !(key instanceof Array)){
30593             keyCode = key["key"];
30594             shift = key["shift"];
30595             ctrl = key["ctrl"];
30596             alt = key["alt"];
30597         }else{
30598             keyCode = key;
30599         }
30600         var handler = function(dlg, e){
30601             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30602                 var k = e.getKey();
30603                 if(keyCode instanceof Array){
30604                     for(var i = 0, len = keyCode.length; i < len; i++){
30605                         if(keyCode[i] == k){
30606                           fn.call(scope || window, dlg, k, e);
30607                           return;
30608                         }
30609                     }
30610                 }else{
30611                     if(k == keyCode){
30612                         fn.call(scope || window, dlg, k, e);
30613                     }
30614                 }
30615             }
30616         };
30617         this.on("keydown", handler);
30618         return this;
30619     },
30620
30621     /**
30622      * Returns the TabPanel component (creates it if it doesn't exist).
30623      * Note: If you wish to simply check for the existence of tabs without creating them,
30624      * check for a null 'tabs' property.
30625      * @return {Roo.TabPanel} The tabs component
30626      */
30627     getTabs : function(){
30628         if(!this.tabs){
30629             this.el.addClass("x-dlg-auto-tabs");
30630             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30631             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30632         }
30633         return this.tabs;
30634     },
30635
30636     /**
30637      * Adds a button to the footer section of the dialog.
30638      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30639      * object or a valid Roo.DomHelper element config
30640      * @param {Function} handler The function called when the button is clicked
30641      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30642      * @return {Roo.Button} The new button
30643      */
30644     addButton : function(config, handler, scope){
30645         var dh = Roo.DomHelper;
30646         if(!this.footer){
30647             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30648         }
30649         if(!this.btnContainer){
30650             var tb = this.footer.createChild({
30651
30652                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30653                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30654             }, null, true);
30655             this.btnContainer = tb.firstChild.firstChild.firstChild;
30656         }
30657         var bconfig = {
30658             handler: handler,
30659             scope: scope,
30660             minWidth: this.minButtonWidth,
30661             hideParent:true
30662         };
30663         if(typeof config == "string"){
30664             bconfig.text = config;
30665         }else{
30666             if(config.tag){
30667                 bconfig.dhconfig = config;
30668             }else{
30669                 Roo.apply(bconfig, config);
30670             }
30671         }
30672         var fc = false;
30673         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30674             bconfig.position = Math.max(0, bconfig.position);
30675             fc = this.btnContainer.childNodes[bconfig.position];
30676         }
30677          
30678         var btn = new Roo.Button(
30679             fc ? 
30680                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30681                 : this.btnContainer.appendChild(document.createElement("td")),
30682             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30683             bconfig
30684         );
30685         this.syncBodyHeight();
30686         if(!this.buttons){
30687             /**
30688              * Array of all the buttons that have been added to this dialog via addButton
30689              * @type Array
30690              */
30691             this.buttons = [];
30692         }
30693         this.buttons.push(btn);
30694         return btn;
30695     },
30696
30697     /**
30698      * Sets the default button to be focused when the dialog is displayed.
30699      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30700      * @return {Roo.BasicDialog} this
30701      */
30702     setDefaultButton : function(btn){
30703         this.defaultButton = btn;
30704         return this;
30705     },
30706
30707     // private
30708     getHeaderFooterHeight : function(safe){
30709         var height = 0;
30710         if(this.header){
30711            height += this.header.getHeight();
30712         }
30713         if(this.footer){
30714            var fm = this.footer.getMargins();
30715             height += (this.footer.getHeight()+fm.top+fm.bottom);
30716         }
30717         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30718         height += this.centerBg.getPadding("tb");
30719         return height;
30720     },
30721
30722     // private
30723     syncBodyHeight : function()
30724     {
30725         var bd = this.body, // the text
30726             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30727             bw = this.bwrap;
30728         var height = this.size.height - this.getHeaderFooterHeight(false);
30729         bd.setHeight(height-bd.getMargins("tb"));
30730         var hh = this.header.getHeight();
30731         var h = this.size.height-hh;
30732         cb.setHeight(h);
30733         
30734         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30735         bw.setHeight(h-cb.getPadding("tb"));
30736         
30737         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30738         bd.setWidth(bw.getWidth(true));
30739         if(this.tabs){
30740             this.tabs.syncHeight();
30741             if(Roo.isIE){
30742                 this.tabs.el.repaint();
30743             }
30744         }
30745     },
30746
30747     /**
30748      * Restores the previous state of the dialog if Roo.state is configured.
30749      * @return {Roo.BasicDialog} this
30750      */
30751     restoreState : function(){
30752         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30753         if(box && box.width){
30754             this.xy = [box.x, box.y];
30755             this.resizeTo(box.width, box.height);
30756         }
30757         return this;
30758     },
30759
30760     // private
30761     beforeShow : function(){
30762         this.expand();
30763         if(this.fixedcenter){
30764             this.xy = this.el.getCenterXY(true);
30765         }
30766         if(this.modal){
30767             Roo.get(document.body).addClass("x-body-masked");
30768             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30769             this.mask.show();
30770         }
30771         this.constrainXY();
30772     },
30773
30774     // private
30775     animShow : function(){
30776         var b = Roo.get(this.animateTarget).getBox();
30777         this.proxy.setSize(b.width, b.height);
30778         this.proxy.setLocation(b.x, b.y);
30779         this.proxy.show();
30780         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30781                     true, .35, this.showEl.createDelegate(this));
30782     },
30783
30784     /**
30785      * Shows the dialog.
30786      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30787      * @return {Roo.BasicDialog} this
30788      */
30789     show : function(animateTarget){
30790         if (this.fireEvent("beforeshow", this) === false){
30791             return;
30792         }
30793         if(this.syncHeightBeforeShow){
30794             this.syncBodyHeight();
30795         }else if(this.firstShow){
30796             this.firstShow = false;
30797             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30798         }
30799         this.animateTarget = animateTarget || this.animateTarget;
30800         if(!this.el.isVisible()){
30801             this.beforeShow();
30802             if(this.animateTarget && Roo.get(this.animateTarget)){
30803                 this.animShow();
30804             }else{
30805                 this.showEl();
30806             }
30807         }
30808         return this;
30809     },
30810
30811     // private
30812     showEl : function(){
30813         this.proxy.hide();
30814         this.el.setXY(this.xy);
30815         this.el.show();
30816         this.adjustAssets(true);
30817         this.toFront();
30818         this.focus();
30819         // IE peekaboo bug - fix found by Dave Fenwick
30820         if(Roo.isIE){
30821             this.el.repaint();
30822         }
30823         this.fireEvent("show", this);
30824     },
30825
30826     /**
30827      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30828      * dialog itself will receive focus.
30829      */
30830     focus : function(){
30831         if(this.defaultButton){
30832             this.defaultButton.focus();
30833         }else{
30834             this.focusEl.focus();
30835         }
30836     },
30837
30838     // private
30839     constrainXY : function(){
30840         if(this.constraintoviewport !== false){
30841             if(!this.viewSize){
30842                 if(this.container){
30843                     var s = this.container.getSize();
30844                     this.viewSize = [s.width, s.height];
30845                 }else{
30846                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30847                 }
30848             }
30849             var s = Roo.get(this.container||document).getScroll();
30850
30851             var x = this.xy[0], y = this.xy[1];
30852             var w = this.size.width, h = this.size.height;
30853             var vw = this.viewSize[0], vh = this.viewSize[1];
30854             // only move it if it needs it
30855             var moved = false;
30856             // first validate right/bottom
30857             if(x + w > vw+s.left){
30858                 x = vw - w;
30859                 moved = true;
30860             }
30861             if(y + h > vh+s.top){
30862                 y = vh - h;
30863                 moved = true;
30864             }
30865             // then make sure top/left isn't negative
30866             if(x < s.left){
30867                 x = s.left;
30868                 moved = true;
30869             }
30870             if(y < s.top){
30871                 y = s.top;
30872                 moved = true;
30873             }
30874             if(moved){
30875                 // cache xy
30876                 this.xy = [x, y];
30877                 if(this.isVisible()){
30878                     this.el.setLocation(x, y);
30879                     this.adjustAssets();
30880                 }
30881             }
30882         }
30883     },
30884
30885     // private
30886     onDrag : function(){
30887         if(!this.proxyDrag){
30888             this.xy = this.el.getXY();
30889             this.adjustAssets();
30890         }
30891     },
30892
30893     // private
30894     adjustAssets : function(doShow){
30895         var x = this.xy[0], y = this.xy[1];
30896         var w = this.size.width, h = this.size.height;
30897         if(doShow === true){
30898             if(this.shadow){
30899                 this.shadow.show(this.el);
30900             }
30901             if(this.shim){
30902                 this.shim.show();
30903             }
30904         }
30905         if(this.shadow && this.shadow.isVisible()){
30906             this.shadow.show(this.el);
30907         }
30908         if(this.shim && this.shim.isVisible()){
30909             this.shim.setBounds(x, y, w, h);
30910         }
30911     },
30912
30913     // private
30914     adjustViewport : function(w, h){
30915         if(!w || !h){
30916             w = Roo.lib.Dom.getViewWidth();
30917             h = Roo.lib.Dom.getViewHeight();
30918         }
30919         // cache the size
30920         this.viewSize = [w, h];
30921         if(this.modal && this.mask.isVisible()){
30922             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30923             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30924         }
30925         if(this.isVisible()){
30926             this.constrainXY();
30927         }
30928     },
30929
30930     /**
30931      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30932      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30933      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30934      */
30935     destroy : function(removeEl){
30936         if(this.isVisible()){
30937             this.animateTarget = null;
30938             this.hide();
30939         }
30940         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30941         if(this.tabs){
30942             this.tabs.destroy(removeEl);
30943         }
30944         Roo.destroy(
30945              this.shim,
30946              this.proxy,
30947              this.resizer,
30948              this.close,
30949              this.mask
30950         );
30951         if(this.dd){
30952             this.dd.unreg();
30953         }
30954         if(this.buttons){
30955            for(var i = 0, len = this.buttons.length; i < len; i++){
30956                this.buttons[i].destroy();
30957            }
30958         }
30959         this.el.removeAllListeners();
30960         if(removeEl === true){
30961             this.el.update("");
30962             this.el.remove();
30963         }
30964         Roo.DialogManager.unregister(this);
30965     },
30966
30967     // private
30968     startMove : function(){
30969         if(this.proxyDrag){
30970             this.proxy.show();
30971         }
30972         if(this.constraintoviewport !== false){
30973             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30974         }
30975     },
30976
30977     // private
30978     endMove : function(){
30979         if(!this.proxyDrag){
30980             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30981         }else{
30982             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30983             this.proxy.hide();
30984         }
30985         this.refreshSize();
30986         this.adjustAssets();
30987         this.focus();
30988         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30989     },
30990
30991     /**
30992      * Brings this dialog to the front of any other visible dialogs
30993      * @return {Roo.BasicDialog} this
30994      */
30995     toFront : function(){
30996         Roo.DialogManager.bringToFront(this);
30997         return this;
30998     },
30999
31000     /**
31001      * Sends this dialog to the back (under) of any other visible dialogs
31002      * @return {Roo.BasicDialog} this
31003      */
31004     toBack : function(){
31005         Roo.DialogManager.sendToBack(this);
31006         return this;
31007     },
31008
31009     /**
31010      * Centers this dialog in the viewport
31011      * @return {Roo.BasicDialog} this
31012      */
31013     center : function(){
31014         var xy = this.el.getCenterXY(true);
31015         this.moveTo(xy[0], xy[1]);
31016         return this;
31017     },
31018
31019     /**
31020      * Moves the dialog's top-left corner to the specified point
31021      * @param {Number} x
31022      * @param {Number} y
31023      * @return {Roo.BasicDialog} this
31024      */
31025     moveTo : function(x, y){
31026         this.xy = [x,y];
31027         if(this.isVisible()){
31028             this.el.setXY(this.xy);
31029             this.adjustAssets();
31030         }
31031         return this;
31032     },
31033
31034     /**
31035      * Aligns the dialog to the specified element
31036      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31037      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31038      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31039      * @return {Roo.BasicDialog} this
31040      */
31041     alignTo : function(element, position, offsets){
31042         this.xy = this.el.getAlignToXY(element, position, offsets);
31043         if(this.isVisible()){
31044             this.el.setXY(this.xy);
31045             this.adjustAssets();
31046         }
31047         return this;
31048     },
31049
31050     /**
31051      * Anchors an element to another element and realigns it when the window is resized.
31052      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31053      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31054      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31055      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31056      * is a number, it is used as the buffer delay (defaults to 50ms).
31057      * @return {Roo.BasicDialog} this
31058      */
31059     anchorTo : function(el, alignment, offsets, monitorScroll){
31060         var action = function(){
31061             this.alignTo(el, alignment, offsets);
31062         };
31063         Roo.EventManager.onWindowResize(action, this);
31064         var tm = typeof monitorScroll;
31065         if(tm != 'undefined'){
31066             Roo.EventManager.on(window, 'scroll', action, this,
31067                 {buffer: tm == 'number' ? monitorScroll : 50});
31068         }
31069         action.call(this);
31070         return this;
31071     },
31072
31073     /**
31074      * Returns true if the dialog is visible
31075      * @return {Boolean}
31076      */
31077     isVisible : function(){
31078         return this.el.isVisible();
31079     },
31080
31081     // private
31082     animHide : function(callback){
31083         var b = Roo.get(this.animateTarget).getBox();
31084         this.proxy.show();
31085         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31086         this.el.hide();
31087         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31088                     this.hideEl.createDelegate(this, [callback]));
31089     },
31090
31091     /**
31092      * Hides the dialog.
31093      * @param {Function} callback (optional) Function to call when the dialog is hidden
31094      * @return {Roo.BasicDialog} this
31095      */
31096     hide : function(callback){
31097         if (this.fireEvent("beforehide", this) === false){
31098             return;
31099         }
31100         if(this.shadow){
31101             this.shadow.hide();
31102         }
31103         if(this.shim) {
31104           this.shim.hide();
31105         }
31106         // sometimes animateTarget seems to get set.. causing problems...
31107         // this just double checks..
31108         if(this.animateTarget && Roo.get(this.animateTarget)) {
31109            this.animHide(callback);
31110         }else{
31111             this.el.hide();
31112             this.hideEl(callback);
31113         }
31114         return this;
31115     },
31116
31117     // private
31118     hideEl : function(callback){
31119         this.proxy.hide();
31120         if(this.modal){
31121             this.mask.hide();
31122             Roo.get(document.body).removeClass("x-body-masked");
31123         }
31124         this.fireEvent("hide", this);
31125         if(typeof callback == "function"){
31126             callback();
31127         }
31128     },
31129
31130     // private
31131     hideAction : function(){
31132         this.setLeft("-10000px");
31133         this.setTop("-10000px");
31134         this.setStyle("visibility", "hidden");
31135     },
31136
31137     // private
31138     refreshSize : function(){
31139         this.size = this.el.getSize();
31140         this.xy = this.el.getXY();
31141         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31142     },
31143
31144     // private
31145     // z-index is managed by the DialogManager and may be overwritten at any time
31146     setZIndex : function(index){
31147         if(this.modal){
31148             this.mask.setStyle("z-index", index);
31149         }
31150         if(this.shim){
31151             this.shim.setStyle("z-index", ++index);
31152         }
31153         if(this.shadow){
31154             this.shadow.setZIndex(++index);
31155         }
31156         this.el.setStyle("z-index", ++index);
31157         if(this.proxy){
31158             this.proxy.setStyle("z-index", ++index);
31159         }
31160         if(this.resizer){
31161             this.resizer.proxy.setStyle("z-index", ++index);
31162         }
31163
31164         this.lastZIndex = index;
31165     },
31166
31167     /**
31168      * Returns the element for this dialog
31169      * @return {Roo.Element} The underlying dialog Element
31170      */
31171     getEl : function(){
31172         return this.el;
31173     }
31174 });
31175
31176 /**
31177  * @class Roo.DialogManager
31178  * Provides global access to BasicDialogs that have been created and
31179  * support for z-indexing (layering) multiple open dialogs.
31180  */
31181 Roo.DialogManager = function(){
31182     var list = {};
31183     var accessList = [];
31184     var front = null;
31185
31186     // private
31187     var sortDialogs = function(d1, d2){
31188         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31189     };
31190
31191     // private
31192     var orderDialogs = function(){
31193         accessList.sort(sortDialogs);
31194         var seed = Roo.DialogManager.zseed;
31195         for(var i = 0, len = accessList.length; i < len; i++){
31196             var dlg = accessList[i];
31197             if(dlg){
31198                 dlg.setZIndex(seed + (i*10));
31199             }
31200         }
31201     };
31202
31203     return {
31204         /**
31205          * The starting z-index for BasicDialogs (defaults to 9000)
31206          * @type Number The z-index value
31207          */
31208         zseed : 9000,
31209
31210         // private
31211         register : function(dlg){
31212             list[dlg.id] = dlg;
31213             accessList.push(dlg);
31214         },
31215
31216         // private
31217         unregister : function(dlg){
31218             delete list[dlg.id];
31219             var i=0;
31220             var len=0;
31221             if(!accessList.indexOf){
31222                 for(  i = 0, len = accessList.length; i < len; i++){
31223                     if(accessList[i] == dlg){
31224                         accessList.splice(i, 1);
31225                         return;
31226                     }
31227                 }
31228             }else{
31229                  i = accessList.indexOf(dlg);
31230                 if(i != -1){
31231                     accessList.splice(i, 1);
31232                 }
31233             }
31234         },
31235
31236         /**
31237          * Gets a registered dialog by id
31238          * @param {String/Object} id The id of the dialog or a dialog
31239          * @return {Roo.BasicDialog} this
31240          */
31241         get : function(id){
31242             return typeof id == "object" ? id : list[id];
31243         },
31244
31245         /**
31246          * Brings the specified dialog to the front
31247          * @param {String/Object} dlg The id of the dialog or a dialog
31248          * @return {Roo.BasicDialog} this
31249          */
31250         bringToFront : function(dlg){
31251             dlg = this.get(dlg);
31252             if(dlg != front){
31253                 front = dlg;
31254                 dlg._lastAccess = new Date().getTime();
31255                 orderDialogs();
31256             }
31257             return dlg;
31258         },
31259
31260         /**
31261          * Sends the specified dialog to the back
31262          * @param {String/Object} dlg The id of the dialog or a dialog
31263          * @return {Roo.BasicDialog} this
31264          */
31265         sendToBack : function(dlg){
31266             dlg = this.get(dlg);
31267             dlg._lastAccess = -(new Date().getTime());
31268             orderDialogs();
31269             return dlg;
31270         },
31271
31272         /**
31273          * Hides all dialogs
31274          */
31275         hideAll : function(){
31276             for(var id in list){
31277                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31278                     list[id].hide();
31279                 }
31280             }
31281         }
31282     };
31283 }();
31284
31285 /**
31286  * @class Roo.LayoutDialog
31287  * @extends Roo.BasicDialog
31288  * Dialog which provides adjustments for working with a layout in a Dialog.
31289  * Add your necessary layout config options to the dialog's config.<br>
31290  * Example usage (including a nested layout):
31291  * <pre><code>
31292 if(!dialog){
31293     dialog = new Roo.LayoutDialog("download-dlg", {
31294         modal: true,
31295         width:600,
31296         height:450,
31297         shadow:true,
31298         minWidth:500,
31299         minHeight:350,
31300         autoTabs:true,
31301         proxyDrag:true,
31302         // layout config merges with the dialog config
31303         center:{
31304             tabPosition: "top",
31305             alwaysShowTabs: true
31306         }
31307     });
31308     dialog.addKeyListener(27, dialog.hide, dialog);
31309     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31310     dialog.addButton("Build It!", this.getDownload, this);
31311
31312     // we can even add nested layouts
31313     var innerLayout = new Roo.BorderLayout("dl-inner", {
31314         east: {
31315             initialSize: 200,
31316             autoScroll:true,
31317             split:true
31318         },
31319         center: {
31320             autoScroll:true
31321         }
31322     });
31323     innerLayout.beginUpdate();
31324     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31325     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31326     innerLayout.endUpdate(true);
31327
31328     var layout = dialog.getLayout();
31329     layout.beginUpdate();
31330     layout.add("center", new Roo.ContentPanel("standard-panel",
31331                         {title: "Download the Source", fitToFrame:true}));
31332     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31333                {title: "Build your own roo.js"}));
31334     layout.getRegion("center").showPanel(sp);
31335     layout.endUpdate();
31336 }
31337 </code></pre>
31338     * @constructor
31339     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31340     * @param {Object} config configuration options
31341   */
31342 Roo.LayoutDialog = function(el, cfg){
31343     
31344     var config=  cfg;
31345     if (typeof(cfg) == 'undefined') {
31346         config = Roo.apply({}, el);
31347         // not sure why we use documentElement here.. - it should always be body.
31348         // IE7 borks horribly if we use documentElement.
31349         // webkit also does not like documentElement - it creates a body element...
31350         el = Roo.get( document.body || document.documentElement ).createChild();
31351         //config.autoCreate = true;
31352     }
31353     
31354     
31355     config.autoTabs = false;
31356     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31357     this.body.setStyle({overflow:"hidden", position:"relative"});
31358     this.layout = new Roo.BorderLayout(this.body.dom, config);
31359     this.layout.monitorWindowResize = false;
31360     this.el.addClass("x-dlg-auto-layout");
31361     // fix case when center region overwrites center function
31362     this.center = Roo.BasicDialog.prototype.center;
31363     this.on("show", this.layout.layout, this.layout, true);
31364     if (config.items) {
31365         var xitems = config.items;
31366         delete config.items;
31367         Roo.each(xitems, this.addxtype, this);
31368     }
31369     
31370     
31371 };
31372 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31373     /**
31374      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31375      * @deprecated
31376      */
31377     endUpdate : function(){
31378         this.layout.endUpdate();
31379     },
31380
31381     /**
31382      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31383      *  @deprecated
31384      */
31385     beginUpdate : function(){
31386         this.layout.beginUpdate();
31387     },
31388
31389     /**
31390      * Get the BorderLayout for this dialog
31391      * @return {Roo.BorderLayout}
31392      */
31393     getLayout : function(){
31394         return this.layout;
31395     },
31396
31397     showEl : function(){
31398         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31399         if(Roo.isIE7){
31400             this.layout.layout();
31401         }
31402     },
31403
31404     // private
31405     // Use the syncHeightBeforeShow config option to control this automatically
31406     syncBodyHeight : function(){
31407         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31408         if(this.layout){this.layout.layout();}
31409     },
31410     
31411       /**
31412      * Add an xtype element (actually adds to the layout.)
31413      * @return {Object} xdata xtype object data.
31414      */
31415     
31416     addxtype : function(c) {
31417         return this.layout.addxtype(c);
31418     }
31419 });/*
31420  * Based on:
31421  * Ext JS Library 1.1.1
31422  * Copyright(c) 2006-2007, Ext JS, LLC.
31423  *
31424  * Originally Released Under LGPL - original licence link has changed is not relivant.
31425  *
31426  * Fork - LGPL
31427  * <script type="text/javascript">
31428  */
31429  
31430 /**
31431  * @class Roo.MessageBox
31432  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31433  * Example usage:
31434  *<pre><code>
31435 // Basic alert:
31436 Roo.Msg.alert('Status', 'Changes saved successfully.');
31437
31438 // Prompt for user data:
31439 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31440     if (btn == 'ok'){
31441         // process text value...
31442     }
31443 });
31444
31445 // Show a dialog using config options:
31446 Roo.Msg.show({
31447    title:'Save Changes?',
31448    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31449    buttons: Roo.Msg.YESNOCANCEL,
31450    fn: processResult,
31451    animEl: 'elId'
31452 });
31453 </code></pre>
31454  * @singleton
31455  */
31456 Roo.MessageBox = function(){
31457     var dlg, opt, mask, waitTimer;
31458     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31459     var buttons, activeTextEl, bwidth;
31460
31461     // private
31462     var handleButton = function(button){
31463         dlg.hide();
31464         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31465     };
31466
31467     // private
31468     var handleHide = function(){
31469         if(opt && opt.cls){
31470             dlg.el.removeClass(opt.cls);
31471         }
31472         if(waitTimer){
31473             Roo.TaskMgr.stop(waitTimer);
31474             waitTimer = null;
31475         }
31476     };
31477
31478     // private
31479     var updateButtons = function(b){
31480         var width = 0;
31481         if(!b){
31482             buttons["ok"].hide();
31483             buttons["cancel"].hide();
31484             buttons["yes"].hide();
31485             buttons["no"].hide();
31486             dlg.footer.dom.style.display = 'none';
31487             return width;
31488         }
31489         dlg.footer.dom.style.display = '';
31490         for(var k in buttons){
31491             if(typeof buttons[k] != "function"){
31492                 if(b[k]){
31493                     buttons[k].show();
31494                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31495                     width += buttons[k].el.getWidth()+15;
31496                 }else{
31497                     buttons[k].hide();
31498                 }
31499             }
31500         }
31501         return width;
31502     };
31503
31504     // private
31505     var handleEsc = function(d, k, e){
31506         if(opt && opt.closable !== false){
31507             dlg.hide();
31508         }
31509         if(e){
31510             e.stopEvent();
31511         }
31512     };
31513
31514     return {
31515         /**
31516          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31517          * @return {Roo.BasicDialog} The BasicDialog element
31518          */
31519         getDialog : function(){
31520            if(!dlg){
31521                 dlg = new Roo.BasicDialog("x-msg-box", {
31522                     autoCreate : true,
31523                     shadow: true,
31524                     draggable: true,
31525                     resizable:false,
31526                     constraintoviewport:false,
31527                     fixedcenter:true,
31528                     collapsible : false,
31529                     shim:true,
31530                     modal: true,
31531                     width:400, height:100,
31532                     buttonAlign:"center",
31533                     closeClick : function(){
31534                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31535                             handleButton("no");
31536                         }else{
31537                             handleButton("cancel");
31538                         }
31539                     }
31540                 });
31541                 dlg.on("hide", handleHide);
31542                 mask = dlg.mask;
31543                 dlg.addKeyListener(27, handleEsc);
31544                 buttons = {};
31545                 var bt = this.buttonText;
31546                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31547                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31548                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31549                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31550                 bodyEl = dlg.body.createChild({
31551
31552                     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>'
31553                 });
31554                 msgEl = bodyEl.dom.firstChild;
31555                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31556                 textboxEl.enableDisplayMode();
31557                 textboxEl.addKeyListener([10,13], function(){
31558                     if(dlg.isVisible() && opt && opt.buttons){
31559                         if(opt.buttons.ok){
31560                             handleButton("ok");
31561                         }else if(opt.buttons.yes){
31562                             handleButton("yes");
31563                         }
31564                     }
31565                 });
31566                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31567                 textareaEl.enableDisplayMode();
31568                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31569                 progressEl.enableDisplayMode();
31570                 var pf = progressEl.dom.firstChild;
31571                 if (pf) {
31572                     pp = Roo.get(pf.firstChild);
31573                     pp.setHeight(pf.offsetHeight);
31574                 }
31575                 
31576             }
31577             return dlg;
31578         },
31579
31580         /**
31581          * Updates the message box body text
31582          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31583          * the XHTML-compliant non-breaking space character '&amp;#160;')
31584          * @return {Roo.MessageBox} This message box
31585          */
31586         updateText : function(text){
31587             if(!dlg.isVisible() && !opt.width){
31588                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31589             }
31590             msgEl.innerHTML = text || '&#160;';
31591       
31592             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31593             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31594             var w = Math.max(
31595                     Math.min(opt.width || cw , this.maxWidth), 
31596                     Math.max(opt.minWidth || this.minWidth, bwidth)
31597             );
31598             if(opt.prompt){
31599                 activeTextEl.setWidth(w);
31600             }
31601             if(dlg.isVisible()){
31602                 dlg.fixedcenter = false;
31603             }
31604             // to big, make it scroll. = But as usual stupid IE does not support
31605             // !important..
31606             
31607             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31608                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31609                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31610             } else {
31611                 bodyEl.dom.style.height = '';
31612                 bodyEl.dom.style.overflowY = '';
31613             }
31614             if (cw > w) {
31615                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31616             } else {
31617                 bodyEl.dom.style.overflowX = '';
31618             }
31619             
31620             dlg.setContentSize(w, bodyEl.getHeight());
31621             if(dlg.isVisible()){
31622                 dlg.fixedcenter = true;
31623             }
31624             return this;
31625         },
31626
31627         /**
31628          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31629          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31630          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31631          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31632          * @return {Roo.MessageBox} This message box
31633          */
31634         updateProgress : function(value, text){
31635             if(text){
31636                 this.updateText(text);
31637             }
31638             if (pp) { // weird bug on my firefox - for some reason this is not defined
31639                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31640             }
31641             return this;
31642         },        
31643
31644         /**
31645          * Returns true if the message box is currently displayed
31646          * @return {Boolean} True if the message box is visible, else false
31647          */
31648         isVisible : function(){
31649             return dlg && dlg.isVisible();  
31650         },
31651
31652         /**
31653          * Hides the message box if it is displayed
31654          */
31655         hide : function(){
31656             if(this.isVisible()){
31657                 dlg.hide();
31658             }  
31659         },
31660
31661         /**
31662          * Displays a new message box, or reinitializes an existing message box, based on the config options
31663          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31664          * The following config object properties are supported:
31665          * <pre>
31666 Property    Type             Description
31667 ----------  ---------------  ------------------------------------------------------------------------------------
31668 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31669                                    closes (defaults to undefined)
31670 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31671                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31672 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31673                                    progress and wait dialogs will ignore this property and always hide the
31674                                    close button as they can only be closed programmatically.
31675 cls               String           A custom CSS class to apply to the message box element
31676 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31677                                    displayed (defaults to 75)
31678 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31679                                    function will be btn (the name of the button that was clicked, if applicable,
31680                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31681                                    Progress and wait dialogs will ignore this option since they do not respond to
31682                                    user actions and can only be closed programmatically, so any required function
31683                                    should be called by the same code after it closes the dialog.
31684 icon              String           A CSS class that provides a background image to be used as an icon for
31685                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31686 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31687 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31688 modal             Boolean          False to allow user interaction with the page while the message box is
31689                                    displayed (defaults to true)
31690 msg               String           A string that will replace the existing message box body text (defaults
31691                                    to the XHTML-compliant non-breaking space character '&#160;')
31692 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31693 progress          Boolean          True to display a progress bar (defaults to false)
31694 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31695 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31696 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31697 title             String           The title text
31698 value             String           The string value to set into the active textbox element if displayed
31699 wait              Boolean          True to display a progress bar (defaults to false)
31700 width             Number           The width of the dialog in pixels
31701 </pre>
31702          *
31703          * Example usage:
31704          * <pre><code>
31705 Roo.Msg.show({
31706    title: 'Address',
31707    msg: 'Please enter your address:',
31708    width: 300,
31709    buttons: Roo.MessageBox.OKCANCEL,
31710    multiline: true,
31711    fn: saveAddress,
31712    animEl: 'addAddressBtn'
31713 });
31714 </code></pre>
31715          * @param {Object} config Configuration options
31716          * @return {Roo.MessageBox} This message box
31717          */
31718         show : function(options)
31719         {
31720             
31721             // this causes nightmares if you show one dialog after another
31722             // especially on callbacks..
31723              
31724             if(this.isVisible()){
31725                 
31726                 this.hide();
31727                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31728                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31729                 Roo.log("New Dialog Message:" +  options.msg )
31730                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31731                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31732                 
31733             }
31734             var d = this.getDialog();
31735             opt = options;
31736             d.setTitle(opt.title || "&#160;");
31737             d.close.setDisplayed(opt.closable !== false);
31738             activeTextEl = textboxEl;
31739             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31740             if(opt.prompt){
31741                 if(opt.multiline){
31742                     textboxEl.hide();
31743                     textareaEl.show();
31744                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31745                         opt.multiline : this.defaultTextHeight);
31746                     activeTextEl = textareaEl;
31747                 }else{
31748                     textboxEl.show();
31749                     textareaEl.hide();
31750                 }
31751             }else{
31752                 textboxEl.hide();
31753                 textareaEl.hide();
31754             }
31755             progressEl.setDisplayed(opt.progress === true);
31756             this.updateProgress(0);
31757             activeTextEl.dom.value = opt.value || "";
31758             if(opt.prompt){
31759                 dlg.setDefaultButton(activeTextEl);
31760             }else{
31761                 var bs = opt.buttons;
31762                 var db = null;
31763                 if(bs && bs.ok){
31764                     db = buttons["ok"];
31765                 }else if(bs && bs.yes){
31766                     db = buttons["yes"];
31767                 }
31768                 dlg.setDefaultButton(db);
31769             }
31770             bwidth = updateButtons(opt.buttons);
31771             this.updateText(opt.msg);
31772             if(opt.cls){
31773                 d.el.addClass(opt.cls);
31774             }
31775             d.proxyDrag = opt.proxyDrag === true;
31776             d.modal = opt.modal !== false;
31777             d.mask = opt.modal !== false ? mask : false;
31778             if(!d.isVisible()){
31779                 // force it to the end of the z-index stack so it gets a cursor in FF
31780                 document.body.appendChild(dlg.el.dom);
31781                 d.animateTarget = null;
31782                 d.show(options.animEl);
31783             }
31784             return this;
31785         },
31786
31787         /**
31788          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31789          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31790          * and closing the message box when the process is complete.
31791          * @param {String} title The title bar text
31792          * @param {String} msg The message box body text
31793          * @return {Roo.MessageBox} This message box
31794          */
31795         progress : function(title, msg){
31796             this.show({
31797                 title : title,
31798                 msg : msg,
31799                 buttons: false,
31800                 progress:true,
31801                 closable:false,
31802                 minWidth: this.minProgressWidth,
31803                 modal : true
31804             });
31805             return this;
31806         },
31807
31808         /**
31809          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31810          * If a callback function is passed it will be called after the user clicks the button, and the
31811          * id of the button that was clicked will be passed as the only parameter to the callback
31812          * (could also be the top-right close button).
31813          * @param {String} title The title bar text
31814          * @param {String} msg The message box body text
31815          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31816          * @param {Object} scope (optional) The scope of the callback function
31817          * @return {Roo.MessageBox} This message box
31818          */
31819         alert : function(title, msg, fn, scope){
31820             this.show({
31821                 title : title,
31822                 msg : msg,
31823                 buttons: this.OK,
31824                 fn: fn,
31825                 scope : scope,
31826                 modal : true
31827             });
31828             return this;
31829         },
31830
31831         /**
31832          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31833          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31834          * You are responsible for closing the message box when the process is complete.
31835          * @param {String} msg The message box body text
31836          * @param {String} title (optional) The title bar text
31837          * @return {Roo.MessageBox} This message box
31838          */
31839         wait : function(msg, title){
31840             this.show({
31841                 title : title,
31842                 msg : msg,
31843                 buttons: false,
31844                 closable:false,
31845                 progress:true,
31846                 modal:true,
31847                 width:300,
31848                 wait:true
31849             });
31850             waitTimer = Roo.TaskMgr.start({
31851                 run: function(i){
31852                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31853                 },
31854                 interval: 1000
31855             });
31856             return this;
31857         },
31858
31859         /**
31860          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31861          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31862          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31863          * @param {String} title The title bar text
31864          * @param {String} msg The message box body text
31865          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31866          * @param {Object} scope (optional) The scope of the callback function
31867          * @return {Roo.MessageBox} This message box
31868          */
31869         confirm : function(title, msg, fn, scope){
31870             this.show({
31871                 title : title,
31872                 msg : msg,
31873                 buttons: this.YESNO,
31874                 fn: fn,
31875                 scope : scope,
31876                 modal : true
31877             });
31878             return this;
31879         },
31880
31881         /**
31882          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31883          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31884          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31885          * (could also be the top-right close button) and the text that was entered will be passed as the two
31886          * parameters to the callback.
31887          * @param {String} title The title bar text
31888          * @param {String} msg The message box body text
31889          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31890          * @param {Object} scope (optional) The scope of the callback function
31891          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31892          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31893          * @return {Roo.MessageBox} This message box
31894          */
31895         prompt : function(title, msg, fn, scope, multiline){
31896             this.show({
31897                 title : title,
31898                 msg : msg,
31899                 buttons: this.OKCANCEL,
31900                 fn: fn,
31901                 minWidth:250,
31902                 scope : scope,
31903                 prompt:true,
31904                 multiline: multiline,
31905                 modal : true
31906             });
31907             return this;
31908         },
31909
31910         /**
31911          * Button config that displays a single OK button
31912          * @type Object
31913          */
31914         OK : {ok:true},
31915         /**
31916          * Button config that displays Yes and No buttons
31917          * @type Object
31918          */
31919         YESNO : {yes:true, no:true},
31920         /**
31921          * Button config that displays OK and Cancel buttons
31922          * @type Object
31923          */
31924         OKCANCEL : {ok:true, cancel:true},
31925         /**
31926          * Button config that displays Yes, No and Cancel buttons
31927          * @type Object
31928          */
31929         YESNOCANCEL : {yes:true, no:true, cancel:true},
31930
31931         /**
31932          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31933          * @type Number
31934          */
31935         defaultTextHeight : 75,
31936         /**
31937          * The maximum width in pixels of the message box (defaults to 600)
31938          * @type Number
31939          */
31940         maxWidth : 600,
31941         /**
31942          * The minimum width in pixels of the message box (defaults to 100)
31943          * @type Number
31944          */
31945         minWidth : 100,
31946         /**
31947          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31948          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31949          * @type Number
31950          */
31951         minProgressWidth : 250,
31952         /**
31953          * An object containing the default button text strings that can be overriden for localized language support.
31954          * Supported properties are: ok, cancel, yes and no.
31955          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31956          * @type Object
31957          */
31958         buttonText : {
31959             ok : "OK",
31960             cancel : "Cancel",
31961             yes : "Yes",
31962             no : "No"
31963         }
31964     };
31965 }();
31966
31967 /**
31968  * Shorthand for {@link Roo.MessageBox}
31969  */
31970 Roo.Msg = Roo.MessageBox;/*
31971  * Based on:
31972  * Ext JS Library 1.1.1
31973  * Copyright(c) 2006-2007, Ext JS, LLC.
31974  *
31975  * Originally Released Under LGPL - original licence link has changed is not relivant.
31976  *
31977  * Fork - LGPL
31978  * <script type="text/javascript">
31979  */
31980 /**
31981  * @class Roo.QuickTips
31982  * Provides attractive and customizable tooltips for any element.
31983  * @singleton
31984  */
31985 Roo.QuickTips = function(){
31986     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31987     var ce, bd, xy, dd;
31988     var visible = false, disabled = true, inited = false;
31989     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31990     
31991     var onOver = function(e){
31992         if(disabled){
31993             return;
31994         }
31995         var t = e.getTarget();
31996         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31997             return;
31998         }
31999         if(ce && t == ce.el){
32000             clearTimeout(hideProc);
32001             return;
32002         }
32003         if(t && tagEls[t.id]){
32004             tagEls[t.id].el = t;
32005             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32006             return;
32007         }
32008         var ttp, et = Roo.fly(t);
32009         var ns = cfg.namespace;
32010         if(tm.interceptTitles && t.title){
32011             ttp = t.title;
32012             t.qtip = ttp;
32013             t.removeAttribute("title");
32014             e.preventDefault();
32015         }else{
32016             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32017         }
32018         if(ttp){
32019             showProc = show.defer(tm.showDelay, tm, [{
32020                 el: t, 
32021                 text: ttp, 
32022                 width: et.getAttributeNS(ns, cfg.width),
32023                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32024                 title: et.getAttributeNS(ns, cfg.title),
32025                     cls: et.getAttributeNS(ns, cfg.cls)
32026             }]);
32027         }
32028     };
32029     
32030     var onOut = function(e){
32031         clearTimeout(showProc);
32032         var t = e.getTarget();
32033         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32034             hideProc = setTimeout(hide, tm.hideDelay);
32035         }
32036     };
32037     
32038     var onMove = function(e){
32039         if(disabled){
32040             return;
32041         }
32042         xy = e.getXY();
32043         xy[1] += 18;
32044         if(tm.trackMouse && ce){
32045             el.setXY(xy);
32046         }
32047     };
32048     
32049     var onDown = function(e){
32050         clearTimeout(showProc);
32051         clearTimeout(hideProc);
32052         if(!e.within(el)){
32053             if(tm.hideOnClick){
32054                 hide();
32055                 tm.disable();
32056                 tm.enable.defer(100, tm);
32057             }
32058         }
32059     };
32060     
32061     var getPad = function(){
32062         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32063     };
32064
32065     var show = function(o){
32066         if(disabled){
32067             return;
32068         }
32069         clearTimeout(dismissProc);
32070         ce = o;
32071         if(removeCls){ // in case manually hidden
32072             el.removeClass(removeCls);
32073             removeCls = null;
32074         }
32075         if(ce.cls){
32076             el.addClass(ce.cls);
32077             removeCls = ce.cls;
32078         }
32079         if(ce.title){
32080             tipTitle.update(ce.title);
32081             tipTitle.show();
32082         }else{
32083             tipTitle.update('');
32084             tipTitle.hide();
32085         }
32086         el.dom.style.width  = tm.maxWidth+'px';
32087         //tipBody.dom.style.width = '';
32088         tipBodyText.update(o.text);
32089         var p = getPad(), w = ce.width;
32090         if(!w){
32091             var td = tipBodyText.dom;
32092             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32093             if(aw > tm.maxWidth){
32094                 w = tm.maxWidth;
32095             }else if(aw < tm.minWidth){
32096                 w = tm.minWidth;
32097             }else{
32098                 w = aw;
32099             }
32100         }
32101         //tipBody.setWidth(w);
32102         el.setWidth(parseInt(w, 10) + p);
32103         if(ce.autoHide === false){
32104             close.setDisplayed(true);
32105             if(dd){
32106                 dd.unlock();
32107             }
32108         }else{
32109             close.setDisplayed(false);
32110             if(dd){
32111                 dd.lock();
32112             }
32113         }
32114         if(xy){
32115             el.avoidY = xy[1]-18;
32116             el.setXY(xy);
32117         }
32118         if(tm.animate){
32119             el.setOpacity(.1);
32120             el.setStyle("visibility", "visible");
32121             el.fadeIn({callback: afterShow});
32122         }else{
32123             afterShow();
32124         }
32125     };
32126     
32127     var afterShow = function(){
32128         if(ce){
32129             el.show();
32130             esc.enable();
32131             if(tm.autoDismiss && ce.autoHide !== false){
32132                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32133             }
32134         }
32135     };
32136     
32137     var hide = function(noanim){
32138         clearTimeout(dismissProc);
32139         clearTimeout(hideProc);
32140         ce = null;
32141         if(el.isVisible()){
32142             esc.disable();
32143             if(noanim !== true && tm.animate){
32144                 el.fadeOut({callback: afterHide});
32145             }else{
32146                 afterHide();
32147             } 
32148         }
32149     };
32150     
32151     var afterHide = function(){
32152         el.hide();
32153         if(removeCls){
32154             el.removeClass(removeCls);
32155             removeCls = null;
32156         }
32157     };
32158     
32159     return {
32160         /**
32161         * @cfg {Number} minWidth
32162         * The minimum width of the quick tip (defaults to 40)
32163         */
32164        minWidth : 40,
32165         /**
32166         * @cfg {Number} maxWidth
32167         * The maximum width of the quick tip (defaults to 300)
32168         */
32169        maxWidth : 300,
32170         /**
32171         * @cfg {Boolean} interceptTitles
32172         * True to automatically use the element's DOM title value if available (defaults to false)
32173         */
32174        interceptTitles : false,
32175         /**
32176         * @cfg {Boolean} trackMouse
32177         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32178         */
32179        trackMouse : false,
32180         /**
32181         * @cfg {Boolean} hideOnClick
32182         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32183         */
32184        hideOnClick : true,
32185         /**
32186         * @cfg {Number} showDelay
32187         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32188         */
32189        showDelay : 500,
32190         /**
32191         * @cfg {Number} hideDelay
32192         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32193         */
32194        hideDelay : 200,
32195         /**
32196         * @cfg {Boolean} autoHide
32197         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32198         * Used in conjunction with hideDelay.
32199         */
32200        autoHide : true,
32201         /**
32202         * @cfg {Boolean}
32203         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32204         * (defaults to true).  Used in conjunction with autoDismissDelay.
32205         */
32206        autoDismiss : true,
32207         /**
32208         * @cfg {Number}
32209         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32210         */
32211        autoDismissDelay : 5000,
32212        /**
32213         * @cfg {Boolean} animate
32214         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32215         */
32216        animate : false,
32217
32218        /**
32219         * @cfg {String} title
32220         * Title text to display (defaults to '').  This can be any valid HTML markup.
32221         */
32222         title: '',
32223        /**
32224         * @cfg {String} text
32225         * Body text to display (defaults to '').  This can be any valid HTML markup.
32226         */
32227         text : '',
32228        /**
32229         * @cfg {String} cls
32230         * A CSS class to apply to the base quick tip element (defaults to '').
32231         */
32232         cls : '',
32233        /**
32234         * @cfg {Number} width
32235         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32236         * minWidth or maxWidth.
32237         */
32238         width : null,
32239
32240     /**
32241      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32242      * or display QuickTips in a page.
32243      */
32244        init : function(){
32245           tm = Roo.QuickTips;
32246           cfg = tm.tagConfig;
32247           if(!inited){
32248               if(!Roo.isReady){ // allow calling of init() before onReady
32249                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32250                   return;
32251               }
32252               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32253               el.fxDefaults = {stopFx: true};
32254               // maximum custom styling
32255               //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>');
32256               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>');              
32257               tipTitle = el.child('h3');
32258               tipTitle.enableDisplayMode("block");
32259               tipBody = el.child('div.x-tip-bd');
32260               tipBodyText = el.child('div.x-tip-bd-inner');
32261               //bdLeft = el.child('div.x-tip-bd-left');
32262               //bdRight = el.child('div.x-tip-bd-right');
32263               close = el.child('div.x-tip-close');
32264               close.enableDisplayMode("block");
32265               close.on("click", hide);
32266               var d = Roo.get(document);
32267               d.on("mousedown", onDown);
32268               d.on("mouseover", onOver);
32269               d.on("mouseout", onOut);
32270               d.on("mousemove", onMove);
32271               esc = d.addKeyListener(27, hide);
32272               esc.disable();
32273               if(Roo.dd.DD){
32274                   dd = el.initDD("default", null, {
32275                       onDrag : function(){
32276                           el.sync();  
32277                       }
32278                   });
32279                   dd.setHandleElId(tipTitle.id);
32280                   dd.lock();
32281               }
32282               inited = true;
32283           }
32284           this.enable(); 
32285        },
32286
32287     /**
32288      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32289      * are supported:
32290      * <pre>
32291 Property    Type                   Description
32292 ----------  ---------------------  ------------------------------------------------------------------------
32293 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32294      * </ul>
32295      * @param {Object} config The config object
32296      */
32297        register : function(config){
32298            var cs = config instanceof Array ? config : arguments;
32299            for(var i = 0, len = cs.length; i < len; i++) {
32300                var c = cs[i];
32301                var target = c.target;
32302                if(target){
32303                    if(target instanceof Array){
32304                        for(var j = 0, jlen = target.length; j < jlen; j++){
32305                            tagEls[target[j]] = c;
32306                        }
32307                    }else{
32308                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32309                    }
32310                }
32311            }
32312        },
32313
32314     /**
32315      * Removes this quick tip from its element and destroys it.
32316      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32317      */
32318        unregister : function(el){
32319            delete tagEls[Roo.id(el)];
32320        },
32321
32322     /**
32323      * Enable this quick tip.
32324      */
32325        enable : function(){
32326            if(inited && disabled){
32327                locks.pop();
32328                if(locks.length < 1){
32329                    disabled = false;
32330                }
32331            }
32332        },
32333
32334     /**
32335      * Disable this quick tip.
32336      */
32337        disable : function(){
32338           disabled = true;
32339           clearTimeout(showProc);
32340           clearTimeout(hideProc);
32341           clearTimeout(dismissProc);
32342           if(ce){
32343               hide(true);
32344           }
32345           locks.push(1);
32346        },
32347
32348     /**
32349      * Returns true if the quick tip is enabled, else false.
32350      */
32351        isEnabled : function(){
32352             return !disabled;
32353        },
32354
32355         // private
32356        tagConfig : {
32357            namespace : "ext",
32358            attribute : "qtip",
32359            width : "width",
32360            target : "target",
32361            title : "qtitle",
32362            hide : "hide",
32363            cls : "qclass"
32364        }
32365    };
32366 }();
32367
32368 // backwards compat
32369 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32370  * Based on:
32371  * Ext JS Library 1.1.1
32372  * Copyright(c) 2006-2007, Ext JS, LLC.
32373  *
32374  * Originally Released Under LGPL - original licence link has changed is not relivant.
32375  *
32376  * Fork - LGPL
32377  * <script type="text/javascript">
32378  */
32379  
32380
32381 /**
32382  * @class Roo.tree.TreePanel
32383  * @extends Roo.data.Tree
32384
32385  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32386  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32387  * @cfg {Boolean} enableDD true to enable drag and drop
32388  * @cfg {Boolean} enableDrag true to enable just drag
32389  * @cfg {Boolean} enableDrop true to enable just drop
32390  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32391  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32392  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32393  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32394  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32395  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32396  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32397  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32398  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32399  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32400  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32401  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32402  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32403  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32404  * @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>
32405  * @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>
32406  * 
32407  * @constructor
32408  * @param {String/HTMLElement/Element} el The container element
32409  * @param {Object} config
32410  */
32411 Roo.tree.TreePanel = function(el, config){
32412     var root = false;
32413     var loader = false;
32414     if (config.root) {
32415         root = config.root;
32416         delete config.root;
32417     }
32418     if (config.loader) {
32419         loader = config.loader;
32420         delete config.loader;
32421     }
32422     
32423     Roo.apply(this, config);
32424     Roo.tree.TreePanel.superclass.constructor.call(this);
32425     this.el = Roo.get(el);
32426     this.el.addClass('x-tree');
32427     //console.log(root);
32428     if (root) {
32429         this.setRootNode( Roo.factory(root, Roo.tree));
32430     }
32431     if (loader) {
32432         this.loader = Roo.factory(loader, Roo.tree);
32433     }
32434    /**
32435     * Read-only. The id of the container element becomes this TreePanel's id.
32436     */
32437     this.id = this.el.id;
32438     this.addEvents({
32439         /**
32440         * @event beforeload
32441         * Fires before a node is loaded, return false to cancel
32442         * @param {Node} node The node being loaded
32443         */
32444         "beforeload" : true,
32445         /**
32446         * @event load
32447         * Fires when a node is loaded
32448         * @param {Node} node The node that was loaded
32449         */
32450         "load" : true,
32451         /**
32452         * @event textchange
32453         * Fires when the text for a node is changed
32454         * @param {Node} node The node
32455         * @param {String} text The new text
32456         * @param {String} oldText The old text
32457         */
32458         "textchange" : true,
32459         /**
32460         * @event beforeexpand
32461         * Fires before a node is expanded, return false to cancel.
32462         * @param {Node} node The node
32463         * @param {Boolean} deep
32464         * @param {Boolean} anim
32465         */
32466         "beforeexpand" : true,
32467         /**
32468         * @event beforecollapse
32469         * Fires before a node is collapsed, return false to cancel.
32470         * @param {Node} node The node
32471         * @param {Boolean} deep
32472         * @param {Boolean} anim
32473         */
32474         "beforecollapse" : true,
32475         /**
32476         * @event expand
32477         * Fires when a node is expanded
32478         * @param {Node} node The node
32479         */
32480         "expand" : true,
32481         /**
32482         * @event disabledchange
32483         * Fires when the disabled status of a node changes
32484         * @param {Node} node The node
32485         * @param {Boolean} disabled
32486         */
32487         "disabledchange" : true,
32488         /**
32489         * @event collapse
32490         * Fires when a node is collapsed
32491         * @param {Node} node The node
32492         */
32493         "collapse" : true,
32494         /**
32495         * @event beforeclick
32496         * Fires before click processing on a node. Return false to cancel the default action.
32497         * @param {Node} node The node
32498         * @param {Roo.EventObject} e The event object
32499         */
32500         "beforeclick":true,
32501         /**
32502         * @event checkchange
32503         * Fires when a node with a checkbox's checked property changes
32504         * @param {Node} this This node
32505         * @param {Boolean} checked
32506         */
32507         "checkchange":true,
32508         /**
32509         * @event click
32510         * Fires when a node is clicked
32511         * @param {Node} node The node
32512         * @param {Roo.EventObject} e The event object
32513         */
32514         "click":true,
32515         /**
32516         * @event dblclick
32517         * Fires when a node is double clicked
32518         * @param {Node} node The node
32519         * @param {Roo.EventObject} e The event object
32520         */
32521         "dblclick":true,
32522         /**
32523         * @event contextmenu
32524         * Fires when a node is right clicked
32525         * @param {Node} node The node
32526         * @param {Roo.EventObject} e The event object
32527         */
32528         "contextmenu":true,
32529         /**
32530         * @event beforechildrenrendered
32531         * Fires right before the child nodes for a node are rendered
32532         * @param {Node} node The node
32533         */
32534         "beforechildrenrendered":true,
32535         /**
32536         * @event startdrag
32537         * Fires when a node starts being dragged
32538         * @param {Roo.tree.TreePanel} this
32539         * @param {Roo.tree.TreeNode} node
32540         * @param {event} e The raw browser event
32541         */ 
32542        "startdrag" : true,
32543        /**
32544         * @event enddrag
32545         * Fires when a drag operation is complete
32546         * @param {Roo.tree.TreePanel} this
32547         * @param {Roo.tree.TreeNode} node
32548         * @param {event} e The raw browser event
32549         */
32550        "enddrag" : true,
32551        /**
32552         * @event dragdrop
32553         * Fires when a dragged node is dropped on a valid DD target
32554         * @param {Roo.tree.TreePanel} this
32555         * @param {Roo.tree.TreeNode} node
32556         * @param {DD} dd The dd it was dropped on
32557         * @param {event} e The raw browser event
32558         */
32559        "dragdrop" : true,
32560        /**
32561         * @event beforenodedrop
32562         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32563         * passed to handlers has the following properties:<br />
32564         * <ul style="padding:5px;padding-left:16px;">
32565         * <li>tree - The TreePanel</li>
32566         * <li>target - The node being targeted for the drop</li>
32567         * <li>data - The drag data from the drag source</li>
32568         * <li>point - The point of the drop - append, above or below</li>
32569         * <li>source - The drag source</li>
32570         * <li>rawEvent - Raw mouse event</li>
32571         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32572         * to be inserted by setting them on this object.</li>
32573         * <li>cancel - Set this to true to cancel the drop.</li>
32574         * </ul>
32575         * @param {Object} dropEvent
32576         */
32577        "beforenodedrop" : true,
32578        /**
32579         * @event nodedrop
32580         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32581         * passed to handlers has the following properties:<br />
32582         * <ul style="padding:5px;padding-left:16px;">
32583         * <li>tree - The TreePanel</li>
32584         * <li>target - The node being targeted for the drop</li>
32585         * <li>data - The drag data from the drag source</li>
32586         * <li>point - The point of the drop - append, above or below</li>
32587         * <li>source - The drag source</li>
32588         * <li>rawEvent - Raw mouse event</li>
32589         * <li>dropNode - Dropped node(s).</li>
32590         * </ul>
32591         * @param {Object} dropEvent
32592         */
32593        "nodedrop" : true,
32594         /**
32595         * @event nodedragover
32596         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32597         * passed to handlers has the following properties:<br />
32598         * <ul style="padding:5px;padding-left:16px;">
32599         * <li>tree - The TreePanel</li>
32600         * <li>target - The node being targeted for the drop</li>
32601         * <li>data - The drag data from the drag source</li>
32602         * <li>point - The point of the drop - append, above or below</li>
32603         * <li>source - The drag source</li>
32604         * <li>rawEvent - Raw mouse event</li>
32605         * <li>dropNode - Drop node(s) provided by the source.</li>
32606         * <li>cancel - Set this to true to signal drop not allowed.</li>
32607         * </ul>
32608         * @param {Object} dragOverEvent
32609         */
32610        "nodedragover" : true
32611         
32612     });
32613     if(this.singleExpand){
32614        this.on("beforeexpand", this.restrictExpand, this);
32615     }
32616     if (this.editor) {
32617         this.editor.tree = this;
32618         this.editor = Roo.factory(this.editor, Roo.tree);
32619     }
32620     
32621     if (this.selModel) {
32622         this.selModel = Roo.factory(this.selModel, Roo.tree);
32623     }
32624    
32625 };
32626 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32627     rootVisible : true,
32628     animate: Roo.enableFx,
32629     lines : true,
32630     enableDD : false,
32631     hlDrop : Roo.enableFx,
32632   
32633     renderer: false,
32634     
32635     rendererTip: false,
32636     // private
32637     restrictExpand : function(node){
32638         var p = node.parentNode;
32639         if(p){
32640             if(p.expandedChild && p.expandedChild.parentNode == p){
32641                 p.expandedChild.collapse();
32642             }
32643             p.expandedChild = node;
32644         }
32645     },
32646
32647     // private override
32648     setRootNode : function(node){
32649         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32650         if(!this.rootVisible){
32651             node.ui = new Roo.tree.RootTreeNodeUI(node);
32652         }
32653         return node;
32654     },
32655
32656     /**
32657      * Returns the container element for this TreePanel
32658      */
32659     getEl : function(){
32660         return this.el;
32661     },
32662
32663     /**
32664      * Returns the default TreeLoader for this TreePanel
32665      */
32666     getLoader : function(){
32667         return this.loader;
32668     },
32669
32670     /**
32671      * Expand all nodes
32672      */
32673     expandAll : function(){
32674         this.root.expand(true);
32675     },
32676
32677     /**
32678      * Collapse all nodes
32679      */
32680     collapseAll : function(){
32681         this.root.collapse(true);
32682     },
32683
32684     /**
32685      * Returns the selection model used by this TreePanel
32686      */
32687     getSelectionModel : function(){
32688         if(!this.selModel){
32689             this.selModel = new Roo.tree.DefaultSelectionModel();
32690         }
32691         return this.selModel;
32692     },
32693
32694     /**
32695      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32696      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32697      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32698      * @return {Array}
32699      */
32700     getChecked : function(a, startNode){
32701         startNode = startNode || this.root;
32702         var r = [];
32703         var f = function(){
32704             if(this.attributes.checked){
32705                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32706             }
32707         }
32708         startNode.cascade(f);
32709         return r;
32710     },
32711
32712     /**
32713      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32714      * @param {String} path
32715      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32716      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32717      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32718      */
32719     expandPath : function(path, attr, callback){
32720         attr = attr || "id";
32721         var keys = path.split(this.pathSeparator);
32722         var curNode = this.root;
32723         if(curNode.attributes[attr] != keys[1]){ // invalid root
32724             if(callback){
32725                 callback(false, null);
32726             }
32727             return;
32728         }
32729         var index = 1;
32730         var f = function(){
32731             if(++index == keys.length){
32732                 if(callback){
32733                     callback(true, curNode);
32734                 }
32735                 return;
32736             }
32737             var c = curNode.findChild(attr, keys[index]);
32738             if(!c){
32739                 if(callback){
32740                     callback(false, curNode);
32741                 }
32742                 return;
32743             }
32744             curNode = c;
32745             c.expand(false, false, f);
32746         };
32747         curNode.expand(false, false, f);
32748     },
32749
32750     /**
32751      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32752      * @param {String} path
32753      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32754      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32755      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32756      */
32757     selectPath : function(path, attr, callback){
32758         attr = attr || "id";
32759         var keys = path.split(this.pathSeparator);
32760         var v = keys.pop();
32761         if(keys.length > 0){
32762             var f = function(success, node){
32763                 if(success && node){
32764                     var n = node.findChild(attr, v);
32765                     if(n){
32766                         n.select();
32767                         if(callback){
32768                             callback(true, n);
32769                         }
32770                     }else if(callback){
32771                         callback(false, n);
32772                     }
32773                 }else{
32774                     if(callback){
32775                         callback(false, n);
32776                     }
32777                 }
32778             };
32779             this.expandPath(keys.join(this.pathSeparator), attr, f);
32780         }else{
32781             this.root.select();
32782             if(callback){
32783                 callback(true, this.root);
32784             }
32785         }
32786     },
32787
32788     getTreeEl : function(){
32789         return this.el;
32790     },
32791
32792     /**
32793      * Trigger rendering of this TreePanel
32794      */
32795     render : function(){
32796         if (this.innerCt) {
32797             return this; // stop it rendering more than once!!
32798         }
32799         
32800         this.innerCt = this.el.createChild({tag:"ul",
32801                cls:"x-tree-root-ct " +
32802                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32803
32804         if(this.containerScroll){
32805             Roo.dd.ScrollManager.register(this.el);
32806         }
32807         if((this.enableDD || this.enableDrop) && !this.dropZone){
32808            /**
32809             * The dropZone used by this tree if drop is enabled
32810             * @type Roo.tree.TreeDropZone
32811             */
32812              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32813                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32814            });
32815         }
32816         if((this.enableDD || this.enableDrag) && !this.dragZone){
32817            /**
32818             * The dragZone used by this tree if drag is enabled
32819             * @type Roo.tree.TreeDragZone
32820             */
32821             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32822                ddGroup: this.ddGroup || "TreeDD",
32823                scroll: this.ddScroll
32824            });
32825         }
32826         this.getSelectionModel().init(this);
32827         if (!this.root) {
32828             Roo.log("ROOT not set in tree");
32829             return this;
32830         }
32831         this.root.render();
32832         if(!this.rootVisible){
32833             this.root.renderChildren();
32834         }
32835         return this;
32836     }
32837 });/*
32838  * Based on:
32839  * Ext JS Library 1.1.1
32840  * Copyright(c) 2006-2007, Ext JS, LLC.
32841  *
32842  * Originally Released Under LGPL - original licence link has changed is not relivant.
32843  *
32844  * Fork - LGPL
32845  * <script type="text/javascript">
32846  */
32847  
32848
32849 /**
32850  * @class Roo.tree.DefaultSelectionModel
32851  * @extends Roo.util.Observable
32852  * The default single selection for a TreePanel.
32853  * @param {Object} cfg Configuration
32854  */
32855 Roo.tree.DefaultSelectionModel = function(cfg){
32856    this.selNode = null;
32857    
32858    
32859    
32860    this.addEvents({
32861        /**
32862         * @event selectionchange
32863         * Fires when the selected node changes
32864         * @param {DefaultSelectionModel} this
32865         * @param {TreeNode} node the new selection
32866         */
32867        "selectionchange" : true,
32868
32869        /**
32870         * @event beforeselect
32871         * Fires before the selected node changes, return false to cancel the change
32872         * @param {DefaultSelectionModel} this
32873         * @param {TreeNode} node the new selection
32874         * @param {TreeNode} node the old selection
32875         */
32876        "beforeselect" : true
32877    });
32878    
32879     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32880 };
32881
32882 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32883     init : function(tree){
32884         this.tree = tree;
32885         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32886         tree.on("click", this.onNodeClick, this);
32887     },
32888     
32889     onNodeClick : function(node, e){
32890         if (e.ctrlKey && this.selNode == node)  {
32891             this.unselect(node);
32892             return;
32893         }
32894         this.select(node);
32895     },
32896     
32897     /**
32898      * Select a node.
32899      * @param {TreeNode} node The node to select
32900      * @return {TreeNode} The selected node
32901      */
32902     select : function(node){
32903         var last = this.selNode;
32904         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32905             if(last){
32906                 last.ui.onSelectedChange(false);
32907             }
32908             this.selNode = node;
32909             node.ui.onSelectedChange(true);
32910             this.fireEvent("selectionchange", this, node, last);
32911         }
32912         return node;
32913     },
32914     
32915     /**
32916      * Deselect a node.
32917      * @param {TreeNode} node The node to unselect
32918      */
32919     unselect : function(node){
32920         if(this.selNode == node){
32921             this.clearSelections();
32922         }    
32923     },
32924     
32925     /**
32926      * Clear all selections
32927      */
32928     clearSelections : function(){
32929         var n = this.selNode;
32930         if(n){
32931             n.ui.onSelectedChange(false);
32932             this.selNode = null;
32933             this.fireEvent("selectionchange", this, null);
32934         }
32935         return n;
32936     },
32937     
32938     /**
32939      * Get the selected node
32940      * @return {TreeNode} The selected node
32941      */
32942     getSelectedNode : function(){
32943         return this.selNode;    
32944     },
32945     
32946     /**
32947      * Returns true if the node is selected
32948      * @param {TreeNode} node The node to check
32949      * @return {Boolean}
32950      */
32951     isSelected : function(node){
32952         return this.selNode == node;  
32953     },
32954
32955     /**
32956      * Selects the node above the selected node in the tree, intelligently walking the nodes
32957      * @return TreeNode The new selection
32958      */
32959     selectPrevious : function(){
32960         var s = this.selNode || this.lastSelNode;
32961         if(!s){
32962             return null;
32963         }
32964         var ps = s.previousSibling;
32965         if(ps){
32966             if(!ps.isExpanded() || ps.childNodes.length < 1){
32967                 return this.select(ps);
32968             } else{
32969                 var lc = ps.lastChild;
32970                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32971                     lc = lc.lastChild;
32972                 }
32973                 return this.select(lc);
32974             }
32975         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32976             return this.select(s.parentNode);
32977         }
32978         return null;
32979     },
32980
32981     /**
32982      * Selects the node above the selected node in the tree, intelligently walking the nodes
32983      * @return TreeNode The new selection
32984      */
32985     selectNext : function(){
32986         var s = this.selNode || this.lastSelNode;
32987         if(!s){
32988             return null;
32989         }
32990         if(s.firstChild && s.isExpanded()){
32991              return this.select(s.firstChild);
32992          }else if(s.nextSibling){
32993              return this.select(s.nextSibling);
32994          }else if(s.parentNode){
32995             var newS = null;
32996             s.parentNode.bubble(function(){
32997                 if(this.nextSibling){
32998                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32999                     return false;
33000                 }
33001             });
33002             return newS;
33003          }
33004         return null;
33005     },
33006
33007     onKeyDown : function(e){
33008         var s = this.selNode || this.lastSelNode;
33009         // undesirable, but required
33010         var sm = this;
33011         if(!s){
33012             return;
33013         }
33014         var k = e.getKey();
33015         switch(k){
33016              case e.DOWN:
33017                  e.stopEvent();
33018                  this.selectNext();
33019              break;
33020              case e.UP:
33021                  e.stopEvent();
33022                  this.selectPrevious();
33023              break;
33024              case e.RIGHT:
33025                  e.preventDefault();
33026                  if(s.hasChildNodes()){
33027                      if(!s.isExpanded()){
33028                          s.expand();
33029                      }else if(s.firstChild){
33030                          this.select(s.firstChild, e);
33031                      }
33032                  }
33033              break;
33034              case e.LEFT:
33035                  e.preventDefault();
33036                  if(s.hasChildNodes() && s.isExpanded()){
33037                      s.collapse();
33038                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33039                      this.select(s.parentNode, e);
33040                  }
33041              break;
33042         };
33043     }
33044 });
33045
33046 /**
33047  * @class Roo.tree.MultiSelectionModel
33048  * @extends Roo.util.Observable
33049  * Multi selection for a TreePanel.
33050  * @param {Object} cfg Configuration
33051  */
33052 Roo.tree.MultiSelectionModel = function(){
33053    this.selNodes = [];
33054    this.selMap = {};
33055    this.addEvents({
33056        /**
33057         * @event selectionchange
33058         * Fires when the selected nodes change
33059         * @param {MultiSelectionModel} this
33060         * @param {Array} nodes Array of the selected nodes
33061         */
33062        "selectionchange" : true
33063    });
33064    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33065    
33066 };
33067
33068 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33069     init : function(tree){
33070         this.tree = tree;
33071         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33072         tree.on("click", this.onNodeClick, this);
33073     },
33074     
33075     onNodeClick : function(node, e){
33076         this.select(node, e, e.ctrlKey);
33077     },
33078     
33079     /**
33080      * Select a node.
33081      * @param {TreeNode} node The node to select
33082      * @param {EventObject} e (optional) An event associated with the selection
33083      * @param {Boolean} keepExisting True to retain existing selections
33084      * @return {TreeNode} The selected node
33085      */
33086     select : function(node, e, keepExisting){
33087         if(keepExisting !== true){
33088             this.clearSelections(true);
33089         }
33090         if(this.isSelected(node)){
33091             this.lastSelNode = node;
33092             return node;
33093         }
33094         this.selNodes.push(node);
33095         this.selMap[node.id] = node;
33096         this.lastSelNode = node;
33097         node.ui.onSelectedChange(true);
33098         this.fireEvent("selectionchange", this, this.selNodes);
33099         return node;
33100     },
33101     
33102     /**
33103      * Deselect a node.
33104      * @param {TreeNode} node The node to unselect
33105      */
33106     unselect : function(node){
33107         if(this.selMap[node.id]){
33108             node.ui.onSelectedChange(false);
33109             var sn = this.selNodes;
33110             var index = -1;
33111             if(sn.indexOf){
33112                 index = sn.indexOf(node);
33113             }else{
33114                 for(var i = 0, len = sn.length; i < len; i++){
33115                     if(sn[i] == node){
33116                         index = i;
33117                         break;
33118                     }
33119                 }
33120             }
33121             if(index != -1){
33122                 this.selNodes.splice(index, 1);
33123             }
33124             delete this.selMap[node.id];
33125             this.fireEvent("selectionchange", this, this.selNodes);
33126         }
33127     },
33128     
33129     /**
33130      * Clear all selections
33131      */
33132     clearSelections : function(suppressEvent){
33133         var sn = this.selNodes;
33134         if(sn.length > 0){
33135             for(var i = 0, len = sn.length; i < len; i++){
33136                 sn[i].ui.onSelectedChange(false);
33137             }
33138             this.selNodes = [];
33139             this.selMap = {};
33140             if(suppressEvent !== true){
33141                 this.fireEvent("selectionchange", this, this.selNodes);
33142             }
33143         }
33144     },
33145     
33146     /**
33147      * Returns true if the node is selected
33148      * @param {TreeNode} node The node to check
33149      * @return {Boolean}
33150      */
33151     isSelected : function(node){
33152         return this.selMap[node.id] ? true : false;  
33153     },
33154     
33155     /**
33156      * Returns an array of the selected nodes
33157      * @return {Array}
33158      */
33159     getSelectedNodes : function(){
33160         return this.selNodes;    
33161     },
33162
33163     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33164
33165     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33166
33167     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33168 });/*
33169  * Based on:
33170  * Ext JS Library 1.1.1
33171  * Copyright(c) 2006-2007, Ext JS, LLC.
33172  *
33173  * Originally Released Under LGPL - original licence link has changed is not relivant.
33174  *
33175  * Fork - LGPL
33176  * <script type="text/javascript">
33177  */
33178  
33179 /**
33180  * @class Roo.tree.TreeNode
33181  * @extends Roo.data.Node
33182  * @cfg {String} text The text for this node
33183  * @cfg {Boolean} expanded true to start the node expanded
33184  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33185  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33186  * @cfg {Boolean} disabled true to start the node disabled
33187  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33188  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33189  * @cfg {String} cls A css class to be added to the node
33190  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33191  * @cfg {String} href URL of the link used for the node (defaults to #)
33192  * @cfg {String} hrefTarget target frame for the link
33193  * @cfg {String} qtip An Ext QuickTip for the node
33194  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33195  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33196  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33197  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33198  * (defaults to undefined with no checkbox rendered)
33199  * @constructor
33200  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33201  */
33202 Roo.tree.TreeNode = function(attributes){
33203     attributes = attributes || {};
33204     if(typeof attributes == "string"){
33205         attributes = {text: attributes};
33206     }
33207     this.childrenRendered = false;
33208     this.rendered = false;
33209     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33210     this.expanded = attributes.expanded === true;
33211     this.isTarget = attributes.isTarget !== false;
33212     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33213     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33214
33215     /**
33216      * Read-only. The text for this node. To change it use setText().
33217      * @type String
33218      */
33219     this.text = attributes.text;
33220     /**
33221      * True if this node is disabled.
33222      * @type Boolean
33223      */
33224     this.disabled = attributes.disabled === true;
33225
33226     this.addEvents({
33227         /**
33228         * @event textchange
33229         * Fires when the text for this node is changed
33230         * @param {Node} this This node
33231         * @param {String} text The new text
33232         * @param {String} oldText The old text
33233         */
33234         "textchange" : true,
33235         /**
33236         * @event beforeexpand
33237         * Fires before this node is expanded, return false to cancel.
33238         * @param {Node} this This node
33239         * @param {Boolean} deep
33240         * @param {Boolean} anim
33241         */
33242         "beforeexpand" : true,
33243         /**
33244         * @event beforecollapse
33245         * Fires before this node is collapsed, return false to cancel.
33246         * @param {Node} this This node
33247         * @param {Boolean} deep
33248         * @param {Boolean} anim
33249         */
33250         "beforecollapse" : true,
33251         /**
33252         * @event expand
33253         * Fires when this node is expanded
33254         * @param {Node} this This node
33255         */
33256         "expand" : true,
33257         /**
33258         * @event disabledchange
33259         * Fires when the disabled status of this node changes
33260         * @param {Node} this This node
33261         * @param {Boolean} disabled
33262         */
33263         "disabledchange" : true,
33264         /**
33265         * @event collapse
33266         * Fires when this node is collapsed
33267         * @param {Node} this This node
33268         */
33269         "collapse" : true,
33270         /**
33271         * @event beforeclick
33272         * Fires before click processing. Return false to cancel the default action.
33273         * @param {Node} this This node
33274         * @param {Roo.EventObject} e The event object
33275         */
33276         "beforeclick":true,
33277         /**
33278         * @event checkchange
33279         * Fires when a node with a checkbox's checked property changes
33280         * @param {Node} this This node
33281         * @param {Boolean} checked
33282         */
33283         "checkchange":true,
33284         /**
33285         * @event click
33286         * Fires when this node is clicked
33287         * @param {Node} this This node
33288         * @param {Roo.EventObject} e The event object
33289         */
33290         "click":true,
33291         /**
33292         * @event dblclick
33293         * Fires when this node is double clicked
33294         * @param {Node} this This node
33295         * @param {Roo.EventObject} e The event object
33296         */
33297         "dblclick":true,
33298         /**
33299         * @event contextmenu
33300         * Fires when this node is right clicked
33301         * @param {Node} this This node
33302         * @param {Roo.EventObject} e The event object
33303         */
33304         "contextmenu":true,
33305         /**
33306         * @event beforechildrenrendered
33307         * Fires right before the child nodes for this node are rendered
33308         * @param {Node} this This node
33309         */
33310         "beforechildrenrendered":true
33311     });
33312
33313     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33314
33315     /**
33316      * Read-only. The UI for this node
33317      * @type TreeNodeUI
33318      */
33319     this.ui = new uiClass(this);
33320     
33321     // finally support items[]
33322     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33323         return;
33324     }
33325     
33326     
33327     Roo.each(this.attributes.items, function(c) {
33328         this.appendChild(Roo.factory(c,Roo.Tree));
33329     }, this);
33330     delete this.attributes.items;
33331     
33332     
33333     
33334 };
33335 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33336     preventHScroll: true,
33337     /**
33338      * Returns true if this node is expanded
33339      * @return {Boolean}
33340      */
33341     isExpanded : function(){
33342         return this.expanded;
33343     },
33344
33345     /**
33346      * Returns the UI object for this node
33347      * @return {TreeNodeUI}
33348      */
33349     getUI : function(){
33350         return this.ui;
33351     },
33352
33353     // private override
33354     setFirstChild : function(node){
33355         var of = this.firstChild;
33356         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33357         if(this.childrenRendered && of && node != of){
33358             of.renderIndent(true, true);
33359         }
33360         if(this.rendered){
33361             this.renderIndent(true, true);
33362         }
33363     },
33364
33365     // private override
33366     setLastChild : function(node){
33367         var ol = this.lastChild;
33368         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33369         if(this.childrenRendered && ol && node != ol){
33370             ol.renderIndent(true, true);
33371         }
33372         if(this.rendered){
33373             this.renderIndent(true, true);
33374         }
33375     },
33376
33377     // these methods are overridden to provide lazy rendering support
33378     // private override
33379     appendChild : function()
33380     {
33381         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33382         if(node && this.childrenRendered){
33383             node.render();
33384         }
33385         this.ui.updateExpandIcon();
33386         return node;
33387     },
33388
33389     // private override
33390     removeChild : function(node){
33391         this.ownerTree.getSelectionModel().unselect(node);
33392         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33393         // if it's been rendered remove dom node
33394         if(this.childrenRendered){
33395             node.ui.remove();
33396         }
33397         if(this.childNodes.length < 1){
33398             this.collapse(false, false);
33399         }else{
33400             this.ui.updateExpandIcon();
33401         }
33402         if(!this.firstChild) {
33403             this.childrenRendered = false;
33404         }
33405         return node;
33406     },
33407
33408     // private override
33409     insertBefore : function(node, refNode){
33410         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33411         if(newNode && refNode && this.childrenRendered){
33412             node.render();
33413         }
33414         this.ui.updateExpandIcon();
33415         return newNode;
33416     },
33417
33418     /**
33419      * Sets the text for this node
33420      * @param {String} text
33421      */
33422     setText : function(text){
33423         var oldText = this.text;
33424         this.text = text;
33425         this.attributes.text = text;
33426         if(this.rendered){ // event without subscribing
33427             this.ui.onTextChange(this, text, oldText);
33428         }
33429         this.fireEvent("textchange", this, text, oldText);
33430     },
33431
33432     /**
33433      * Triggers selection of this node
33434      */
33435     select : function(){
33436         this.getOwnerTree().getSelectionModel().select(this);
33437     },
33438
33439     /**
33440      * Triggers deselection of this node
33441      */
33442     unselect : function(){
33443         this.getOwnerTree().getSelectionModel().unselect(this);
33444     },
33445
33446     /**
33447      * Returns true if this node is selected
33448      * @return {Boolean}
33449      */
33450     isSelected : function(){
33451         return this.getOwnerTree().getSelectionModel().isSelected(this);
33452     },
33453
33454     /**
33455      * Expand this node.
33456      * @param {Boolean} deep (optional) True to expand all children as well
33457      * @param {Boolean} anim (optional) false to cancel the default animation
33458      * @param {Function} callback (optional) A callback to be called when
33459      * expanding this node completes (does not wait for deep expand to complete).
33460      * Called with 1 parameter, this node.
33461      */
33462     expand : function(deep, anim, callback){
33463         if(!this.expanded){
33464             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33465                 return;
33466             }
33467             if(!this.childrenRendered){
33468                 this.renderChildren();
33469             }
33470             this.expanded = true;
33471             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33472                 this.ui.animExpand(function(){
33473                     this.fireEvent("expand", this);
33474                     if(typeof callback == "function"){
33475                         callback(this);
33476                     }
33477                     if(deep === true){
33478                         this.expandChildNodes(true);
33479                     }
33480                 }.createDelegate(this));
33481                 return;
33482             }else{
33483                 this.ui.expand();
33484                 this.fireEvent("expand", this);
33485                 if(typeof callback == "function"){
33486                     callback(this);
33487                 }
33488             }
33489         }else{
33490            if(typeof callback == "function"){
33491                callback(this);
33492            }
33493         }
33494         if(deep === true){
33495             this.expandChildNodes(true);
33496         }
33497     },
33498
33499     isHiddenRoot : function(){
33500         return this.isRoot && !this.getOwnerTree().rootVisible;
33501     },
33502
33503     /**
33504      * Collapse this node.
33505      * @param {Boolean} deep (optional) True to collapse all children as well
33506      * @param {Boolean} anim (optional) false to cancel the default animation
33507      */
33508     collapse : function(deep, anim){
33509         if(this.expanded && !this.isHiddenRoot()){
33510             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33511                 return;
33512             }
33513             this.expanded = false;
33514             if((this.getOwnerTree().animate && anim !== false) || anim){
33515                 this.ui.animCollapse(function(){
33516                     this.fireEvent("collapse", this);
33517                     if(deep === true){
33518                         this.collapseChildNodes(true);
33519                     }
33520                 }.createDelegate(this));
33521                 return;
33522             }else{
33523                 this.ui.collapse();
33524                 this.fireEvent("collapse", this);
33525             }
33526         }
33527         if(deep === true){
33528             var cs = this.childNodes;
33529             for(var i = 0, len = cs.length; i < len; i++) {
33530                 cs[i].collapse(true, false);
33531             }
33532         }
33533     },
33534
33535     // private
33536     delayedExpand : function(delay){
33537         if(!this.expandProcId){
33538             this.expandProcId = this.expand.defer(delay, this);
33539         }
33540     },
33541
33542     // private
33543     cancelExpand : function(){
33544         if(this.expandProcId){
33545             clearTimeout(this.expandProcId);
33546         }
33547         this.expandProcId = false;
33548     },
33549
33550     /**
33551      * Toggles expanded/collapsed state of the node
33552      */
33553     toggle : function(){
33554         if(this.expanded){
33555             this.collapse();
33556         }else{
33557             this.expand();
33558         }
33559     },
33560
33561     /**
33562      * Ensures all parent nodes are expanded
33563      */
33564     ensureVisible : function(callback){
33565         var tree = this.getOwnerTree();
33566         tree.expandPath(this.parentNode.getPath(), false, function(){
33567             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33568             Roo.callback(callback);
33569         }.createDelegate(this));
33570     },
33571
33572     /**
33573      * Expand all child nodes
33574      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33575      */
33576     expandChildNodes : function(deep){
33577         var cs = this.childNodes;
33578         for(var i = 0, len = cs.length; i < len; i++) {
33579                 cs[i].expand(deep);
33580         }
33581     },
33582
33583     /**
33584      * Collapse all child nodes
33585      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33586      */
33587     collapseChildNodes : function(deep){
33588         var cs = this.childNodes;
33589         for(var i = 0, len = cs.length; i < len; i++) {
33590                 cs[i].collapse(deep);
33591         }
33592     },
33593
33594     /**
33595      * Disables this node
33596      */
33597     disable : function(){
33598         this.disabled = true;
33599         this.unselect();
33600         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33601             this.ui.onDisableChange(this, true);
33602         }
33603         this.fireEvent("disabledchange", this, true);
33604     },
33605
33606     /**
33607      * Enables this node
33608      */
33609     enable : function(){
33610         this.disabled = false;
33611         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33612             this.ui.onDisableChange(this, false);
33613         }
33614         this.fireEvent("disabledchange", this, false);
33615     },
33616
33617     // private
33618     renderChildren : function(suppressEvent){
33619         if(suppressEvent !== false){
33620             this.fireEvent("beforechildrenrendered", this);
33621         }
33622         var cs = this.childNodes;
33623         for(var i = 0, len = cs.length; i < len; i++){
33624             cs[i].render(true);
33625         }
33626         this.childrenRendered = true;
33627     },
33628
33629     // private
33630     sort : function(fn, scope){
33631         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33632         if(this.childrenRendered){
33633             var cs = this.childNodes;
33634             for(var i = 0, len = cs.length; i < len; i++){
33635                 cs[i].render(true);
33636             }
33637         }
33638     },
33639
33640     // private
33641     render : function(bulkRender){
33642         this.ui.render(bulkRender);
33643         if(!this.rendered){
33644             this.rendered = true;
33645             if(this.expanded){
33646                 this.expanded = false;
33647                 this.expand(false, false);
33648             }
33649         }
33650     },
33651
33652     // private
33653     renderIndent : function(deep, refresh){
33654         if(refresh){
33655             this.ui.childIndent = null;
33656         }
33657         this.ui.renderIndent();
33658         if(deep === true && this.childrenRendered){
33659             var cs = this.childNodes;
33660             for(var i = 0, len = cs.length; i < len; i++){
33661                 cs[i].renderIndent(true, refresh);
33662             }
33663         }
33664     }
33665 });/*
33666  * Based on:
33667  * Ext JS Library 1.1.1
33668  * Copyright(c) 2006-2007, Ext JS, LLC.
33669  *
33670  * Originally Released Under LGPL - original licence link has changed is not relivant.
33671  *
33672  * Fork - LGPL
33673  * <script type="text/javascript">
33674  */
33675  
33676 /**
33677  * @class Roo.tree.AsyncTreeNode
33678  * @extends Roo.tree.TreeNode
33679  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33680  * @constructor
33681  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33682  */
33683  Roo.tree.AsyncTreeNode = function(config){
33684     this.loaded = false;
33685     this.loading = false;
33686     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33687     /**
33688     * @event beforeload
33689     * Fires before this node is loaded, return false to cancel
33690     * @param {Node} this This node
33691     */
33692     this.addEvents({'beforeload':true, 'load': true});
33693     /**
33694     * @event load
33695     * Fires when this node is loaded
33696     * @param {Node} this This node
33697     */
33698     /**
33699      * The loader used by this node (defaults to using the tree's defined loader)
33700      * @type TreeLoader
33701      * @property loader
33702      */
33703 };
33704 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33705     expand : function(deep, anim, callback){
33706         if(this.loading){ // if an async load is already running, waiting til it's done
33707             var timer;
33708             var f = function(){
33709                 if(!this.loading){ // done loading
33710                     clearInterval(timer);
33711                     this.expand(deep, anim, callback);
33712                 }
33713             }.createDelegate(this);
33714             timer = setInterval(f, 200);
33715             return;
33716         }
33717         if(!this.loaded){
33718             if(this.fireEvent("beforeload", this) === false){
33719                 return;
33720             }
33721             this.loading = true;
33722             this.ui.beforeLoad(this);
33723             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33724             if(loader){
33725                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33726                 return;
33727             }
33728         }
33729         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33730     },
33731     
33732     /**
33733      * Returns true if this node is currently loading
33734      * @return {Boolean}
33735      */
33736     isLoading : function(){
33737         return this.loading;  
33738     },
33739     
33740     loadComplete : function(deep, anim, callback){
33741         this.loading = false;
33742         this.loaded = true;
33743         this.ui.afterLoad(this);
33744         this.fireEvent("load", this);
33745         this.expand(deep, anim, callback);
33746     },
33747     
33748     /**
33749      * Returns true if this node has been loaded
33750      * @return {Boolean}
33751      */
33752     isLoaded : function(){
33753         return this.loaded;
33754     },
33755     
33756     hasChildNodes : function(){
33757         if(!this.isLeaf() && !this.loaded){
33758             return true;
33759         }else{
33760             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33761         }
33762     },
33763
33764     /**
33765      * Trigger a reload for this node
33766      * @param {Function} callback
33767      */
33768     reload : function(callback){
33769         this.collapse(false, false);
33770         while(this.firstChild){
33771             this.removeChild(this.firstChild);
33772         }
33773         this.childrenRendered = false;
33774         this.loaded = false;
33775         if(this.isHiddenRoot()){
33776             this.expanded = false;
33777         }
33778         this.expand(false, false, callback);
33779     }
33780 });/*
33781  * Based on:
33782  * Ext JS Library 1.1.1
33783  * Copyright(c) 2006-2007, Ext JS, LLC.
33784  *
33785  * Originally Released Under LGPL - original licence link has changed is not relivant.
33786  *
33787  * Fork - LGPL
33788  * <script type="text/javascript">
33789  */
33790  
33791 /**
33792  * @class Roo.tree.TreeNodeUI
33793  * @constructor
33794  * @param {Object} node The node to render
33795  * The TreeNode UI implementation is separate from the
33796  * tree implementation. Unless you are customizing the tree UI,
33797  * you should never have to use this directly.
33798  */
33799 Roo.tree.TreeNodeUI = function(node){
33800     this.node = node;
33801     this.rendered = false;
33802     this.animating = false;
33803     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33804 };
33805
33806 Roo.tree.TreeNodeUI.prototype = {
33807     removeChild : function(node){
33808         if(this.rendered){
33809             this.ctNode.removeChild(node.ui.getEl());
33810         }
33811     },
33812
33813     beforeLoad : function(){
33814          this.addClass("x-tree-node-loading");
33815     },
33816
33817     afterLoad : function(){
33818          this.removeClass("x-tree-node-loading");
33819     },
33820
33821     onTextChange : function(node, text, oldText){
33822         if(this.rendered){
33823             this.textNode.innerHTML = text;
33824         }
33825     },
33826
33827     onDisableChange : function(node, state){
33828         this.disabled = state;
33829         if(state){
33830             this.addClass("x-tree-node-disabled");
33831         }else{
33832             this.removeClass("x-tree-node-disabled");
33833         }
33834     },
33835
33836     onSelectedChange : function(state){
33837         if(state){
33838             this.focus();
33839             this.addClass("x-tree-selected");
33840         }else{
33841             //this.blur();
33842             this.removeClass("x-tree-selected");
33843         }
33844     },
33845
33846     onMove : function(tree, node, oldParent, newParent, index, refNode){
33847         this.childIndent = null;
33848         if(this.rendered){
33849             var targetNode = newParent.ui.getContainer();
33850             if(!targetNode){//target not rendered
33851                 this.holder = document.createElement("div");
33852                 this.holder.appendChild(this.wrap);
33853                 return;
33854             }
33855             var insertBefore = refNode ? refNode.ui.getEl() : null;
33856             if(insertBefore){
33857                 targetNode.insertBefore(this.wrap, insertBefore);
33858             }else{
33859                 targetNode.appendChild(this.wrap);
33860             }
33861             this.node.renderIndent(true);
33862         }
33863     },
33864
33865     addClass : function(cls){
33866         if(this.elNode){
33867             Roo.fly(this.elNode).addClass(cls);
33868         }
33869     },
33870
33871     removeClass : function(cls){
33872         if(this.elNode){
33873             Roo.fly(this.elNode).removeClass(cls);
33874         }
33875     },
33876
33877     remove : function(){
33878         if(this.rendered){
33879             this.holder = document.createElement("div");
33880             this.holder.appendChild(this.wrap);
33881         }
33882     },
33883
33884     fireEvent : function(){
33885         return this.node.fireEvent.apply(this.node, arguments);
33886     },
33887
33888     initEvents : function(){
33889         this.node.on("move", this.onMove, this);
33890         var E = Roo.EventManager;
33891         var a = this.anchor;
33892
33893         var el = Roo.fly(a, '_treeui');
33894
33895         if(Roo.isOpera){ // opera render bug ignores the CSS
33896             el.setStyle("text-decoration", "none");
33897         }
33898
33899         el.on("click", this.onClick, this);
33900         el.on("dblclick", this.onDblClick, this);
33901
33902         if(this.checkbox){
33903             Roo.EventManager.on(this.checkbox,
33904                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33905         }
33906
33907         el.on("contextmenu", this.onContextMenu, this);
33908
33909         var icon = Roo.fly(this.iconNode);
33910         icon.on("click", this.onClick, this);
33911         icon.on("dblclick", this.onDblClick, this);
33912         icon.on("contextmenu", this.onContextMenu, this);
33913         E.on(this.ecNode, "click", this.ecClick, this, true);
33914
33915         if(this.node.disabled){
33916             this.addClass("x-tree-node-disabled");
33917         }
33918         if(this.node.hidden){
33919             this.addClass("x-tree-node-disabled");
33920         }
33921         var ot = this.node.getOwnerTree();
33922         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33923         if(dd && (!this.node.isRoot || ot.rootVisible)){
33924             Roo.dd.Registry.register(this.elNode, {
33925                 node: this.node,
33926                 handles: this.getDDHandles(),
33927                 isHandle: false
33928             });
33929         }
33930     },
33931
33932     getDDHandles : function(){
33933         return [this.iconNode, this.textNode];
33934     },
33935
33936     hide : function(){
33937         if(this.rendered){
33938             this.wrap.style.display = "none";
33939         }
33940     },
33941
33942     show : function(){
33943         if(this.rendered){
33944             this.wrap.style.display = "";
33945         }
33946     },
33947
33948     onContextMenu : function(e){
33949         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33950             e.preventDefault();
33951             this.focus();
33952             this.fireEvent("contextmenu", this.node, e);
33953         }
33954     },
33955
33956     onClick : function(e){
33957         if(this.dropping){
33958             e.stopEvent();
33959             return;
33960         }
33961         if(this.fireEvent("beforeclick", this.node, e) !== false){
33962             if(!this.disabled && this.node.attributes.href){
33963                 this.fireEvent("click", this.node, e);
33964                 return;
33965             }
33966             e.preventDefault();
33967             if(this.disabled){
33968                 return;
33969             }
33970
33971             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33972                 this.node.toggle();
33973             }
33974
33975             this.fireEvent("click", this.node, e);
33976         }else{
33977             e.stopEvent();
33978         }
33979     },
33980
33981     onDblClick : function(e){
33982         e.preventDefault();
33983         if(this.disabled){
33984             return;
33985         }
33986         if(this.checkbox){
33987             this.toggleCheck();
33988         }
33989         if(!this.animating && this.node.hasChildNodes()){
33990             this.node.toggle();
33991         }
33992         this.fireEvent("dblclick", this.node, e);
33993     },
33994
33995     onCheckChange : function(){
33996         var checked = this.checkbox.checked;
33997         this.node.attributes.checked = checked;
33998         this.fireEvent('checkchange', this.node, checked);
33999     },
34000
34001     ecClick : function(e){
34002         if(!this.animating && this.node.hasChildNodes()){
34003             this.node.toggle();
34004         }
34005     },
34006
34007     startDrop : function(){
34008         this.dropping = true;
34009     },
34010
34011     // delayed drop so the click event doesn't get fired on a drop
34012     endDrop : function(){
34013        setTimeout(function(){
34014            this.dropping = false;
34015        }.createDelegate(this), 50);
34016     },
34017
34018     expand : function(){
34019         this.updateExpandIcon();
34020         this.ctNode.style.display = "";
34021     },
34022
34023     focus : function(){
34024         if(!this.node.preventHScroll){
34025             try{this.anchor.focus();
34026             }catch(e){}
34027         }else if(!Roo.isIE){
34028             try{
34029                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34030                 var l = noscroll.scrollLeft;
34031                 this.anchor.focus();
34032                 noscroll.scrollLeft = l;
34033             }catch(e){}
34034         }
34035     },
34036
34037     toggleCheck : function(value){
34038         var cb = this.checkbox;
34039         if(cb){
34040             cb.checked = (value === undefined ? !cb.checked : value);
34041         }
34042     },
34043
34044     blur : function(){
34045         try{
34046             this.anchor.blur();
34047         }catch(e){}
34048     },
34049
34050     animExpand : function(callback){
34051         var ct = Roo.get(this.ctNode);
34052         ct.stopFx();
34053         if(!this.node.hasChildNodes()){
34054             this.updateExpandIcon();
34055             this.ctNode.style.display = "";
34056             Roo.callback(callback);
34057             return;
34058         }
34059         this.animating = true;
34060         this.updateExpandIcon();
34061
34062         ct.slideIn('t', {
34063            callback : function(){
34064                this.animating = false;
34065                Roo.callback(callback);
34066             },
34067             scope: this,
34068             duration: this.node.ownerTree.duration || .25
34069         });
34070     },
34071
34072     highlight : function(){
34073         var tree = this.node.getOwnerTree();
34074         Roo.fly(this.wrap).highlight(
34075             tree.hlColor || "C3DAF9",
34076             {endColor: tree.hlBaseColor}
34077         );
34078     },
34079
34080     collapse : function(){
34081         this.updateExpandIcon();
34082         this.ctNode.style.display = "none";
34083     },
34084
34085     animCollapse : function(callback){
34086         var ct = Roo.get(this.ctNode);
34087         ct.enableDisplayMode('block');
34088         ct.stopFx();
34089
34090         this.animating = true;
34091         this.updateExpandIcon();
34092
34093         ct.slideOut('t', {
34094             callback : function(){
34095                this.animating = false;
34096                Roo.callback(callback);
34097             },
34098             scope: this,
34099             duration: this.node.ownerTree.duration || .25
34100         });
34101     },
34102
34103     getContainer : function(){
34104         return this.ctNode;
34105     },
34106
34107     getEl : function(){
34108         return this.wrap;
34109     },
34110
34111     appendDDGhost : function(ghostNode){
34112         ghostNode.appendChild(this.elNode.cloneNode(true));
34113     },
34114
34115     getDDRepairXY : function(){
34116         return Roo.lib.Dom.getXY(this.iconNode);
34117     },
34118
34119     onRender : function(){
34120         this.render();
34121     },
34122
34123     render : function(bulkRender){
34124         var n = this.node, a = n.attributes;
34125         var targetNode = n.parentNode ?
34126               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34127
34128         if(!this.rendered){
34129             this.rendered = true;
34130
34131             this.renderElements(n, a, targetNode, bulkRender);
34132
34133             if(a.qtip){
34134                if(this.textNode.setAttributeNS){
34135                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34136                    if(a.qtipTitle){
34137                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34138                    }
34139                }else{
34140                    this.textNode.setAttribute("ext:qtip", a.qtip);
34141                    if(a.qtipTitle){
34142                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34143                    }
34144                }
34145             }else if(a.qtipCfg){
34146                 a.qtipCfg.target = Roo.id(this.textNode);
34147                 Roo.QuickTips.register(a.qtipCfg);
34148             }
34149             this.initEvents();
34150             if(!this.node.expanded){
34151                 this.updateExpandIcon();
34152             }
34153         }else{
34154             if(bulkRender === true) {
34155                 targetNode.appendChild(this.wrap);
34156             }
34157         }
34158     },
34159
34160     renderElements : function(n, a, targetNode, bulkRender)
34161     {
34162         // add some indent caching, this helps performance when rendering a large tree
34163         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34164         var t = n.getOwnerTree();
34165         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34166         if (typeof(n.attributes.html) != 'undefined') {
34167             txt = n.attributes.html;
34168         }
34169         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34170         var cb = typeof a.checked == 'boolean';
34171         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34172         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34173             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34174             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34175             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34176             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34177             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34178              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34179                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34180             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34181             "</li>"];
34182
34183         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34184             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34185                                 n.nextSibling.ui.getEl(), buf.join(""));
34186         }else{
34187             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34188         }
34189
34190         this.elNode = this.wrap.childNodes[0];
34191         this.ctNode = this.wrap.childNodes[1];
34192         var cs = this.elNode.childNodes;
34193         this.indentNode = cs[0];
34194         this.ecNode = cs[1];
34195         this.iconNode = cs[2];
34196         var index = 3;
34197         if(cb){
34198             this.checkbox = cs[3];
34199             index++;
34200         }
34201         this.anchor = cs[index];
34202         this.textNode = cs[index].firstChild;
34203     },
34204
34205     getAnchor : function(){
34206         return this.anchor;
34207     },
34208
34209     getTextEl : function(){
34210         return this.textNode;
34211     },
34212
34213     getIconEl : function(){
34214         return this.iconNode;
34215     },
34216
34217     isChecked : function(){
34218         return this.checkbox ? this.checkbox.checked : false;
34219     },
34220
34221     updateExpandIcon : function(){
34222         if(this.rendered){
34223             var n = this.node, c1, c2;
34224             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34225             var hasChild = n.hasChildNodes();
34226             if(hasChild){
34227                 if(n.expanded){
34228                     cls += "-minus";
34229                     c1 = "x-tree-node-collapsed";
34230                     c2 = "x-tree-node-expanded";
34231                 }else{
34232                     cls += "-plus";
34233                     c1 = "x-tree-node-expanded";
34234                     c2 = "x-tree-node-collapsed";
34235                 }
34236                 if(this.wasLeaf){
34237                     this.removeClass("x-tree-node-leaf");
34238                     this.wasLeaf = false;
34239                 }
34240                 if(this.c1 != c1 || this.c2 != c2){
34241                     Roo.fly(this.elNode).replaceClass(c1, c2);
34242                     this.c1 = c1; this.c2 = c2;
34243                 }
34244             }else{
34245                 // this changes non-leafs into leafs if they have no children.
34246                 // it's not very rational behaviour..
34247                 
34248                 if(!this.wasLeaf && this.node.leaf){
34249                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34250                     delete this.c1;
34251                     delete this.c2;
34252                     this.wasLeaf = true;
34253                 }
34254             }
34255             var ecc = "x-tree-ec-icon "+cls;
34256             if(this.ecc != ecc){
34257                 this.ecNode.className = ecc;
34258                 this.ecc = ecc;
34259             }
34260         }
34261     },
34262
34263     getChildIndent : function(){
34264         if(!this.childIndent){
34265             var buf = [];
34266             var p = this.node;
34267             while(p){
34268                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34269                     if(!p.isLast()) {
34270                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34271                     } else {
34272                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34273                     }
34274                 }
34275                 p = p.parentNode;
34276             }
34277             this.childIndent = buf.join("");
34278         }
34279         return this.childIndent;
34280     },
34281
34282     renderIndent : function(){
34283         if(this.rendered){
34284             var indent = "";
34285             var p = this.node.parentNode;
34286             if(p){
34287                 indent = p.ui.getChildIndent();
34288             }
34289             if(this.indentMarkup != indent){ // don't rerender if not required
34290                 this.indentNode.innerHTML = indent;
34291                 this.indentMarkup = indent;
34292             }
34293             this.updateExpandIcon();
34294         }
34295     }
34296 };
34297
34298 Roo.tree.RootTreeNodeUI = function(){
34299     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34300 };
34301 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34302     render : function(){
34303         if(!this.rendered){
34304             var targetNode = this.node.ownerTree.innerCt.dom;
34305             this.node.expanded = true;
34306             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34307             this.wrap = this.ctNode = targetNode.firstChild;
34308         }
34309     },
34310     collapse : function(){
34311     },
34312     expand : function(){
34313     }
34314 });/*
34315  * Based on:
34316  * Ext JS Library 1.1.1
34317  * Copyright(c) 2006-2007, Ext JS, LLC.
34318  *
34319  * Originally Released Under LGPL - original licence link has changed is not relivant.
34320  *
34321  * Fork - LGPL
34322  * <script type="text/javascript">
34323  */
34324 /**
34325  * @class Roo.tree.TreeLoader
34326  * @extends Roo.util.Observable
34327  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34328  * nodes from a specified URL. The response must be a javascript Array definition
34329  * who's elements are node definition objects. eg:
34330  * <pre><code>
34331 {  success : true,
34332    data :      [
34333    
34334     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34335     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34336     ]
34337 }
34338
34339
34340 </code></pre>
34341  * <br><br>
34342  * The old style respose with just an array is still supported, but not recommended.
34343  * <br><br>
34344  *
34345  * A server request is sent, and child nodes are loaded only when a node is expanded.
34346  * The loading node's id is passed to the server under the parameter name "node" to
34347  * enable the server to produce the correct child nodes.
34348  * <br><br>
34349  * To pass extra parameters, an event handler may be attached to the "beforeload"
34350  * event, and the parameters specified in the TreeLoader's baseParams property:
34351  * <pre><code>
34352     myTreeLoader.on("beforeload", function(treeLoader, node) {
34353         this.baseParams.category = node.attributes.category;
34354     }, this);
34355 </code></pre><
34356  * This would pass an HTTP parameter called "category" to the server containing
34357  * the value of the Node's "category" attribute.
34358  * @constructor
34359  * Creates a new Treeloader.
34360  * @param {Object} config A config object containing config properties.
34361  */
34362 Roo.tree.TreeLoader = function(config){
34363     this.baseParams = {};
34364     this.requestMethod = "POST";
34365     Roo.apply(this, config);
34366
34367     this.addEvents({
34368     
34369         /**
34370          * @event beforeload
34371          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34372          * @param {Object} This TreeLoader object.
34373          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34374          * @param {Object} callback The callback function specified in the {@link #load} call.
34375          */
34376         beforeload : true,
34377         /**
34378          * @event load
34379          * Fires when the node has been successfuly loaded.
34380          * @param {Object} This TreeLoader object.
34381          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34382          * @param {Object} response The response object containing the data from the server.
34383          */
34384         load : true,
34385         /**
34386          * @event loadexception
34387          * Fires if the network request failed.
34388          * @param {Object} This TreeLoader object.
34389          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34390          * @param {Object} response The response object containing the data from the server.
34391          */
34392         loadexception : true,
34393         /**
34394          * @event create
34395          * Fires before a node is created, enabling you to return custom Node types 
34396          * @param {Object} This TreeLoader object.
34397          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34398          */
34399         create : true
34400     });
34401
34402     Roo.tree.TreeLoader.superclass.constructor.call(this);
34403 };
34404
34405 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34406     /**
34407     * @cfg {String} dataUrl The URL from which to request a Json string which
34408     * specifies an array of node definition object representing the child nodes
34409     * to be loaded.
34410     */
34411     /**
34412     * @cfg {String} requestMethod either GET or POST
34413     * defaults to POST (due to BC)
34414     * to be loaded.
34415     */
34416     /**
34417     * @cfg {Object} baseParams (optional) An object containing properties which
34418     * specify HTTP parameters to be passed to each request for child nodes.
34419     */
34420     /**
34421     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34422     * created by this loader. If the attributes sent by the server have an attribute in this object,
34423     * they take priority.
34424     */
34425     /**
34426     * @cfg {Object} uiProviders (optional) An object containing properties which
34427     * 
34428     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34429     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34430     * <i>uiProvider</i> attribute of a returned child node is a string rather
34431     * than a reference to a TreeNodeUI implementation, this that string value
34432     * is used as a property name in the uiProviders object. You can define the provider named
34433     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34434     */
34435     uiProviders : {},
34436
34437     /**
34438     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34439     * child nodes before loading.
34440     */
34441     clearOnLoad : true,
34442
34443     /**
34444     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34445     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34446     * Grid query { data : [ .....] }
34447     */
34448     
34449     root : false,
34450      /**
34451     * @cfg {String} queryParam (optional) 
34452     * Name of the query as it will be passed on the querystring (defaults to 'node')
34453     * eg. the request will be ?node=[id]
34454     */
34455     
34456     
34457     queryParam: false,
34458     
34459     /**
34460      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34461      * This is called automatically when a node is expanded, but may be used to reload
34462      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34463      * @param {Roo.tree.TreeNode} node
34464      * @param {Function} callback
34465      */
34466     load : function(node, callback){
34467         if(this.clearOnLoad){
34468             while(node.firstChild){
34469                 node.removeChild(node.firstChild);
34470             }
34471         }
34472         if(node.attributes.children){ // preloaded json children
34473             var cs = node.attributes.children;
34474             for(var i = 0, len = cs.length; i < len; i++){
34475                 node.appendChild(this.createNode(cs[i]));
34476             }
34477             if(typeof callback == "function"){
34478                 callback();
34479             }
34480         }else if(this.dataUrl){
34481             this.requestData(node, callback);
34482         }
34483     },
34484
34485     getParams: function(node){
34486         var buf = [], bp = this.baseParams;
34487         for(var key in bp){
34488             if(typeof bp[key] != "function"){
34489                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34490             }
34491         }
34492         var n = this.queryParam === false ? 'node' : this.queryParam;
34493         buf.push(n + "=", encodeURIComponent(node.id));
34494         return buf.join("");
34495     },
34496
34497     requestData : function(node, callback){
34498         if(this.fireEvent("beforeload", this, node, callback) !== false){
34499             this.transId = Roo.Ajax.request({
34500                 method:this.requestMethod,
34501                 url: this.dataUrl||this.url,
34502                 success: this.handleResponse,
34503                 failure: this.handleFailure,
34504                 scope: this,
34505                 argument: {callback: callback, node: node},
34506                 params: this.getParams(node)
34507             });
34508         }else{
34509             // if the load is cancelled, make sure we notify
34510             // the node that we are done
34511             if(typeof callback == "function"){
34512                 callback();
34513             }
34514         }
34515     },
34516
34517     isLoading : function(){
34518         return this.transId ? true : false;
34519     },
34520
34521     abort : function(){
34522         if(this.isLoading()){
34523             Roo.Ajax.abort(this.transId);
34524         }
34525     },
34526
34527     // private
34528     createNode : function(attr)
34529     {
34530         // apply baseAttrs, nice idea Corey!
34531         if(this.baseAttrs){
34532             Roo.applyIf(attr, this.baseAttrs);
34533         }
34534         if(this.applyLoader !== false){
34535             attr.loader = this;
34536         }
34537         // uiProvider = depreciated..
34538         
34539         if(typeof(attr.uiProvider) == 'string'){
34540            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34541                 /**  eval:var:attr */ eval(attr.uiProvider);
34542         }
34543         if(typeof(this.uiProviders['default']) != 'undefined') {
34544             attr.uiProvider = this.uiProviders['default'];
34545         }
34546         
34547         this.fireEvent('create', this, attr);
34548         
34549         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34550         return(attr.leaf ?
34551                         new Roo.tree.TreeNode(attr) :
34552                         new Roo.tree.AsyncTreeNode(attr));
34553     },
34554
34555     processResponse : function(response, node, callback)
34556     {
34557         var json = response.responseText;
34558         try {
34559             
34560             var o = Roo.decode(json);
34561             
34562             if (this.root === false && typeof(o.success) != undefined) {
34563                 this.root = 'data'; // the default behaviour for list like data..
34564                 }
34565                 
34566             if (this.root !== false &&  !o.success) {
34567                 // it's a failure condition.
34568                 var a = response.argument;
34569                 this.fireEvent("loadexception", this, a.node, response);
34570                 Roo.log("Load failed - should have a handler really");
34571                 return;
34572             }
34573             
34574             
34575             
34576             if (this.root !== false) {
34577                  o = o[this.root];
34578             }
34579             
34580             for(var i = 0, len = o.length; i < len; i++){
34581                 var n = this.createNode(o[i]);
34582                 if(n){
34583                     node.appendChild(n);
34584                 }
34585             }
34586             if(typeof callback == "function"){
34587                 callback(this, node);
34588             }
34589         }catch(e){
34590             this.handleFailure(response);
34591         }
34592     },
34593
34594     handleResponse : function(response){
34595         this.transId = false;
34596         var a = response.argument;
34597         this.processResponse(response, a.node, a.callback);
34598         this.fireEvent("load", this, a.node, response);
34599     },
34600
34601     handleFailure : function(response)
34602     {
34603         // should handle failure better..
34604         this.transId = false;
34605         var a = response.argument;
34606         this.fireEvent("loadexception", this, a.node, response);
34607         if(typeof a.callback == "function"){
34608             a.callback(this, a.node);
34609         }
34610     }
34611 });/*
34612  * Based on:
34613  * Ext JS Library 1.1.1
34614  * Copyright(c) 2006-2007, Ext JS, LLC.
34615  *
34616  * Originally Released Under LGPL - original licence link has changed is not relivant.
34617  *
34618  * Fork - LGPL
34619  * <script type="text/javascript">
34620  */
34621
34622 /**
34623 * @class Roo.tree.TreeFilter
34624 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34625 * @param {TreePanel} tree
34626 * @param {Object} config (optional)
34627  */
34628 Roo.tree.TreeFilter = function(tree, config){
34629     this.tree = tree;
34630     this.filtered = {};
34631     Roo.apply(this, config);
34632 };
34633
34634 Roo.tree.TreeFilter.prototype = {
34635     clearBlank:false,
34636     reverse:false,
34637     autoClear:false,
34638     remove:false,
34639
34640      /**
34641      * Filter the data by a specific attribute.
34642      * @param {String/RegExp} value Either string that the attribute value
34643      * should start with or a RegExp to test against the attribute
34644      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34645      * @param {TreeNode} startNode (optional) The node to start the filter at.
34646      */
34647     filter : function(value, attr, startNode){
34648         attr = attr || "text";
34649         var f;
34650         if(typeof value == "string"){
34651             var vlen = value.length;
34652             // auto clear empty filter
34653             if(vlen == 0 && this.clearBlank){
34654                 this.clear();
34655                 return;
34656             }
34657             value = value.toLowerCase();
34658             f = function(n){
34659                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34660             };
34661         }else if(value.exec){ // regex?
34662             f = function(n){
34663                 return value.test(n.attributes[attr]);
34664             };
34665         }else{
34666             throw 'Illegal filter type, must be string or regex';
34667         }
34668         this.filterBy(f, null, startNode);
34669         },
34670
34671     /**
34672      * Filter by a function. The passed function will be called with each
34673      * node in the tree (or from the startNode). If the function returns true, the node is kept
34674      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34675      * @param {Function} fn The filter function
34676      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34677      */
34678     filterBy : function(fn, scope, startNode){
34679         startNode = startNode || this.tree.root;
34680         if(this.autoClear){
34681             this.clear();
34682         }
34683         var af = this.filtered, rv = this.reverse;
34684         var f = function(n){
34685             if(n == startNode){
34686                 return true;
34687             }
34688             if(af[n.id]){
34689                 return false;
34690             }
34691             var m = fn.call(scope || n, n);
34692             if(!m || rv){
34693                 af[n.id] = n;
34694                 n.ui.hide();
34695                 return false;
34696             }
34697             return true;
34698         };
34699         startNode.cascade(f);
34700         if(this.remove){
34701            for(var id in af){
34702                if(typeof id != "function"){
34703                    var n = af[id];
34704                    if(n && n.parentNode){
34705                        n.parentNode.removeChild(n);
34706                    }
34707                }
34708            }
34709         }
34710     },
34711
34712     /**
34713      * Clears the current filter. Note: with the "remove" option
34714      * set a filter cannot be cleared.
34715      */
34716     clear : function(){
34717         var t = this.tree;
34718         var af = this.filtered;
34719         for(var id in af){
34720             if(typeof id != "function"){
34721                 var n = af[id];
34722                 if(n){
34723                     n.ui.show();
34724                 }
34725             }
34726         }
34727         this.filtered = {};
34728     }
34729 };
34730 /*
34731  * Based on:
34732  * Ext JS Library 1.1.1
34733  * Copyright(c) 2006-2007, Ext JS, LLC.
34734  *
34735  * Originally Released Under LGPL - original licence link has changed is not relivant.
34736  *
34737  * Fork - LGPL
34738  * <script type="text/javascript">
34739  */
34740  
34741
34742 /**
34743  * @class Roo.tree.TreeSorter
34744  * Provides sorting of nodes in a TreePanel
34745  * 
34746  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34747  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34748  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34749  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34750  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34751  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34752  * @constructor
34753  * @param {TreePanel} tree
34754  * @param {Object} config
34755  */
34756 Roo.tree.TreeSorter = function(tree, config){
34757     Roo.apply(this, config);
34758     tree.on("beforechildrenrendered", this.doSort, this);
34759     tree.on("append", this.updateSort, this);
34760     tree.on("insert", this.updateSort, this);
34761     
34762     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34763     var p = this.property || "text";
34764     var sortType = this.sortType;
34765     var fs = this.folderSort;
34766     var cs = this.caseSensitive === true;
34767     var leafAttr = this.leafAttr || 'leaf';
34768
34769     this.sortFn = function(n1, n2){
34770         if(fs){
34771             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34772                 return 1;
34773             }
34774             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34775                 return -1;
34776             }
34777         }
34778         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34779         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34780         if(v1 < v2){
34781                         return dsc ? +1 : -1;
34782                 }else if(v1 > v2){
34783                         return dsc ? -1 : +1;
34784         }else{
34785                 return 0;
34786         }
34787     };
34788 };
34789
34790 Roo.tree.TreeSorter.prototype = {
34791     doSort : function(node){
34792         node.sort(this.sortFn);
34793     },
34794     
34795     compareNodes : function(n1, n2){
34796         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34797     },
34798     
34799     updateSort : function(tree, node){
34800         if(node.childrenRendered){
34801             this.doSort.defer(1, this, [node]);
34802         }
34803     }
34804 };/*
34805  * Based on:
34806  * Ext JS Library 1.1.1
34807  * Copyright(c) 2006-2007, Ext JS, LLC.
34808  *
34809  * Originally Released Under LGPL - original licence link has changed is not relivant.
34810  *
34811  * Fork - LGPL
34812  * <script type="text/javascript">
34813  */
34814
34815 if(Roo.dd.DropZone){
34816     
34817 Roo.tree.TreeDropZone = function(tree, config){
34818     this.allowParentInsert = false;
34819     this.allowContainerDrop = false;
34820     this.appendOnly = false;
34821     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34822     this.tree = tree;
34823     this.lastInsertClass = "x-tree-no-status";
34824     this.dragOverData = {};
34825 };
34826
34827 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34828     ddGroup : "TreeDD",
34829     scroll:  true,
34830     
34831     expandDelay : 1000,
34832     
34833     expandNode : function(node){
34834         if(node.hasChildNodes() && !node.isExpanded()){
34835             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34836         }
34837     },
34838     
34839     queueExpand : function(node){
34840         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34841     },
34842     
34843     cancelExpand : function(){
34844         if(this.expandProcId){
34845             clearTimeout(this.expandProcId);
34846             this.expandProcId = false;
34847         }
34848     },
34849     
34850     isValidDropPoint : function(n, pt, dd, e, data){
34851         if(!n || !data){ return false; }
34852         var targetNode = n.node;
34853         var dropNode = data.node;
34854         // default drop rules
34855         if(!(targetNode && targetNode.isTarget && pt)){
34856             return false;
34857         }
34858         if(pt == "append" && targetNode.allowChildren === false){
34859             return false;
34860         }
34861         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34862             return false;
34863         }
34864         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34865             return false;
34866         }
34867         // reuse the object
34868         var overEvent = this.dragOverData;
34869         overEvent.tree = this.tree;
34870         overEvent.target = targetNode;
34871         overEvent.data = data;
34872         overEvent.point = pt;
34873         overEvent.source = dd;
34874         overEvent.rawEvent = e;
34875         overEvent.dropNode = dropNode;
34876         overEvent.cancel = false;  
34877         var result = this.tree.fireEvent("nodedragover", overEvent);
34878         return overEvent.cancel === false && result !== false;
34879     },
34880     
34881     getDropPoint : function(e, n, dd)
34882     {
34883         var tn = n.node;
34884         if(tn.isRoot){
34885             return tn.allowChildren !== false ? "append" : false; // always append for root
34886         }
34887         var dragEl = n.ddel;
34888         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34889         var y = Roo.lib.Event.getPageY(e);
34890         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34891         
34892         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34893         var noAppend = tn.allowChildren === false;
34894         if(this.appendOnly || tn.parentNode.allowChildren === false){
34895             return noAppend ? false : "append";
34896         }
34897         var noBelow = false;
34898         if(!this.allowParentInsert){
34899             noBelow = tn.hasChildNodes() && tn.isExpanded();
34900         }
34901         var q = (b - t) / (noAppend ? 2 : 3);
34902         if(y >= t && y < (t + q)){
34903             return "above";
34904         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34905             return "below";
34906         }else{
34907             return "append";
34908         }
34909     },
34910     
34911     onNodeEnter : function(n, dd, e, data)
34912     {
34913         this.cancelExpand();
34914     },
34915     
34916     onNodeOver : function(n, dd, e, data)
34917     {
34918        
34919         var pt = this.getDropPoint(e, n, dd);
34920         var node = n.node;
34921         
34922         // auto node expand check
34923         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34924             this.queueExpand(node);
34925         }else if(pt != "append"){
34926             this.cancelExpand();
34927         }
34928         
34929         // set the insert point style on the target node
34930         var returnCls = this.dropNotAllowed;
34931         if(this.isValidDropPoint(n, pt, dd, e, data)){
34932            if(pt){
34933                var el = n.ddel;
34934                var cls;
34935                if(pt == "above"){
34936                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34937                    cls = "x-tree-drag-insert-above";
34938                }else if(pt == "below"){
34939                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34940                    cls = "x-tree-drag-insert-below";
34941                }else{
34942                    returnCls = "x-tree-drop-ok-append";
34943                    cls = "x-tree-drag-append";
34944                }
34945                if(this.lastInsertClass != cls){
34946                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34947                    this.lastInsertClass = cls;
34948                }
34949            }
34950        }
34951        return returnCls;
34952     },
34953     
34954     onNodeOut : function(n, dd, e, data){
34955         
34956         this.cancelExpand();
34957         this.removeDropIndicators(n);
34958     },
34959     
34960     onNodeDrop : function(n, dd, e, data){
34961         var point = this.getDropPoint(e, n, dd);
34962         var targetNode = n.node;
34963         targetNode.ui.startDrop();
34964         if(!this.isValidDropPoint(n, point, dd, e, data)){
34965             targetNode.ui.endDrop();
34966             return false;
34967         }
34968         // first try to find the drop node
34969         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34970         var dropEvent = {
34971             tree : this.tree,
34972             target: targetNode,
34973             data: data,
34974             point: point,
34975             source: dd,
34976             rawEvent: e,
34977             dropNode: dropNode,
34978             cancel: !dropNode   
34979         };
34980         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34981         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34982             targetNode.ui.endDrop();
34983             return false;
34984         }
34985         // allow target changing
34986         targetNode = dropEvent.target;
34987         if(point == "append" && !targetNode.isExpanded()){
34988             targetNode.expand(false, null, function(){
34989                 this.completeDrop(dropEvent);
34990             }.createDelegate(this));
34991         }else{
34992             this.completeDrop(dropEvent);
34993         }
34994         return true;
34995     },
34996     
34997     completeDrop : function(de){
34998         var ns = de.dropNode, p = de.point, t = de.target;
34999         if(!(ns instanceof Array)){
35000             ns = [ns];
35001         }
35002         var n;
35003         for(var i = 0, len = ns.length; i < len; i++){
35004             n = ns[i];
35005             if(p == "above"){
35006                 t.parentNode.insertBefore(n, t);
35007             }else if(p == "below"){
35008                 t.parentNode.insertBefore(n, t.nextSibling);
35009             }else{
35010                 t.appendChild(n);
35011             }
35012         }
35013         n.ui.focus();
35014         if(this.tree.hlDrop){
35015             n.ui.highlight();
35016         }
35017         t.ui.endDrop();
35018         this.tree.fireEvent("nodedrop", de);
35019     },
35020     
35021     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35022         if(this.tree.hlDrop){
35023             dropNode.ui.focus();
35024             dropNode.ui.highlight();
35025         }
35026         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35027     },
35028     
35029     getTree : function(){
35030         return this.tree;
35031     },
35032     
35033     removeDropIndicators : function(n){
35034         if(n && n.ddel){
35035             var el = n.ddel;
35036             Roo.fly(el).removeClass([
35037                     "x-tree-drag-insert-above",
35038                     "x-tree-drag-insert-below",
35039                     "x-tree-drag-append"]);
35040             this.lastInsertClass = "_noclass";
35041         }
35042     },
35043     
35044     beforeDragDrop : function(target, e, id){
35045         this.cancelExpand();
35046         return true;
35047     },
35048     
35049     afterRepair : function(data){
35050         if(data && Roo.enableFx){
35051             data.node.ui.highlight();
35052         }
35053         this.hideProxy();
35054     } 
35055     
35056 });
35057
35058 }
35059 /*
35060  * Based on:
35061  * Ext JS Library 1.1.1
35062  * Copyright(c) 2006-2007, Ext JS, LLC.
35063  *
35064  * Originally Released Under LGPL - original licence link has changed is not relivant.
35065  *
35066  * Fork - LGPL
35067  * <script type="text/javascript">
35068  */
35069  
35070
35071 if(Roo.dd.DragZone){
35072 Roo.tree.TreeDragZone = function(tree, config){
35073     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35074     this.tree = tree;
35075 };
35076
35077 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35078     ddGroup : "TreeDD",
35079    
35080     onBeforeDrag : function(data, e){
35081         var n = data.node;
35082         return n && n.draggable && !n.disabled;
35083     },
35084      
35085     
35086     onInitDrag : function(e){
35087         var data = this.dragData;
35088         this.tree.getSelectionModel().select(data.node);
35089         this.proxy.update("");
35090         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35091         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35092     },
35093     
35094     getRepairXY : function(e, data){
35095         return data.node.ui.getDDRepairXY();
35096     },
35097     
35098     onEndDrag : function(data, e){
35099         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35100         
35101         
35102     },
35103     
35104     onValidDrop : function(dd, e, id){
35105         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35106         this.hideProxy();
35107     },
35108     
35109     beforeInvalidDrop : function(e, id){
35110         // this scrolls the original position back into view
35111         var sm = this.tree.getSelectionModel();
35112         sm.clearSelections();
35113         sm.select(this.dragData.node);
35114     }
35115 });
35116 }/*
35117  * Based on:
35118  * Ext JS Library 1.1.1
35119  * Copyright(c) 2006-2007, Ext JS, LLC.
35120  *
35121  * Originally Released Under LGPL - original licence link has changed is not relivant.
35122  *
35123  * Fork - LGPL
35124  * <script type="text/javascript">
35125  */
35126 /**
35127  * @class Roo.tree.TreeEditor
35128  * @extends Roo.Editor
35129  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35130  * as the editor field.
35131  * @constructor
35132  * @param {Object} config (used to be the tree panel.)
35133  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35134  * 
35135  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35136  * @cfg {Roo.form.TextField|Object} field The field configuration
35137  *
35138  * 
35139  */
35140 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35141     var tree = config;
35142     var field;
35143     if (oldconfig) { // old style..
35144         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35145     } else {
35146         // new style..
35147         tree = config.tree;
35148         config.field = config.field  || {};
35149         config.field.xtype = 'TextField';
35150         field = Roo.factory(config.field, Roo.form);
35151     }
35152     config = config || {};
35153     
35154     
35155     this.addEvents({
35156         /**
35157          * @event beforenodeedit
35158          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35159          * false from the handler of this event.
35160          * @param {Editor} this
35161          * @param {Roo.tree.Node} node 
35162          */
35163         "beforenodeedit" : true
35164     });
35165     
35166     //Roo.log(config);
35167     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35168
35169     this.tree = tree;
35170
35171     tree.on('beforeclick', this.beforeNodeClick, this);
35172     tree.getTreeEl().on('mousedown', this.hide, this);
35173     this.on('complete', this.updateNode, this);
35174     this.on('beforestartedit', this.fitToTree, this);
35175     this.on('startedit', this.bindScroll, this, {delay:10});
35176     this.on('specialkey', this.onSpecialKey, this);
35177 };
35178
35179 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35180     /**
35181      * @cfg {String} alignment
35182      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35183      */
35184     alignment: "l-l",
35185     // inherit
35186     autoSize: false,
35187     /**
35188      * @cfg {Boolean} hideEl
35189      * True to hide the bound element while the editor is displayed (defaults to false)
35190      */
35191     hideEl : false,
35192     /**
35193      * @cfg {String} cls
35194      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35195      */
35196     cls: "x-small-editor x-tree-editor",
35197     /**
35198      * @cfg {Boolean} shim
35199      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35200      */
35201     shim:false,
35202     // inherit
35203     shadow:"frame",
35204     /**
35205      * @cfg {Number} maxWidth
35206      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35207      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35208      * scroll and client offsets into account prior to each edit.
35209      */
35210     maxWidth: 250,
35211
35212     editDelay : 350,
35213
35214     // private
35215     fitToTree : function(ed, el){
35216         var td = this.tree.getTreeEl().dom, nd = el.dom;
35217         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35218             td.scrollLeft = nd.offsetLeft;
35219         }
35220         var w = Math.min(
35221                 this.maxWidth,
35222                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35223         this.setSize(w, '');
35224         
35225         return this.fireEvent('beforenodeedit', this, this.editNode);
35226         
35227     },
35228
35229     // private
35230     triggerEdit : function(node){
35231         this.completeEdit();
35232         this.editNode = node;
35233         this.startEdit(node.ui.textNode, node.text);
35234     },
35235
35236     // private
35237     bindScroll : function(){
35238         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35239     },
35240
35241     // private
35242     beforeNodeClick : function(node, e){
35243         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35244         this.lastClick = new Date();
35245         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35246             e.stopEvent();
35247             this.triggerEdit(node);
35248             return false;
35249         }
35250         return true;
35251     },
35252
35253     // private
35254     updateNode : function(ed, value){
35255         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35256         this.editNode.setText(value);
35257     },
35258
35259     // private
35260     onHide : function(){
35261         Roo.tree.TreeEditor.superclass.onHide.call(this);
35262         if(this.editNode){
35263             this.editNode.ui.focus();
35264         }
35265     },
35266
35267     // private
35268     onSpecialKey : function(field, e){
35269         var k = e.getKey();
35270         if(k == e.ESC){
35271             e.stopEvent();
35272             this.cancelEdit();
35273         }else if(k == e.ENTER && !e.hasModifier()){
35274             e.stopEvent();
35275             this.completeEdit();
35276         }
35277     }
35278 });//<Script type="text/javascript">
35279 /*
35280  * Based on:
35281  * Ext JS Library 1.1.1
35282  * Copyright(c) 2006-2007, Ext JS, LLC.
35283  *
35284  * Originally Released Under LGPL - original licence link has changed is not relivant.
35285  *
35286  * Fork - LGPL
35287  * <script type="text/javascript">
35288  */
35289  
35290 /**
35291  * Not documented??? - probably should be...
35292  */
35293
35294 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35295     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35296     
35297     renderElements : function(n, a, targetNode, bulkRender){
35298         //consel.log("renderElements?");
35299         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35300
35301         var t = n.getOwnerTree();
35302         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35303         
35304         var cols = t.columns;
35305         var bw = t.borderWidth;
35306         var c = cols[0];
35307         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35308          var cb = typeof a.checked == "boolean";
35309         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35310         var colcls = 'x-t-' + tid + '-c0';
35311         var buf = [
35312             '<li class="x-tree-node">',
35313             
35314                 
35315                 '<div class="x-tree-node-el ', a.cls,'">',
35316                     // extran...
35317                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35318                 
35319                 
35320                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35321                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35322                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35323                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35324                            (a.iconCls ? ' '+a.iconCls : ''),
35325                            '" unselectable="on" />',
35326                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35327                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35328                              
35329                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35330                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35331                             '<span unselectable="on" qtip="' + tx + '">',
35332                              tx,
35333                              '</span></a>' ,
35334                     '</div>',
35335                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35336                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35337                  ];
35338         for(var i = 1, len = cols.length; i < len; i++){
35339             c = cols[i];
35340             colcls = 'x-t-' + tid + '-c' +i;
35341             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35342             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35343                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35344                       "</div>");
35345          }
35346          
35347          buf.push(
35348             '</a>',
35349             '<div class="x-clear"></div></div>',
35350             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35351             "</li>");
35352         
35353         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35354             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35355                                 n.nextSibling.ui.getEl(), buf.join(""));
35356         }else{
35357             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35358         }
35359         var el = this.wrap.firstChild;
35360         this.elRow = el;
35361         this.elNode = el.firstChild;
35362         this.ranchor = el.childNodes[1];
35363         this.ctNode = this.wrap.childNodes[1];
35364         var cs = el.firstChild.childNodes;
35365         this.indentNode = cs[0];
35366         this.ecNode = cs[1];
35367         this.iconNode = cs[2];
35368         var index = 3;
35369         if(cb){
35370             this.checkbox = cs[3];
35371             index++;
35372         }
35373         this.anchor = cs[index];
35374         
35375         this.textNode = cs[index].firstChild;
35376         
35377         //el.on("click", this.onClick, this);
35378         //el.on("dblclick", this.onDblClick, this);
35379         
35380         
35381        // console.log(this);
35382     },
35383     initEvents : function(){
35384         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35385         
35386             
35387         var a = this.ranchor;
35388
35389         var el = Roo.get(a);
35390
35391         if(Roo.isOpera){ // opera render bug ignores the CSS
35392             el.setStyle("text-decoration", "none");
35393         }
35394
35395         el.on("click", this.onClick, this);
35396         el.on("dblclick", this.onDblClick, this);
35397         el.on("contextmenu", this.onContextMenu, this);
35398         
35399     },
35400     
35401     /*onSelectedChange : function(state){
35402         if(state){
35403             this.focus();
35404             this.addClass("x-tree-selected");
35405         }else{
35406             //this.blur();
35407             this.removeClass("x-tree-selected");
35408         }
35409     },*/
35410     addClass : function(cls){
35411         if(this.elRow){
35412             Roo.fly(this.elRow).addClass(cls);
35413         }
35414         
35415     },
35416     
35417     
35418     removeClass : function(cls){
35419         if(this.elRow){
35420             Roo.fly(this.elRow).removeClass(cls);
35421         }
35422     }
35423
35424     
35425     
35426 });//<Script type="text/javascript">
35427
35428 /*
35429  * Based on:
35430  * Ext JS Library 1.1.1
35431  * Copyright(c) 2006-2007, Ext JS, LLC.
35432  *
35433  * Originally Released Under LGPL - original licence link has changed is not relivant.
35434  *
35435  * Fork - LGPL
35436  * <script type="text/javascript">
35437  */
35438  
35439
35440 /**
35441  * @class Roo.tree.ColumnTree
35442  * @extends Roo.data.TreePanel
35443  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35444  * @cfg {int} borderWidth  compined right/left border allowance
35445  * @constructor
35446  * @param {String/HTMLElement/Element} el The container element
35447  * @param {Object} config
35448  */
35449 Roo.tree.ColumnTree =  function(el, config)
35450 {
35451    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35452    this.addEvents({
35453         /**
35454         * @event resize
35455         * Fire this event on a container when it resizes
35456         * @param {int} w Width
35457         * @param {int} h Height
35458         */
35459        "resize" : true
35460     });
35461     this.on('resize', this.onResize, this);
35462 };
35463
35464 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35465     //lines:false,
35466     
35467     
35468     borderWidth: Roo.isBorderBox ? 0 : 2, 
35469     headEls : false,
35470     
35471     render : function(){
35472         // add the header.....
35473        
35474         Roo.tree.ColumnTree.superclass.render.apply(this);
35475         
35476         this.el.addClass('x-column-tree');
35477         
35478         this.headers = this.el.createChild(
35479             {cls:'x-tree-headers'},this.innerCt.dom);
35480    
35481         var cols = this.columns, c;
35482         var totalWidth = 0;
35483         this.headEls = [];
35484         var  len = cols.length;
35485         for(var i = 0; i < len; i++){
35486              c = cols[i];
35487              totalWidth += c.width;
35488             this.headEls.push(this.headers.createChild({
35489                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35490                  cn: {
35491                      cls:'x-tree-hd-text',
35492                      html: c.header
35493                  },
35494                  style:'width:'+(c.width-this.borderWidth)+'px;'
35495              }));
35496         }
35497         this.headers.createChild({cls:'x-clear'});
35498         // prevent floats from wrapping when clipped
35499         this.headers.setWidth(totalWidth);
35500         //this.innerCt.setWidth(totalWidth);
35501         this.innerCt.setStyle({ overflow: 'auto' });
35502         this.onResize(this.width, this.height);
35503              
35504         
35505     },
35506     onResize : function(w,h)
35507     {
35508         this.height = h;
35509         this.width = w;
35510         // resize cols..
35511         this.innerCt.setWidth(this.width);
35512         this.innerCt.setHeight(this.height-20);
35513         
35514         // headers...
35515         var cols = this.columns, c;
35516         var totalWidth = 0;
35517         var expEl = false;
35518         var len = cols.length;
35519         for(var i = 0; i < len; i++){
35520             c = cols[i];
35521             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35522                 // it's the expander..
35523                 expEl  = this.headEls[i];
35524                 continue;
35525             }
35526             totalWidth += c.width;
35527             
35528         }
35529         if (expEl) {
35530             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35531         }
35532         this.headers.setWidth(w-20);
35533
35534         
35535         
35536         
35537     }
35538 });
35539 /*
35540  * Based on:
35541  * Ext JS Library 1.1.1
35542  * Copyright(c) 2006-2007, Ext JS, LLC.
35543  *
35544  * Originally Released Under LGPL - original licence link has changed is not relivant.
35545  *
35546  * Fork - LGPL
35547  * <script type="text/javascript">
35548  */
35549  
35550 /**
35551  * @class Roo.menu.Menu
35552  * @extends Roo.util.Observable
35553  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35554  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35555  * @constructor
35556  * Creates a new Menu
35557  * @param {Object} config Configuration options
35558  */
35559 Roo.menu.Menu = function(config){
35560     Roo.apply(this, config);
35561     this.id = this.id || Roo.id();
35562     this.addEvents({
35563         /**
35564          * @event beforeshow
35565          * Fires before this menu is displayed
35566          * @param {Roo.menu.Menu} this
35567          */
35568         beforeshow : true,
35569         /**
35570          * @event beforehide
35571          * Fires before this menu is hidden
35572          * @param {Roo.menu.Menu} this
35573          */
35574         beforehide : true,
35575         /**
35576          * @event show
35577          * Fires after this menu is displayed
35578          * @param {Roo.menu.Menu} this
35579          */
35580         show : true,
35581         /**
35582          * @event hide
35583          * Fires after this menu is hidden
35584          * @param {Roo.menu.Menu} this
35585          */
35586         hide : true,
35587         /**
35588          * @event click
35589          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35590          * @param {Roo.menu.Menu} this
35591          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35592          * @param {Roo.EventObject} e
35593          */
35594         click : true,
35595         /**
35596          * @event mouseover
35597          * Fires when the mouse is hovering over this menu
35598          * @param {Roo.menu.Menu} this
35599          * @param {Roo.EventObject} e
35600          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35601          */
35602         mouseover : true,
35603         /**
35604          * @event mouseout
35605          * Fires when the mouse exits this menu
35606          * @param {Roo.menu.Menu} this
35607          * @param {Roo.EventObject} e
35608          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35609          */
35610         mouseout : true,
35611         /**
35612          * @event itemclick
35613          * Fires when a menu item contained in this menu is clicked
35614          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35615          * @param {Roo.EventObject} e
35616          */
35617         itemclick: true
35618     });
35619     if (this.registerMenu) {
35620         Roo.menu.MenuMgr.register(this);
35621     }
35622     
35623     var mis = this.items;
35624     this.items = new Roo.util.MixedCollection();
35625     if(mis){
35626         this.add.apply(this, mis);
35627     }
35628 };
35629
35630 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35631     /**
35632      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35633      */
35634     minWidth : 120,
35635     /**
35636      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35637      * for bottom-right shadow (defaults to "sides")
35638      */
35639     shadow : "sides",
35640     /**
35641      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35642      * this menu (defaults to "tl-tr?")
35643      */
35644     subMenuAlign : "tl-tr?",
35645     /**
35646      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35647      * relative to its element of origin (defaults to "tl-bl?")
35648      */
35649     defaultAlign : "tl-bl?",
35650     /**
35651      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35652      */
35653     allowOtherMenus : false,
35654     /**
35655      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35656      */
35657     registerMenu : true,
35658
35659     hidden:true,
35660
35661     // private
35662     render : function(){
35663         if(this.el){
35664             return;
35665         }
35666         var el = this.el = new Roo.Layer({
35667             cls: "x-menu",
35668             shadow:this.shadow,
35669             constrain: false,
35670             parentEl: this.parentEl || document.body,
35671             zindex:15000
35672         });
35673
35674         this.keyNav = new Roo.menu.MenuNav(this);
35675
35676         if(this.plain){
35677             el.addClass("x-menu-plain");
35678         }
35679         if(this.cls){
35680             el.addClass(this.cls);
35681         }
35682         // generic focus element
35683         this.focusEl = el.createChild({
35684             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35685         });
35686         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35687         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35688         
35689         ul.on("mouseover", this.onMouseOver, this);
35690         ul.on("mouseout", this.onMouseOut, this);
35691         this.items.each(function(item){
35692             if (item.hidden) {
35693                 return;
35694             }
35695             
35696             var li = document.createElement("li");
35697             li.className = "x-menu-list-item";
35698             ul.dom.appendChild(li);
35699             item.render(li, this);
35700         }, this);
35701         this.ul = ul;
35702         this.autoWidth();
35703     },
35704
35705     // private
35706     autoWidth : function(){
35707         var el = this.el, ul = this.ul;
35708         if(!el){
35709             return;
35710         }
35711         var w = this.width;
35712         if(w){
35713             el.setWidth(w);
35714         }else if(Roo.isIE){
35715             el.setWidth(this.minWidth);
35716             var t = el.dom.offsetWidth; // force recalc
35717             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35718         }
35719     },
35720
35721     // private
35722     delayAutoWidth : function(){
35723         if(this.rendered){
35724             if(!this.awTask){
35725                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35726             }
35727             this.awTask.delay(20);
35728         }
35729     },
35730
35731     // private
35732     findTargetItem : function(e){
35733         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35734         if(t && t.menuItemId){
35735             return this.items.get(t.menuItemId);
35736         }
35737     },
35738
35739     // private
35740     onClick : function(e){
35741         Roo.log("menu.onClick");
35742         var t = this.findTargetItem(e);
35743         if(!t){
35744             return;
35745         }
35746         Roo.log(e);
35747         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35748             if(t == this.activeItem && t.shouldDeactivate(e)){
35749                 this.activeItem.deactivate();
35750                 delete this.activeItem;
35751                 return;
35752             }
35753             if(t.canActivate){
35754                 this.setActiveItem(t, true);
35755             }
35756             return;
35757             
35758             
35759         }
35760         
35761         t.onClick(e);
35762         this.fireEvent("click", this, t, e);
35763     },
35764
35765     // private
35766     setActiveItem : function(item, autoExpand){
35767         if(item != this.activeItem){
35768             if(this.activeItem){
35769                 this.activeItem.deactivate();
35770             }
35771             this.activeItem = item;
35772             item.activate(autoExpand);
35773         }else if(autoExpand){
35774             item.expandMenu();
35775         }
35776     },
35777
35778     // private
35779     tryActivate : function(start, step){
35780         var items = this.items;
35781         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35782             var item = items.get(i);
35783             if(!item.disabled && item.canActivate){
35784                 this.setActiveItem(item, false);
35785                 return item;
35786             }
35787         }
35788         return false;
35789     },
35790
35791     // private
35792     onMouseOver : function(e){
35793         var t;
35794         if(t = this.findTargetItem(e)){
35795             if(t.canActivate && !t.disabled){
35796                 this.setActiveItem(t, true);
35797             }
35798         }
35799         this.fireEvent("mouseover", this, e, t);
35800     },
35801
35802     // private
35803     onMouseOut : function(e){
35804         var t;
35805         if(t = this.findTargetItem(e)){
35806             if(t == this.activeItem && t.shouldDeactivate(e)){
35807                 this.activeItem.deactivate();
35808                 delete this.activeItem;
35809             }
35810         }
35811         this.fireEvent("mouseout", this, e, t);
35812     },
35813
35814     /**
35815      * Read-only.  Returns true if the menu is currently displayed, else false.
35816      * @type Boolean
35817      */
35818     isVisible : function(){
35819         return this.el && !this.hidden;
35820     },
35821
35822     /**
35823      * Displays this menu relative to another element
35824      * @param {String/HTMLElement/Roo.Element} element The element to align to
35825      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35826      * the element (defaults to this.defaultAlign)
35827      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35828      */
35829     show : function(el, pos, parentMenu){
35830         this.parentMenu = parentMenu;
35831         if(!this.el){
35832             this.render();
35833         }
35834         this.fireEvent("beforeshow", this);
35835         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35836     },
35837
35838     /**
35839      * Displays this menu at a specific xy position
35840      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35841      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35842      */
35843     showAt : function(xy, parentMenu, /* private: */_e){
35844         this.parentMenu = parentMenu;
35845         if(!this.el){
35846             this.render();
35847         }
35848         if(_e !== false){
35849             this.fireEvent("beforeshow", this);
35850             xy = this.el.adjustForConstraints(xy);
35851         }
35852         this.el.setXY(xy);
35853         this.el.show();
35854         this.hidden = false;
35855         this.focus();
35856         this.fireEvent("show", this);
35857     },
35858
35859     focus : function(){
35860         if(!this.hidden){
35861             this.doFocus.defer(50, this);
35862         }
35863     },
35864
35865     doFocus : function(){
35866         if(!this.hidden){
35867             this.focusEl.focus();
35868         }
35869     },
35870
35871     /**
35872      * Hides this menu and optionally all parent menus
35873      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35874      */
35875     hide : function(deep){
35876         if(this.el && this.isVisible()){
35877             this.fireEvent("beforehide", this);
35878             if(this.activeItem){
35879                 this.activeItem.deactivate();
35880                 this.activeItem = null;
35881             }
35882             this.el.hide();
35883             this.hidden = true;
35884             this.fireEvent("hide", this);
35885         }
35886         if(deep === true && this.parentMenu){
35887             this.parentMenu.hide(true);
35888         }
35889     },
35890
35891     /**
35892      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35893      * Any of the following are valid:
35894      * <ul>
35895      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35896      * <li>An HTMLElement object which will be converted to a menu item</li>
35897      * <li>A menu item config object that will be created as a new menu item</li>
35898      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35899      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35900      * </ul>
35901      * Usage:
35902      * <pre><code>
35903 // Create the menu
35904 var menu = new Roo.menu.Menu();
35905
35906 // Create a menu item to add by reference
35907 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35908
35909 // Add a bunch of items at once using different methods.
35910 // Only the last item added will be returned.
35911 var item = menu.add(
35912     menuItem,                // add existing item by ref
35913     'Dynamic Item',          // new TextItem
35914     '-',                     // new separator
35915     { text: 'Config Item' }  // new item by config
35916 );
35917 </code></pre>
35918      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35919      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35920      */
35921     add : function(){
35922         var a = arguments, l = a.length, item;
35923         for(var i = 0; i < l; i++){
35924             var el = a[i];
35925             if ((typeof(el) == "object") && el.xtype && el.xns) {
35926                 el = Roo.factory(el, Roo.menu);
35927             }
35928             
35929             if(el.render){ // some kind of Item
35930                 item = this.addItem(el);
35931             }else if(typeof el == "string"){ // string
35932                 if(el == "separator" || el == "-"){
35933                     item = this.addSeparator();
35934                 }else{
35935                     item = this.addText(el);
35936                 }
35937             }else if(el.tagName || el.el){ // element
35938                 item = this.addElement(el);
35939             }else if(typeof el == "object"){ // must be menu item config?
35940                 item = this.addMenuItem(el);
35941             }
35942         }
35943         return item;
35944     },
35945
35946     /**
35947      * Returns this menu's underlying {@link Roo.Element} object
35948      * @return {Roo.Element} The element
35949      */
35950     getEl : function(){
35951         if(!this.el){
35952             this.render();
35953         }
35954         return this.el;
35955     },
35956
35957     /**
35958      * Adds a separator bar to the menu
35959      * @return {Roo.menu.Item} The menu item that was added
35960      */
35961     addSeparator : function(){
35962         return this.addItem(new Roo.menu.Separator());
35963     },
35964
35965     /**
35966      * Adds an {@link Roo.Element} object to the menu
35967      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35968      * @return {Roo.menu.Item} The menu item that was added
35969      */
35970     addElement : function(el){
35971         return this.addItem(new Roo.menu.BaseItem(el));
35972     },
35973
35974     /**
35975      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35976      * @param {Roo.menu.Item} item The menu item to add
35977      * @return {Roo.menu.Item} The menu item that was added
35978      */
35979     addItem : function(item){
35980         this.items.add(item);
35981         if(this.ul){
35982             var li = document.createElement("li");
35983             li.className = "x-menu-list-item";
35984             this.ul.dom.appendChild(li);
35985             item.render(li, this);
35986             this.delayAutoWidth();
35987         }
35988         return item;
35989     },
35990
35991     /**
35992      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35993      * @param {Object} config A MenuItem config object
35994      * @return {Roo.menu.Item} The menu item that was added
35995      */
35996     addMenuItem : function(config){
35997         if(!(config instanceof Roo.menu.Item)){
35998             if(typeof config.checked == "boolean"){ // must be check menu item config?
35999                 config = new Roo.menu.CheckItem(config);
36000             }else{
36001                 config = new Roo.menu.Item(config);
36002             }
36003         }
36004         return this.addItem(config);
36005     },
36006
36007     /**
36008      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36009      * @param {String} text The text to display in the menu item
36010      * @return {Roo.menu.Item} The menu item that was added
36011      */
36012     addText : function(text){
36013         return this.addItem(new Roo.menu.TextItem({ text : text }));
36014     },
36015
36016     /**
36017      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36018      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36019      * @param {Roo.menu.Item} item The menu item to add
36020      * @return {Roo.menu.Item} The menu item that was added
36021      */
36022     insert : function(index, item){
36023         this.items.insert(index, item);
36024         if(this.ul){
36025             var li = document.createElement("li");
36026             li.className = "x-menu-list-item";
36027             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36028             item.render(li, this);
36029             this.delayAutoWidth();
36030         }
36031         return item;
36032     },
36033
36034     /**
36035      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36036      * @param {Roo.menu.Item} item The menu item to remove
36037      */
36038     remove : function(item){
36039         this.items.removeKey(item.id);
36040         item.destroy();
36041     },
36042
36043     /**
36044      * Removes and destroys all items in the menu
36045      */
36046     removeAll : function(){
36047         var f;
36048         while(f = this.items.first()){
36049             this.remove(f);
36050         }
36051     }
36052 });
36053
36054 // MenuNav is a private utility class used internally by the Menu
36055 Roo.menu.MenuNav = function(menu){
36056     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36057     this.scope = this.menu = menu;
36058 };
36059
36060 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36061     doRelay : function(e, h){
36062         var k = e.getKey();
36063         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36064             this.menu.tryActivate(0, 1);
36065             return false;
36066         }
36067         return h.call(this.scope || this, e, this.menu);
36068     },
36069
36070     up : function(e, m){
36071         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36072             m.tryActivate(m.items.length-1, -1);
36073         }
36074     },
36075
36076     down : function(e, m){
36077         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36078             m.tryActivate(0, 1);
36079         }
36080     },
36081
36082     right : function(e, m){
36083         if(m.activeItem){
36084             m.activeItem.expandMenu(true);
36085         }
36086     },
36087
36088     left : function(e, m){
36089         m.hide();
36090         if(m.parentMenu && m.parentMenu.activeItem){
36091             m.parentMenu.activeItem.activate();
36092         }
36093     },
36094
36095     enter : function(e, m){
36096         if(m.activeItem){
36097             e.stopPropagation();
36098             m.activeItem.onClick(e);
36099             m.fireEvent("click", this, m.activeItem);
36100             return true;
36101         }
36102     }
36103 });/*
36104  * Based on:
36105  * Ext JS Library 1.1.1
36106  * Copyright(c) 2006-2007, Ext JS, LLC.
36107  *
36108  * Originally Released Under LGPL - original licence link has changed is not relivant.
36109  *
36110  * Fork - LGPL
36111  * <script type="text/javascript">
36112  */
36113  
36114 /**
36115  * @class Roo.menu.MenuMgr
36116  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36117  * @singleton
36118  */
36119 Roo.menu.MenuMgr = function(){
36120    var menus, active, groups = {}, attached = false, lastShow = new Date();
36121
36122    // private - called when first menu is created
36123    function init(){
36124        menus = {};
36125        active = new Roo.util.MixedCollection();
36126        Roo.get(document).addKeyListener(27, function(){
36127            if(active.length > 0){
36128                hideAll();
36129            }
36130        });
36131    }
36132
36133    // private
36134    function hideAll(){
36135        if(active && active.length > 0){
36136            var c = active.clone();
36137            c.each(function(m){
36138                m.hide();
36139            });
36140        }
36141    }
36142
36143    // private
36144    function onHide(m){
36145        active.remove(m);
36146        if(active.length < 1){
36147            Roo.get(document).un("mousedown", onMouseDown);
36148            attached = false;
36149        }
36150    }
36151
36152    // private
36153    function onShow(m){
36154        var last = active.last();
36155        lastShow = new Date();
36156        active.add(m);
36157        if(!attached){
36158            Roo.get(document).on("mousedown", onMouseDown);
36159            attached = true;
36160        }
36161        if(m.parentMenu){
36162           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36163           m.parentMenu.activeChild = m;
36164        }else if(last && last.isVisible()){
36165           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36166        }
36167    }
36168
36169    // private
36170    function onBeforeHide(m){
36171        if(m.activeChild){
36172            m.activeChild.hide();
36173        }
36174        if(m.autoHideTimer){
36175            clearTimeout(m.autoHideTimer);
36176            delete m.autoHideTimer;
36177        }
36178    }
36179
36180    // private
36181    function onBeforeShow(m){
36182        var pm = m.parentMenu;
36183        if(!pm && !m.allowOtherMenus){
36184            hideAll();
36185        }else if(pm && pm.activeChild && active != m){
36186            pm.activeChild.hide();
36187        }
36188    }
36189
36190    // private
36191    function onMouseDown(e){
36192        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36193            hideAll();
36194        }
36195    }
36196
36197    // private
36198    function onBeforeCheck(mi, state){
36199        if(state){
36200            var g = groups[mi.group];
36201            for(var i = 0, l = g.length; i < l; i++){
36202                if(g[i] != mi){
36203                    g[i].setChecked(false);
36204                }
36205            }
36206        }
36207    }
36208
36209    return {
36210
36211        /**
36212         * Hides all menus that are currently visible
36213         */
36214        hideAll : function(){
36215             hideAll();  
36216        },
36217
36218        // private
36219        register : function(menu){
36220            if(!menus){
36221                init();
36222            }
36223            menus[menu.id] = menu;
36224            menu.on("beforehide", onBeforeHide);
36225            menu.on("hide", onHide);
36226            menu.on("beforeshow", onBeforeShow);
36227            menu.on("show", onShow);
36228            var g = menu.group;
36229            if(g && menu.events["checkchange"]){
36230                if(!groups[g]){
36231                    groups[g] = [];
36232                }
36233                groups[g].push(menu);
36234                menu.on("checkchange", onCheck);
36235            }
36236        },
36237
36238         /**
36239          * Returns a {@link Roo.menu.Menu} object
36240          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36241          * be used to generate and return a new Menu instance.
36242          */
36243        get : function(menu){
36244            if(typeof menu == "string"){ // menu id
36245                return menus[menu];
36246            }else if(menu.events){  // menu instance
36247                return menu;
36248            }else if(typeof menu.length == 'number'){ // array of menu items?
36249                return new Roo.menu.Menu({items:menu});
36250            }else{ // otherwise, must be a config
36251                return new Roo.menu.Menu(menu);
36252            }
36253        },
36254
36255        // private
36256        unregister : function(menu){
36257            delete menus[menu.id];
36258            menu.un("beforehide", onBeforeHide);
36259            menu.un("hide", onHide);
36260            menu.un("beforeshow", onBeforeShow);
36261            menu.un("show", onShow);
36262            var g = menu.group;
36263            if(g && menu.events["checkchange"]){
36264                groups[g].remove(menu);
36265                menu.un("checkchange", onCheck);
36266            }
36267        },
36268
36269        // private
36270        registerCheckable : function(menuItem){
36271            var g = menuItem.group;
36272            if(g){
36273                if(!groups[g]){
36274                    groups[g] = [];
36275                }
36276                groups[g].push(menuItem);
36277                menuItem.on("beforecheckchange", onBeforeCheck);
36278            }
36279        },
36280
36281        // private
36282        unregisterCheckable : function(menuItem){
36283            var g = menuItem.group;
36284            if(g){
36285                groups[g].remove(menuItem);
36286                menuItem.un("beforecheckchange", onBeforeCheck);
36287            }
36288        }
36289    };
36290 }();/*
36291  * Based on:
36292  * Ext JS Library 1.1.1
36293  * Copyright(c) 2006-2007, Ext JS, LLC.
36294  *
36295  * Originally Released Under LGPL - original licence link has changed is not relivant.
36296  *
36297  * Fork - LGPL
36298  * <script type="text/javascript">
36299  */
36300  
36301
36302 /**
36303  * @class Roo.menu.BaseItem
36304  * @extends Roo.Component
36305  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36306  * management and base configuration options shared by all menu components.
36307  * @constructor
36308  * Creates a new BaseItem
36309  * @param {Object} config Configuration options
36310  */
36311 Roo.menu.BaseItem = function(config){
36312     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36313
36314     this.addEvents({
36315         /**
36316          * @event click
36317          * Fires when this item is clicked
36318          * @param {Roo.menu.BaseItem} this
36319          * @param {Roo.EventObject} e
36320          */
36321         click: true,
36322         /**
36323          * @event activate
36324          * Fires when this item is activated
36325          * @param {Roo.menu.BaseItem} this
36326          */
36327         activate : true,
36328         /**
36329          * @event deactivate
36330          * Fires when this item is deactivated
36331          * @param {Roo.menu.BaseItem} this
36332          */
36333         deactivate : true
36334     });
36335
36336     if(this.handler){
36337         this.on("click", this.handler, this.scope, true);
36338     }
36339 };
36340
36341 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36342     /**
36343      * @cfg {Function} handler
36344      * A function that will handle the click event of this menu item (defaults to undefined)
36345      */
36346     /**
36347      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36348      */
36349     canActivate : false,
36350     
36351      /**
36352      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36353      */
36354     hidden: false,
36355     
36356     /**
36357      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36358      */
36359     activeClass : "x-menu-item-active",
36360     /**
36361      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36362      */
36363     hideOnClick : true,
36364     /**
36365      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36366      */
36367     hideDelay : 100,
36368
36369     // private
36370     ctype: "Roo.menu.BaseItem",
36371
36372     // private
36373     actionMode : "container",
36374
36375     // private
36376     render : function(container, parentMenu){
36377         this.parentMenu = parentMenu;
36378         Roo.menu.BaseItem.superclass.render.call(this, container);
36379         this.container.menuItemId = this.id;
36380     },
36381
36382     // private
36383     onRender : function(container, position){
36384         this.el = Roo.get(this.el);
36385         container.dom.appendChild(this.el.dom);
36386     },
36387
36388     // private
36389     onClick : function(e){
36390         if(!this.disabled && this.fireEvent("click", this, e) !== false
36391                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36392             this.handleClick(e);
36393         }else{
36394             e.stopEvent();
36395         }
36396     },
36397
36398     // private
36399     activate : function(){
36400         if(this.disabled){
36401             return false;
36402         }
36403         var li = this.container;
36404         li.addClass(this.activeClass);
36405         this.region = li.getRegion().adjust(2, 2, -2, -2);
36406         this.fireEvent("activate", this);
36407         return true;
36408     },
36409
36410     // private
36411     deactivate : function(){
36412         this.container.removeClass(this.activeClass);
36413         this.fireEvent("deactivate", this);
36414     },
36415
36416     // private
36417     shouldDeactivate : function(e){
36418         return !this.region || !this.region.contains(e.getPoint());
36419     },
36420
36421     // private
36422     handleClick : function(e){
36423         if(this.hideOnClick){
36424             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36425         }
36426     },
36427
36428     // private
36429     expandMenu : function(autoActivate){
36430         // do nothing
36431     },
36432
36433     // private
36434     hideMenu : function(){
36435         // do nothing
36436     }
36437 });/*
36438  * Based on:
36439  * Ext JS Library 1.1.1
36440  * Copyright(c) 2006-2007, Ext JS, LLC.
36441  *
36442  * Originally Released Under LGPL - original licence link has changed is not relivant.
36443  *
36444  * Fork - LGPL
36445  * <script type="text/javascript">
36446  */
36447  
36448 /**
36449  * @class Roo.menu.Adapter
36450  * @extends Roo.menu.BaseItem
36451  * 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.
36452  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36453  * @constructor
36454  * Creates a new Adapter
36455  * @param {Object} config Configuration options
36456  */
36457 Roo.menu.Adapter = function(component, config){
36458     Roo.menu.Adapter.superclass.constructor.call(this, config);
36459     this.component = component;
36460 };
36461 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36462     // private
36463     canActivate : true,
36464
36465     // private
36466     onRender : function(container, position){
36467         this.component.render(container);
36468         this.el = this.component.getEl();
36469     },
36470
36471     // private
36472     activate : function(){
36473         if(this.disabled){
36474             return false;
36475         }
36476         this.component.focus();
36477         this.fireEvent("activate", this);
36478         return true;
36479     },
36480
36481     // private
36482     deactivate : function(){
36483         this.fireEvent("deactivate", this);
36484     },
36485
36486     // private
36487     disable : function(){
36488         this.component.disable();
36489         Roo.menu.Adapter.superclass.disable.call(this);
36490     },
36491
36492     // private
36493     enable : function(){
36494         this.component.enable();
36495         Roo.menu.Adapter.superclass.enable.call(this);
36496     }
36497 });/*
36498  * Based on:
36499  * Ext JS Library 1.1.1
36500  * Copyright(c) 2006-2007, Ext JS, LLC.
36501  *
36502  * Originally Released Under LGPL - original licence link has changed is not relivant.
36503  *
36504  * Fork - LGPL
36505  * <script type="text/javascript">
36506  */
36507
36508 /**
36509  * @class Roo.menu.TextItem
36510  * @extends Roo.menu.BaseItem
36511  * Adds a static text string to a menu, usually used as either a heading or group separator.
36512  * Note: old style constructor with text is still supported.
36513  * 
36514  * @constructor
36515  * Creates a new TextItem
36516  * @param {Object} cfg Configuration
36517  */
36518 Roo.menu.TextItem = function(cfg){
36519     if (typeof(cfg) == 'string') {
36520         this.text = cfg;
36521     } else {
36522         Roo.apply(this,cfg);
36523     }
36524     
36525     Roo.menu.TextItem.superclass.constructor.call(this);
36526 };
36527
36528 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36529     /**
36530      * @cfg {Boolean} text Text to show on item.
36531      */
36532     text : '',
36533     
36534     /**
36535      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36536      */
36537     hideOnClick : false,
36538     /**
36539      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36540      */
36541     itemCls : "x-menu-text",
36542
36543     // private
36544     onRender : function(){
36545         var s = document.createElement("span");
36546         s.className = this.itemCls;
36547         s.innerHTML = this.text;
36548         this.el = s;
36549         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36550     }
36551 });/*
36552  * Based on:
36553  * Ext JS Library 1.1.1
36554  * Copyright(c) 2006-2007, Ext JS, LLC.
36555  *
36556  * Originally Released Under LGPL - original licence link has changed is not relivant.
36557  *
36558  * Fork - LGPL
36559  * <script type="text/javascript">
36560  */
36561
36562 /**
36563  * @class Roo.menu.Separator
36564  * @extends Roo.menu.BaseItem
36565  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36566  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36567  * @constructor
36568  * @param {Object} config Configuration options
36569  */
36570 Roo.menu.Separator = function(config){
36571     Roo.menu.Separator.superclass.constructor.call(this, config);
36572 };
36573
36574 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36575     /**
36576      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36577      */
36578     itemCls : "x-menu-sep",
36579     /**
36580      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36581      */
36582     hideOnClick : false,
36583
36584     // private
36585     onRender : function(li){
36586         var s = document.createElement("span");
36587         s.className = this.itemCls;
36588         s.innerHTML = "&#160;";
36589         this.el = s;
36590         li.addClass("x-menu-sep-li");
36591         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36592     }
36593 });/*
36594  * Based on:
36595  * Ext JS Library 1.1.1
36596  * Copyright(c) 2006-2007, Ext JS, LLC.
36597  *
36598  * Originally Released Under LGPL - original licence link has changed is not relivant.
36599  *
36600  * Fork - LGPL
36601  * <script type="text/javascript">
36602  */
36603 /**
36604  * @class Roo.menu.Item
36605  * @extends Roo.menu.BaseItem
36606  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36607  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36608  * activation and click handling.
36609  * @constructor
36610  * Creates a new Item
36611  * @param {Object} config Configuration options
36612  */
36613 Roo.menu.Item = function(config){
36614     Roo.menu.Item.superclass.constructor.call(this, config);
36615     if(this.menu){
36616         this.menu = Roo.menu.MenuMgr.get(this.menu);
36617     }
36618 };
36619 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36620     
36621     /**
36622      * @cfg {String} text
36623      * The text to show on the menu item.
36624      */
36625     text: '',
36626      /**
36627      * @cfg {String} HTML to render in menu
36628      * The text to show on the menu item (HTML version).
36629      */
36630     html: '',
36631     /**
36632      * @cfg {String} icon
36633      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36634      */
36635     icon: undefined,
36636     /**
36637      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36638      */
36639     itemCls : "x-menu-item",
36640     /**
36641      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36642      */
36643     canActivate : true,
36644     /**
36645      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36646      */
36647     showDelay: 200,
36648     // doc'd in BaseItem
36649     hideDelay: 200,
36650
36651     // private
36652     ctype: "Roo.menu.Item",
36653     
36654     // private
36655     onRender : function(container, position){
36656         var el = document.createElement("a");
36657         el.hideFocus = true;
36658         el.unselectable = "on";
36659         el.href = this.href || "#";
36660         if(this.hrefTarget){
36661             el.target = this.hrefTarget;
36662         }
36663         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36664         
36665         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36666         
36667         el.innerHTML = String.format(
36668                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36669                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36670         this.el = el;
36671         Roo.menu.Item.superclass.onRender.call(this, container, position);
36672     },
36673
36674     /**
36675      * Sets the text to display in this menu item
36676      * @param {String} text The text to display
36677      * @param {Boolean} isHTML true to indicate text is pure html.
36678      */
36679     setText : function(text, isHTML){
36680         if (isHTML) {
36681             this.html = text;
36682         } else {
36683             this.text = text;
36684             this.html = '';
36685         }
36686         if(this.rendered){
36687             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36688      
36689             this.el.update(String.format(
36690                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36691                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36692             this.parentMenu.autoWidth();
36693         }
36694     },
36695
36696     // private
36697     handleClick : function(e){
36698         if(!this.href){ // if no link defined, stop the event automatically
36699             e.stopEvent();
36700         }
36701         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36702     },
36703
36704     // private
36705     activate : function(autoExpand){
36706         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36707             this.focus();
36708             if(autoExpand){
36709                 this.expandMenu();
36710             }
36711         }
36712         return true;
36713     },
36714
36715     // private
36716     shouldDeactivate : function(e){
36717         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36718             if(this.menu && this.menu.isVisible()){
36719                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36720             }
36721             return true;
36722         }
36723         return false;
36724     },
36725
36726     // private
36727     deactivate : function(){
36728         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36729         this.hideMenu();
36730     },
36731
36732     // private
36733     expandMenu : function(autoActivate){
36734         if(!this.disabled && this.menu){
36735             clearTimeout(this.hideTimer);
36736             delete this.hideTimer;
36737             if(!this.menu.isVisible() && !this.showTimer){
36738                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36739             }else if (this.menu.isVisible() && autoActivate){
36740                 this.menu.tryActivate(0, 1);
36741             }
36742         }
36743     },
36744
36745     // private
36746     deferExpand : function(autoActivate){
36747         delete this.showTimer;
36748         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36749         if(autoActivate){
36750             this.menu.tryActivate(0, 1);
36751         }
36752     },
36753
36754     // private
36755     hideMenu : function(){
36756         clearTimeout(this.showTimer);
36757         delete this.showTimer;
36758         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36759             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36760         }
36761     },
36762
36763     // private
36764     deferHide : function(){
36765         delete this.hideTimer;
36766         this.menu.hide();
36767     }
36768 });/*
36769  * Based on:
36770  * Ext JS Library 1.1.1
36771  * Copyright(c) 2006-2007, Ext JS, LLC.
36772  *
36773  * Originally Released Under LGPL - original licence link has changed is not relivant.
36774  *
36775  * Fork - LGPL
36776  * <script type="text/javascript">
36777  */
36778  
36779 /**
36780  * @class Roo.menu.CheckItem
36781  * @extends Roo.menu.Item
36782  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36783  * @constructor
36784  * Creates a new CheckItem
36785  * @param {Object} config Configuration options
36786  */
36787 Roo.menu.CheckItem = function(config){
36788     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36789     this.addEvents({
36790         /**
36791          * @event beforecheckchange
36792          * Fires before the checked value is set, providing an opportunity to cancel if needed
36793          * @param {Roo.menu.CheckItem} this
36794          * @param {Boolean} checked The new checked value that will be set
36795          */
36796         "beforecheckchange" : true,
36797         /**
36798          * @event checkchange
36799          * Fires after the checked value has been set
36800          * @param {Roo.menu.CheckItem} this
36801          * @param {Boolean} checked The checked value that was set
36802          */
36803         "checkchange" : true
36804     });
36805     if(this.checkHandler){
36806         this.on('checkchange', this.checkHandler, this.scope);
36807     }
36808 };
36809 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36810     /**
36811      * @cfg {String} group
36812      * All check items with the same group name will automatically be grouped into a single-select
36813      * radio button group (defaults to '')
36814      */
36815     /**
36816      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36817      */
36818     itemCls : "x-menu-item x-menu-check-item",
36819     /**
36820      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36821      */
36822     groupClass : "x-menu-group-item",
36823
36824     /**
36825      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36826      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36827      * initialized with checked = true will be rendered as checked.
36828      */
36829     checked: false,
36830
36831     // private
36832     ctype: "Roo.menu.CheckItem",
36833
36834     // private
36835     onRender : function(c){
36836         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36837         if(this.group){
36838             this.el.addClass(this.groupClass);
36839         }
36840         Roo.menu.MenuMgr.registerCheckable(this);
36841         if(this.checked){
36842             this.checked = false;
36843             this.setChecked(true, true);
36844         }
36845     },
36846
36847     // private
36848     destroy : function(){
36849         if(this.rendered){
36850             Roo.menu.MenuMgr.unregisterCheckable(this);
36851         }
36852         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36853     },
36854
36855     /**
36856      * Set the checked state of this item
36857      * @param {Boolean} checked The new checked value
36858      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36859      */
36860     setChecked : function(state, suppressEvent){
36861         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36862             if(this.container){
36863                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36864             }
36865             this.checked = state;
36866             if(suppressEvent !== true){
36867                 this.fireEvent("checkchange", this, state);
36868             }
36869         }
36870     },
36871
36872     // private
36873     handleClick : function(e){
36874        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36875            this.setChecked(!this.checked);
36876        }
36877        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36878     }
36879 });/*
36880  * Based on:
36881  * Ext JS Library 1.1.1
36882  * Copyright(c) 2006-2007, Ext JS, LLC.
36883  *
36884  * Originally Released Under LGPL - original licence link has changed is not relivant.
36885  *
36886  * Fork - LGPL
36887  * <script type="text/javascript">
36888  */
36889  
36890 /**
36891  * @class Roo.menu.DateItem
36892  * @extends Roo.menu.Adapter
36893  * A menu item that wraps the {@link Roo.DatPicker} component.
36894  * @constructor
36895  * Creates a new DateItem
36896  * @param {Object} config Configuration options
36897  */
36898 Roo.menu.DateItem = function(config){
36899     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36900     /** The Roo.DatePicker object @type Roo.DatePicker */
36901     this.picker = this.component;
36902     this.addEvents({select: true});
36903     
36904     this.picker.on("render", function(picker){
36905         picker.getEl().swallowEvent("click");
36906         picker.container.addClass("x-menu-date-item");
36907     });
36908
36909     this.picker.on("select", this.onSelect, this);
36910 };
36911
36912 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36913     // private
36914     onSelect : function(picker, date){
36915         this.fireEvent("select", this, date, picker);
36916         Roo.menu.DateItem.superclass.handleClick.call(this);
36917     }
36918 });/*
36919  * Based on:
36920  * Ext JS Library 1.1.1
36921  * Copyright(c) 2006-2007, Ext JS, LLC.
36922  *
36923  * Originally Released Under LGPL - original licence link has changed is not relivant.
36924  *
36925  * Fork - LGPL
36926  * <script type="text/javascript">
36927  */
36928  
36929 /**
36930  * @class Roo.menu.ColorItem
36931  * @extends Roo.menu.Adapter
36932  * A menu item that wraps the {@link Roo.ColorPalette} component.
36933  * @constructor
36934  * Creates a new ColorItem
36935  * @param {Object} config Configuration options
36936  */
36937 Roo.menu.ColorItem = function(config){
36938     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36939     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36940     this.palette = this.component;
36941     this.relayEvents(this.palette, ["select"]);
36942     if(this.selectHandler){
36943         this.on('select', this.selectHandler, this.scope);
36944     }
36945 };
36946 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36947  * Based on:
36948  * Ext JS Library 1.1.1
36949  * Copyright(c) 2006-2007, Ext JS, LLC.
36950  *
36951  * Originally Released Under LGPL - original licence link has changed is not relivant.
36952  *
36953  * Fork - LGPL
36954  * <script type="text/javascript">
36955  */
36956  
36957
36958 /**
36959  * @class Roo.menu.DateMenu
36960  * @extends Roo.menu.Menu
36961  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36962  * @constructor
36963  * Creates a new DateMenu
36964  * @param {Object} config Configuration options
36965  */
36966 Roo.menu.DateMenu = function(config){
36967     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36968     this.plain = true;
36969     var di = new Roo.menu.DateItem(config);
36970     this.add(di);
36971     /**
36972      * The {@link Roo.DatePicker} instance for this DateMenu
36973      * @type DatePicker
36974      */
36975     this.picker = di.picker;
36976     /**
36977      * @event select
36978      * @param {DatePicker} picker
36979      * @param {Date} date
36980      */
36981     this.relayEvents(di, ["select"]);
36982     this.on('beforeshow', function(){
36983         if(this.picker){
36984             this.picker.hideMonthPicker(false);
36985         }
36986     }, this);
36987 };
36988 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36989     cls:'x-date-menu'
36990 });/*
36991  * Based on:
36992  * Ext JS Library 1.1.1
36993  * Copyright(c) 2006-2007, Ext JS, LLC.
36994  *
36995  * Originally Released Under LGPL - original licence link has changed is not relivant.
36996  *
36997  * Fork - LGPL
36998  * <script type="text/javascript">
36999  */
37000  
37001
37002 /**
37003  * @class Roo.menu.ColorMenu
37004  * @extends Roo.menu.Menu
37005  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37006  * @constructor
37007  * Creates a new ColorMenu
37008  * @param {Object} config Configuration options
37009  */
37010 Roo.menu.ColorMenu = function(config){
37011     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37012     this.plain = true;
37013     var ci = new Roo.menu.ColorItem(config);
37014     this.add(ci);
37015     /**
37016      * The {@link Roo.ColorPalette} instance for this ColorMenu
37017      * @type ColorPalette
37018      */
37019     this.palette = ci.palette;
37020     /**
37021      * @event select
37022      * @param {ColorPalette} palette
37023      * @param {String} color
37024      */
37025     this.relayEvents(ci, ["select"]);
37026 };
37027 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37028  * Based on:
37029  * Ext JS Library 1.1.1
37030  * Copyright(c) 2006-2007, Ext JS, LLC.
37031  *
37032  * Originally Released Under LGPL - original licence link has changed is not relivant.
37033  *
37034  * Fork - LGPL
37035  * <script type="text/javascript">
37036  */
37037  
37038 /**
37039  * @class Roo.form.Field
37040  * @extends Roo.BoxComponent
37041  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37042  * @constructor
37043  * Creates a new Field
37044  * @param {Object} config Configuration options
37045  */
37046 Roo.form.Field = function(config){
37047     Roo.form.Field.superclass.constructor.call(this, config);
37048 };
37049
37050 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37051     /**
37052      * @cfg {String} fieldLabel Label to use when rendering a form.
37053      */
37054        /**
37055      * @cfg {String} qtip Mouse over tip
37056      */
37057      
37058     /**
37059      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37060      */
37061     invalidClass : "x-form-invalid",
37062     /**
37063      * @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")
37064      */
37065     invalidText : "The value in this field is invalid",
37066     /**
37067      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37068      */
37069     focusClass : "x-form-focus",
37070     /**
37071      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37072       automatic validation (defaults to "keyup").
37073      */
37074     validationEvent : "keyup",
37075     /**
37076      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37077      */
37078     validateOnBlur : true,
37079     /**
37080      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37081      */
37082     validationDelay : 250,
37083     /**
37084      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37085      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37086      */
37087     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37088     /**
37089      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37090      */
37091     fieldClass : "x-form-field",
37092     /**
37093      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37094      *<pre>
37095 Value         Description
37096 -----------   ----------------------------------------------------------------------
37097 qtip          Display a quick tip when the user hovers over the field
37098 title         Display a default browser title attribute popup
37099 under         Add a block div beneath the field containing the error text
37100 side          Add an error icon to the right of the field with a popup on hover
37101 [element id]  Add the error text directly to the innerHTML of the specified element
37102 </pre>
37103      */
37104     msgTarget : 'qtip',
37105     /**
37106      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37107      */
37108     msgFx : 'normal',
37109
37110     /**
37111      * @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.
37112      */
37113     readOnly : false,
37114
37115     /**
37116      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37117      */
37118     disabled : false,
37119
37120     /**
37121      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37122      */
37123     inputType : undefined,
37124     
37125     /**
37126      * @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).
37127          */
37128         tabIndex : undefined,
37129         
37130     // private
37131     isFormField : true,
37132
37133     // private
37134     hasFocus : false,
37135     /**
37136      * @property {Roo.Element} fieldEl
37137      * Element Containing the rendered Field (with label etc.)
37138      */
37139     /**
37140      * @cfg {Mixed} value A value to initialize this field with.
37141      */
37142     value : undefined,
37143
37144     /**
37145      * @cfg {String} name The field's HTML name attribute.
37146      */
37147     /**
37148      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37149      */
37150
37151         // private ??
37152         initComponent : function(){
37153         Roo.form.Field.superclass.initComponent.call(this);
37154         this.addEvents({
37155             /**
37156              * @event focus
37157              * Fires when this field receives input focus.
37158              * @param {Roo.form.Field} this
37159              */
37160             focus : true,
37161             /**
37162              * @event blur
37163              * Fires when this field loses input focus.
37164              * @param {Roo.form.Field} this
37165              */
37166             blur : true,
37167             /**
37168              * @event specialkey
37169              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37170              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37171              * @param {Roo.form.Field} this
37172              * @param {Roo.EventObject} e The event object
37173              */
37174             specialkey : true,
37175             /**
37176              * @event change
37177              * Fires just before the field blurs if the field value has changed.
37178              * @param {Roo.form.Field} this
37179              * @param {Mixed} newValue The new value
37180              * @param {Mixed} oldValue The original value
37181              */
37182             change : true,
37183             /**
37184              * @event invalid
37185              * Fires after the field has been marked as invalid.
37186              * @param {Roo.form.Field} this
37187              * @param {String} msg The validation message
37188              */
37189             invalid : true,
37190             /**
37191              * @event valid
37192              * Fires after the field has been validated with no errors.
37193              * @param {Roo.form.Field} this
37194              */
37195             valid : true,
37196              /**
37197              * @event keyup
37198              * Fires after the key up
37199              * @param {Roo.form.Field} this
37200              * @param {Roo.EventObject}  e The event Object
37201              */
37202             keyup : true
37203         });
37204     },
37205
37206     /**
37207      * Returns the name attribute of the field if available
37208      * @return {String} name The field name
37209      */
37210     getName: function(){
37211          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37212     },
37213
37214     // private
37215     onRender : function(ct, position){
37216         Roo.form.Field.superclass.onRender.call(this, ct, position);
37217         if(!this.el){
37218             var cfg = this.getAutoCreate();
37219             if(!cfg.name){
37220                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37221             }
37222             if (!cfg.name.length) {
37223                 delete cfg.name;
37224             }
37225             if(this.inputType){
37226                 cfg.type = this.inputType;
37227             }
37228             this.el = ct.createChild(cfg, position);
37229         }
37230         var type = this.el.dom.type;
37231         if(type){
37232             if(type == 'password'){
37233                 type = 'text';
37234             }
37235             this.el.addClass('x-form-'+type);
37236         }
37237         if(this.readOnly){
37238             this.el.dom.readOnly = true;
37239         }
37240         if(this.tabIndex !== undefined){
37241             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37242         }
37243
37244         this.el.addClass([this.fieldClass, this.cls]);
37245         this.initValue();
37246     },
37247
37248     /**
37249      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37250      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37251      * @return {Roo.form.Field} this
37252      */
37253     applyTo : function(target){
37254         this.allowDomMove = false;
37255         this.el = Roo.get(target);
37256         this.render(this.el.dom.parentNode);
37257         return this;
37258     },
37259
37260     // private
37261     initValue : function(){
37262         if(this.value !== undefined){
37263             this.setValue(this.value);
37264         }else if(this.el.dom.value.length > 0){
37265             this.setValue(this.el.dom.value);
37266         }
37267     },
37268
37269     /**
37270      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37271      */
37272     isDirty : function() {
37273         if(this.disabled) {
37274             return false;
37275         }
37276         return String(this.getValue()) !== String(this.originalValue);
37277     },
37278
37279     // private
37280     afterRender : function(){
37281         Roo.form.Field.superclass.afterRender.call(this);
37282         this.initEvents();
37283     },
37284
37285     // private
37286     fireKey : function(e){
37287         //Roo.log('field ' + e.getKey());
37288         if(e.isNavKeyPress()){
37289             this.fireEvent("specialkey", this, e);
37290         }
37291     },
37292
37293     /**
37294      * Resets the current field value to the originally loaded value and clears any validation messages
37295      */
37296     reset : function(){
37297         this.setValue(this.resetValue);
37298         this.clearInvalid();
37299     },
37300
37301     // private
37302     initEvents : function(){
37303         // safari killled keypress - so keydown is now used..
37304         this.el.on("keydown" , this.fireKey,  this);
37305         this.el.on("focus", this.onFocus,  this);
37306         this.el.on("blur", this.onBlur,  this);
37307         this.el.relayEvent('keyup', this);
37308
37309         // reference to original value for reset
37310         this.originalValue = this.getValue();
37311         this.resetValue =  this.getValue();
37312     },
37313
37314     // private
37315     onFocus : function(){
37316         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37317             this.el.addClass(this.focusClass);
37318         }
37319         if(!this.hasFocus){
37320             this.hasFocus = true;
37321             this.startValue = this.getValue();
37322             this.fireEvent("focus", this);
37323         }
37324     },
37325
37326     beforeBlur : Roo.emptyFn,
37327
37328     // private
37329     onBlur : function(){
37330         this.beforeBlur();
37331         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37332             this.el.removeClass(this.focusClass);
37333         }
37334         this.hasFocus = false;
37335         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37336             this.validate();
37337         }
37338         var v = this.getValue();
37339         if(String(v) !== String(this.startValue)){
37340             this.fireEvent('change', this, v, this.startValue);
37341         }
37342         this.fireEvent("blur", this);
37343     },
37344
37345     /**
37346      * Returns whether or not the field value is currently valid
37347      * @param {Boolean} preventMark True to disable marking the field invalid
37348      * @return {Boolean} True if the value is valid, else false
37349      */
37350     isValid : function(preventMark){
37351         if(this.disabled){
37352             return true;
37353         }
37354         var restore = this.preventMark;
37355         this.preventMark = preventMark === true;
37356         var v = this.validateValue(this.processValue(this.getRawValue()));
37357         this.preventMark = restore;
37358         return v;
37359     },
37360
37361     /**
37362      * Validates the field value
37363      * @return {Boolean} True if the value is valid, else false
37364      */
37365     validate : function(){
37366         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37367             this.clearInvalid();
37368             return true;
37369         }
37370         return false;
37371     },
37372
37373     processValue : function(value){
37374         return value;
37375     },
37376
37377     // private
37378     // Subclasses should provide the validation implementation by overriding this
37379     validateValue : function(value){
37380         return true;
37381     },
37382
37383     /**
37384      * Mark this field as invalid
37385      * @param {String} msg The validation message
37386      */
37387     markInvalid : function(msg){
37388         if(!this.rendered || this.preventMark){ // not rendered
37389             return;
37390         }
37391         
37392         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37393         
37394         obj.el.addClass(this.invalidClass);
37395         msg = msg || this.invalidText;
37396         switch(this.msgTarget){
37397             case 'qtip':
37398                 obj.el.dom.qtip = msg;
37399                 obj.el.dom.qclass = 'x-form-invalid-tip';
37400                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37401                     Roo.QuickTips.enable();
37402                 }
37403                 break;
37404             case 'title':
37405                 this.el.dom.title = msg;
37406                 break;
37407             case 'under':
37408                 if(!this.errorEl){
37409                     var elp = this.el.findParent('.x-form-element', 5, true);
37410                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37411                     this.errorEl.setWidth(elp.getWidth(true)-20);
37412                 }
37413                 this.errorEl.update(msg);
37414                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37415                 break;
37416             case 'side':
37417                 if(!this.errorIcon){
37418                     var elp = this.el.findParent('.x-form-element', 5, true);
37419                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37420                 }
37421                 this.alignErrorIcon();
37422                 this.errorIcon.dom.qtip = msg;
37423                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37424                 this.errorIcon.show();
37425                 this.on('resize', this.alignErrorIcon, this);
37426                 break;
37427             default:
37428                 var t = Roo.getDom(this.msgTarget);
37429                 t.innerHTML = msg;
37430                 t.style.display = this.msgDisplay;
37431                 break;
37432         }
37433         this.fireEvent('invalid', this, msg);
37434     },
37435
37436     // private
37437     alignErrorIcon : function(){
37438         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37439     },
37440
37441     /**
37442      * Clear any invalid styles/messages for this field
37443      */
37444     clearInvalid : function(){
37445         if(!this.rendered || this.preventMark){ // not rendered
37446             return;
37447         }
37448         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37449         
37450         obj.el.removeClass(this.invalidClass);
37451         switch(this.msgTarget){
37452             case 'qtip':
37453                 obj.el.dom.qtip = '';
37454                 break;
37455             case 'title':
37456                 this.el.dom.title = '';
37457                 break;
37458             case 'under':
37459                 if(this.errorEl){
37460                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37461                 }
37462                 break;
37463             case 'side':
37464                 if(this.errorIcon){
37465                     this.errorIcon.dom.qtip = '';
37466                     this.errorIcon.hide();
37467                     this.un('resize', this.alignErrorIcon, this);
37468                 }
37469                 break;
37470             default:
37471                 var t = Roo.getDom(this.msgTarget);
37472                 t.innerHTML = '';
37473                 t.style.display = 'none';
37474                 break;
37475         }
37476         this.fireEvent('valid', this);
37477     },
37478
37479     /**
37480      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37481      * @return {Mixed} value The field value
37482      */
37483     getRawValue : function(){
37484         var v = this.el.getValue();
37485         
37486         return v;
37487     },
37488
37489     /**
37490      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37491      * @return {Mixed} value The field value
37492      */
37493     getValue : function(){
37494         var v = this.el.getValue();
37495          
37496         return v;
37497     },
37498
37499     /**
37500      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37501      * @param {Mixed} value The value to set
37502      */
37503     setRawValue : function(v){
37504         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37505     },
37506
37507     /**
37508      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37509      * @param {Mixed} value The value to set
37510      */
37511     setValue : function(v){
37512         this.value = v;
37513         if(this.rendered){
37514             this.el.dom.value = (v === null || v === undefined ? '' : v);
37515              this.validate();
37516         }
37517     },
37518
37519     adjustSize : function(w, h){
37520         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37521         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37522         return s;
37523     },
37524
37525     adjustWidth : function(tag, w){
37526         tag = tag.toLowerCase();
37527         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37528             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37529                 if(tag == 'input'){
37530                     return w + 2;
37531                 }
37532                 if(tag == 'textarea'){
37533                     return w-2;
37534                 }
37535             }else if(Roo.isOpera){
37536                 if(tag == 'input'){
37537                     return w + 2;
37538                 }
37539                 if(tag == 'textarea'){
37540                     return w-2;
37541                 }
37542             }
37543         }
37544         return w;
37545     }
37546 });
37547
37548
37549 // anything other than normal should be considered experimental
37550 Roo.form.Field.msgFx = {
37551     normal : {
37552         show: function(msgEl, f){
37553             msgEl.setDisplayed('block');
37554         },
37555
37556         hide : function(msgEl, f){
37557             msgEl.setDisplayed(false).update('');
37558         }
37559     },
37560
37561     slide : {
37562         show: function(msgEl, f){
37563             msgEl.slideIn('t', {stopFx:true});
37564         },
37565
37566         hide : function(msgEl, f){
37567             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37568         }
37569     },
37570
37571     slideRight : {
37572         show: function(msgEl, f){
37573             msgEl.fixDisplay();
37574             msgEl.alignTo(f.el, 'tl-tr');
37575             msgEl.slideIn('l', {stopFx:true});
37576         },
37577
37578         hide : function(msgEl, f){
37579             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37580         }
37581     }
37582 };/*
37583  * Based on:
37584  * Ext JS Library 1.1.1
37585  * Copyright(c) 2006-2007, Ext JS, LLC.
37586  *
37587  * Originally Released Under LGPL - original licence link has changed is not relivant.
37588  *
37589  * Fork - LGPL
37590  * <script type="text/javascript">
37591  */
37592  
37593
37594 /**
37595  * @class Roo.form.TextField
37596  * @extends Roo.form.Field
37597  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37598  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37599  * @constructor
37600  * Creates a new TextField
37601  * @param {Object} config Configuration options
37602  */
37603 Roo.form.TextField = function(config){
37604     Roo.form.TextField.superclass.constructor.call(this, config);
37605     this.addEvents({
37606         /**
37607          * @event autosize
37608          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37609          * according to the default logic, but this event provides a hook for the developer to apply additional
37610          * logic at runtime to resize the field if needed.
37611              * @param {Roo.form.Field} this This text field
37612              * @param {Number} width The new field width
37613              */
37614         autosize : true
37615     });
37616 };
37617
37618 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37619     /**
37620      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37621      */
37622     grow : false,
37623     /**
37624      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37625      */
37626     growMin : 30,
37627     /**
37628      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37629      */
37630     growMax : 800,
37631     /**
37632      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37633      */
37634     vtype : null,
37635     /**
37636      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37637      */
37638     maskRe : null,
37639     /**
37640      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37641      */
37642     disableKeyFilter : false,
37643     /**
37644      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37645      */
37646     allowBlank : true,
37647     /**
37648      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37649      */
37650     minLength : 0,
37651     /**
37652      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37653      */
37654     maxLength : Number.MAX_VALUE,
37655     /**
37656      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37657      */
37658     minLengthText : "The minimum length for this field is {0}",
37659     /**
37660      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37661      */
37662     maxLengthText : "The maximum length for this field is {0}",
37663     /**
37664      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37665      */
37666     selectOnFocus : false,
37667     /**
37668      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37669      */
37670     blankText : "This field is required",
37671     /**
37672      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37673      * If available, this function will be called only after the basic validators all return true, and will be passed the
37674      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37675      */
37676     validator : null,
37677     /**
37678      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37679      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37680      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37681      */
37682     regex : null,
37683     /**
37684      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37685      */
37686     regexText : "",
37687     /**
37688      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37689      */
37690     emptyText : null,
37691    
37692
37693     // private
37694     initEvents : function()
37695     {
37696         if (this.emptyText) {
37697             this.el.attr('placeholder', this.emptyText);
37698         }
37699         
37700         Roo.form.TextField.superclass.initEvents.call(this);
37701         if(this.validationEvent == 'keyup'){
37702             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37703             this.el.on('keyup', this.filterValidation, this);
37704         }
37705         else if(this.validationEvent !== false){
37706             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37707         }
37708         
37709         if(this.selectOnFocus){
37710             this.on("focus", this.preFocus, this);
37711             
37712         }
37713         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37714             this.el.on("keypress", this.filterKeys, this);
37715         }
37716         if(this.grow){
37717             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37718             this.el.on("click", this.autoSize,  this);
37719         }
37720         if(this.el.is('input[type=password]') && Roo.isSafari){
37721             this.el.on('keydown', this.SafariOnKeyDown, this);
37722         }
37723     },
37724
37725     processValue : function(value){
37726         if(this.stripCharsRe){
37727             var newValue = value.replace(this.stripCharsRe, '');
37728             if(newValue !== value){
37729                 this.setRawValue(newValue);
37730                 return newValue;
37731             }
37732         }
37733         return value;
37734     },
37735
37736     filterValidation : function(e){
37737         if(!e.isNavKeyPress()){
37738             this.validationTask.delay(this.validationDelay);
37739         }
37740     },
37741
37742     // private
37743     onKeyUp : function(e){
37744         if(!e.isNavKeyPress()){
37745             this.autoSize();
37746         }
37747     },
37748
37749     /**
37750      * Resets the current field value to the originally-loaded value and clears any validation messages.
37751      *  
37752      */
37753     reset : function(){
37754         Roo.form.TextField.superclass.reset.call(this);
37755        
37756     },
37757
37758     
37759     // private
37760     preFocus : function(){
37761         
37762         if(this.selectOnFocus){
37763             this.el.dom.select();
37764         }
37765     },
37766
37767     
37768     // private
37769     filterKeys : function(e){
37770         var k = e.getKey();
37771         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37772             return;
37773         }
37774         var c = e.getCharCode(), cc = String.fromCharCode(c);
37775         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37776             return;
37777         }
37778         if(!this.maskRe.test(cc)){
37779             e.stopEvent();
37780         }
37781     },
37782
37783     setValue : function(v){
37784         
37785         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37786         
37787         this.autoSize();
37788     },
37789
37790     /**
37791      * Validates a value according to the field's validation rules and marks the field as invalid
37792      * if the validation fails
37793      * @param {Mixed} value The value to validate
37794      * @return {Boolean} True if the value is valid, else false
37795      */
37796     validateValue : function(value){
37797         if(value.length < 1)  { // if it's blank
37798              if(this.allowBlank){
37799                 this.clearInvalid();
37800                 return true;
37801              }else{
37802                 this.markInvalid(this.blankText);
37803                 return false;
37804              }
37805         }
37806         if(value.length < this.minLength){
37807             this.markInvalid(String.format(this.minLengthText, this.minLength));
37808             return false;
37809         }
37810         if(value.length > this.maxLength){
37811             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37812             return false;
37813         }
37814         if(this.vtype){
37815             var vt = Roo.form.VTypes;
37816             if(!vt[this.vtype](value, this)){
37817                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37818                 return false;
37819             }
37820         }
37821         if(typeof this.validator == "function"){
37822             var msg = this.validator(value);
37823             if(msg !== true){
37824                 this.markInvalid(msg);
37825                 return false;
37826             }
37827         }
37828         if(this.regex && !this.regex.test(value)){
37829             this.markInvalid(this.regexText);
37830             return false;
37831         }
37832         return true;
37833     },
37834
37835     /**
37836      * Selects text in this field
37837      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37838      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37839      */
37840     selectText : function(start, end){
37841         var v = this.getRawValue();
37842         if(v.length > 0){
37843             start = start === undefined ? 0 : start;
37844             end = end === undefined ? v.length : end;
37845             var d = this.el.dom;
37846             if(d.setSelectionRange){
37847                 d.setSelectionRange(start, end);
37848             }else if(d.createTextRange){
37849                 var range = d.createTextRange();
37850                 range.moveStart("character", start);
37851                 range.moveEnd("character", v.length-end);
37852                 range.select();
37853             }
37854         }
37855     },
37856
37857     /**
37858      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37859      * This only takes effect if grow = true, and fires the autosize event.
37860      */
37861     autoSize : function(){
37862         if(!this.grow || !this.rendered){
37863             return;
37864         }
37865         if(!this.metrics){
37866             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37867         }
37868         var el = this.el;
37869         var v = el.dom.value;
37870         var d = document.createElement('div');
37871         d.appendChild(document.createTextNode(v));
37872         v = d.innerHTML;
37873         d = null;
37874         v += "&#160;";
37875         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37876         this.el.setWidth(w);
37877         this.fireEvent("autosize", this, w);
37878     },
37879     
37880     // private
37881     SafariOnKeyDown : function(event)
37882     {
37883         // this is a workaround for a password hang bug on chrome/ webkit.
37884         
37885         var isSelectAll = false;
37886         
37887         if(this.el.dom.selectionEnd > 0){
37888             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37889         }
37890         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37891             event.preventDefault();
37892             this.setValue('');
37893             return;
37894         }
37895         
37896         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37897             
37898             event.preventDefault();
37899             // this is very hacky as keydown always get's upper case.
37900             
37901             var cc = String.fromCharCode(event.getCharCode());
37902             
37903             
37904             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37905             
37906         }
37907         
37908         
37909     }
37910 });/*
37911  * Based on:
37912  * Ext JS Library 1.1.1
37913  * Copyright(c) 2006-2007, Ext JS, LLC.
37914  *
37915  * Originally Released Under LGPL - original licence link has changed is not relivant.
37916  *
37917  * Fork - LGPL
37918  * <script type="text/javascript">
37919  */
37920  
37921 /**
37922  * @class Roo.form.Hidden
37923  * @extends Roo.form.TextField
37924  * Simple Hidden element used on forms 
37925  * 
37926  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37927  * 
37928  * @constructor
37929  * Creates a new Hidden form element.
37930  * @param {Object} config Configuration options
37931  */
37932
37933
37934
37935 // easy hidden field...
37936 Roo.form.Hidden = function(config){
37937     Roo.form.Hidden.superclass.constructor.call(this, config);
37938 };
37939   
37940 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37941     fieldLabel:      '',
37942     inputType:      'hidden',
37943     width:          50,
37944     allowBlank:     true,
37945     labelSeparator: '',
37946     hidden:         true,
37947     itemCls :       'x-form-item-display-none'
37948
37949
37950 });
37951
37952
37953 /*
37954  * Based on:
37955  * Ext JS Library 1.1.1
37956  * Copyright(c) 2006-2007, Ext JS, LLC.
37957  *
37958  * Originally Released Under LGPL - original licence link has changed is not relivant.
37959  *
37960  * Fork - LGPL
37961  * <script type="text/javascript">
37962  */
37963  
37964 /**
37965  * @class Roo.form.TriggerField
37966  * @extends Roo.form.TextField
37967  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37968  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37969  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37970  * for which you can provide a custom implementation.  For example:
37971  * <pre><code>
37972 var trigger = new Roo.form.TriggerField();
37973 trigger.onTriggerClick = myTriggerFn;
37974 trigger.applyTo('my-field');
37975 </code></pre>
37976  *
37977  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37978  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37979  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37980  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37981  * @constructor
37982  * Create a new TriggerField.
37983  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37984  * to the base TextField)
37985  */
37986 Roo.form.TriggerField = function(config){
37987     this.mimicing = false;
37988     Roo.form.TriggerField.superclass.constructor.call(this, config);
37989 };
37990
37991 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37992     /**
37993      * @cfg {String} triggerClass A CSS class to apply to the trigger
37994      */
37995     /**
37996      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37997      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37998      */
37999     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38000     /**
38001      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38002      */
38003     hideTrigger:false,
38004
38005     /** @cfg {Boolean} grow @hide */
38006     /** @cfg {Number} growMin @hide */
38007     /** @cfg {Number} growMax @hide */
38008
38009     /**
38010      * @hide 
38011      * @method
38012      */
38013     autoSize: Roo.emptyFn,
38014     // private
38015     monitorTab : true,
38016     // private
38017     deferHeight : true,
38018
38019     
38020     actionMode : 'wrap',
38021     // private
38022     onResize : function(w, h){
38023         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38024         if(typeof w == 'number'){
38025             var x = w - this.trigger.getWidth();
38026             this.el.setWidth(this.adjustWidth('input', x));
38027             this.trigger.setStyle('left', x+'px');
38028         }
38029     },
38030
38031     // private
38032     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38033
38034     // private
38035     getResizeEl : function(){
38036         return this.wrap;
38037     },
38038
38039     // private
38040     getPositionEl : function(){
38041         return this.wrap;
38042     },
38043
38044     // private
38045     alignErrorIcon : function(){
38046         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38047     },
38048
38049     // private
38050     onRender : function(ct, position){
38051         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38052         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38053         this.trigger = this.wrap.createChild(this.triggerConfig ||
38054                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38055         if(this.hideTrigger){
38056             this.trigger.setDisplayed(false);
38057         }
38058         this.initTrigger();
38059         if(!this.width){
38060             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38061         }
38062     },
38063
38064     // private
38065     initTrigger : function(){
38066         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38067         this.trigger.addClassOnOver('x-form-trigger-over');
38068         this.trigger.addClassOnClick('x-form-trigger-click');
38069     },
38070
38071     // private
38072     onDestroy : function(){
38073         if(this.trigger){
38074             this.trigger.removeAllListeners();
38075             this.trigger.remove();
38076         }
38077         if(this.wrap){
38078             this.wrap.remove();
38079         }
38080         Roo.form.TriggerField.superclass.onDestroy.call(this);
38081     },
38082
38083     // private
38084     onFocus : function(){
38085         Roo.form.TriggerField.superclass.onFocus.call(this);
38086         if(!this.mimicing){
38087             this.wrap.addClass('x-trigger-wrap-focus');
38088             this.mimicing = true;
38089             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38090             if(this.monitorTab){
38091                 this.el.on("keydown", this.checkTab, this);
38092             }
38093         }
38094     },
38095
38096     // private
38097     checkTab : function(e){
38098         if(e.getKey() == e.TAB){
38099             this.triggerBlur();
38100         }
38101     },
38102
38103     // private
38104     onBlur : function(){
38105         // do nothing
38106     },
38107
38108     // private
38109     mimicBlur : function(e, t){
38110         if(!this.wrap.contains(t) && this.validateBlur()){
38111             this.triggerBlur();
38112         }
38113     },
38114
38115     // private
38116     triggerBlur : function(){
38117         this.mimicing = false;
38118         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38119         if(this.monitorTab){
38120             this.el.un("keydown", this.checkTab, this);
38121         }
38122         this.wrap.removeClass('x-trigger-wrap-focus');
38123         Roo.form.TriggerField.superclass.onBlur.call(this);
38124     },
38125
38126     // private
38127     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38128     validateBlur : function(e, t){
38129         return true;
38130     },
38131
38132     // private
38133     onDisable : function(){
38134         Roo.form.TriggerField.superclass.onDisable.call(this);
38135         if(this.wrap){
38136             this.wrap.addClass('x-item-disabled');
38137         }
38138     },
38139
38140     // private
38141     onEnable : function(){
38142         Roo.form.TriggerField.superclass.onEnable.call(this);
38143         if(this.wrap){
38144             this.wrap.removeClass('x-item-disabled');
38145         }
38146     },
38147
38148     // private
38149     onShow : function(){
38150         var ae = this.getActionEl();
38151         
38152         if(ae){
38153             ae.dom.style.display = '';
38154             ae.dom.style.visibility = 'visible';
38155         }
38156     },
38157
38158     // private
38159     
38160     onHide : function(){
38161         var ae = this.getActionEl();
38162         ae.dom.style.display = 'none';
38163     },
38164
38165     /**
38166      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38167      * by an implementing function.
38168      * @method
38169      * @param {EventObject} e
38170      */
38171     onTriggerClick : Roo.emptyFn
38172 });
38173
38174 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38175 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38176 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38177 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38178     initComponent : function(){
38179         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38180
38181         this.triggerConfig = {
38182             tag:'span', cls:'x-form-twin-triggers', cn:[
38183             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38184             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38185         ]};
38186     },
38187
38188     getTrigger : function(index){
38189         return this.triggers[index];
38190     },
38191
38192     initTrigger : function(){
38193         var ts = this.trigger.select('.x-form-trigger', true);
38194         this.wrap.setStyle('overflow', 'hidden');
38195         var triggerField = this;
38196         ts.each(function(t, all, index){
38197             t.hide = function(){
38198                 var w = triggerField.wrap.getWidth();
38199                 this.dom.style.display = 'none';
38200                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38201             };
38202             t.show = function(){
38203                 var w = triggerField.wrap.getWidth();
38204                 this.dom.style.display = '';
38205                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38206             };
38207             var triggerIndex = 'Trigger'+(index+1);
38208
38209             if(this['hide'+triggerIndex]){
38210                 t.dom.style.display = 'none';
38211             }
38212             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38213             t.addClassOnOver('x-form-trigger-over');
38214             t.addClassOnClick('x-form-trigger-click');
38215         }, this);
38216         this.triggers = ts.elements;
38217     },
38218
38219     onTrigger1Click : Roo.emptyFn,
38220     onTrigger2Click : Roo.emptyFn
38221 });/*
38222  * Based on:
38223  * Ext JS Library 1.1.1
38224  * Copyright(c) 2006-2007, Ext JS, LLC.
38225  *
38226  * Originally Released Under LGPL - original licence link has changed is not relivant.
38227  *
38228  * Fork - LGPL
38229  * <script type="text/javascript">
38230  */
38231  
38232 /**
38233  * @class Roo.form.TextArea
38234  * @extends Roo.form.TextField
38235  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38236  * support for auto-sizing.
38237  * @constructor
38238  * Creates a new TextArea
38239  * @param {Object} config Configuration options
38240  */
38241 Roo.form.TextArea = function(config){
38242     Roo.form.TextArea.superclass.constructor.call(this, config);
38243     // these are provided exchanges for backwards compat
38244     // minHeight/maxHeight were replaced by growMin/growMax to be
38245     // compatible with TextField growing config values
38246     if(this.minHeight !== undefined){
38247         this.growMin = this.minHeight;
38248     }
38249     if(this.maxHeight !== undefined){
38250         this.growMax = this.maxHeight;
38251     }
38252 };
38253
38254 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38255     /**
38256      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38257      */
38258     growMin : 60,
38259     /**
38260      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38261      */
38262     growMax: 1000,
38263     /**
38264      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38265      * in the field (equivalent to setting overflow: hidden, defaults to false)
38266      */
38267     preventScrollbars: false,
38268     /**
38269      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38270      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38271      */
38272
38273     // private
38274     onRender : function(ct, position){
38275         if(!this.el){
38276             this.defaultAutoCreate = {
38277                 tag: "textarea",
38278                 style:"width:300px;height:60px;",
38279                 autocomplete: "new-password"
38280             };
38281         }
38282         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38283         if(this.grow){
38284             this.textSizeEl = Roo.DomHelper.append(document.body, {
38285                 tag: "pre", cls: "x-form-grow-sizer"
38286             });
38287             if(this.preventScrollbars){
38288                 this.el.setStyle("overflow", "hidden");
38289             }
38290             this.el.setHeight(this.growMin);
38291         }
38292     },
38293
38294     onDestroy : function(){
38295         if(this.textSizeEl){
38296             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38297         }
38298         Roo.form.TextArea.superclass.onDestroy.call(this);
38299     },
38300
38301     // private
38302     onKeyUp : function(e){
38303         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38304             this.autoSize();
38305         }
38306     },
38307
38308     /**
38309      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38310      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38311      */
38312     autoSize : function(){
38313         if(!this.grow || !this.textSizeEl){
38314             return;
38315         }
38316         var el = this.el;
38317         var v = el.dom.value;
38318         var ts = this.textSizeEl;
38319
38320         ts.innerHTML = '';
38321         ts.appendChild(document.createTextNode(v));
38322         v = ts.innerHTML;
38323
38324         Roo.fly(ts).setWidth(this.el.getWidth());
38325         if(v.length < 1){
38326             v = "&#160;&#160;";
38327         }else{
38328             if(Roo.isIE){
38329                 v = v.replace(/\n/g, '<p>&#160;</p>');
38330             }
38331             v += "&#160;\n&#160;";
38332         }
38333         ts.innerHTML = v;
38334         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38335         if(h != this.lastHeight){
38336             this.lastHeight = h;
38337             this.el.setHeight(h);
38338             this.fireEvent("autosize", this, h);
38339         }
38340     }
38341 });/*
38342  * Based on:
38343  * Ext JS Library 1.1.1
38344  * Copyright(c) 2006-2007, Ext JS, LLC.
38345  *
38346  * Originally Released Under LGPL - original licence link has changed is not relivant.
38347  *
38348  * Fork - LGPL
38349  * <script type="text/javascript">
38350  */
38351  
38352
38353 /**
38354  * @class Roo.form.NumberField
38355  * @extends Roo.form.TextField
38356  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38357  * @constructor
38358  * Creates a new NumberField
38359  * @param {Object} config Configuration options
38360  */
38361 Roo.form.NumberField = function(config){
38362     Roo.form.NumberField.superclass.constructor.call(this, config);
38363 };
38364
38365 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38366     /**
38367      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38368      */
38369     fieldClass: "x-form-field x-form-num-field",
38370     /**
38371      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38372      */
38373     allowDecimals : true,
38374     /**
38375      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38376      */
38377     decimalSeparator : ".",
38378     /**
38379      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38380      */
38381     decimalPrecision : 2,
38382     /**
38383      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38384      */
38385     allowNegative : true,
38386     /**
38387      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38388      */
38389     minValue : Number.NEGATIVE_INFINITY,
38390     /**
38391      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38392      */
38393     maxValue : Number.MAX_VALUE,
38394     /**
38395      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38396      */
38397     minText : "The minimum value for this field is {0}",
38398     /**
38399      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38400      */
38401     maxText : "The maximum value for this field is {0}",
38402     /**
38403      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38404      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38405      */
38406     nanText : "{0} is not a valid number",
38407
38408     // private
38409     initEvents : function(){
38410         Roo.form.NumberField.superclass.initEvents.call(this);
38411         var allowed = "0123456789";
38412         if(this.allowDecimals){
38413             allowed += this.decimalSeparator;
38414         }
38415         if(this.allowNegative){
38416             allowed += "-";
38417         }
38418         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38419         var keyPress = function(e){
38420             var k = e.getKey();
38421             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38422                 return;
38423             }
38424             var c = e.getCharCode();
38425             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38426                 e.stopEvent();
38427             }
38428         };
38429         this.el.on("keypress", keyPress, this);
38430     },
38431
38432     // private
38433     validateValue : function(value){
38434         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38435             return false;
38436         }
38437         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38438              return true;
38439         }
38440         var num = this.parseValue(value);
38441         if(isNaN(num)){
38442             this.markInvalid(String.format(this.nanText, value));
38443             return false;
38444         }
38445         if(num < this.minValue){
38446             this.markInvalid(String.format(this.minText, this.minValue));
38447             return false;
38448         }
38449         if(num > this.maxValue){
38450             this.markInvalid(String.format(this.maxText, this.maxValue));
38451             return false;
38452         }
38453         return true;
38454     },
38455
38456     getValue : function(){
38457         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38458     },
38459
38460     // private
38461     parseValue : function(value){
38462         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38463         return isNaN(value) ? '' : value;
38464     },
38465
38466     // private
38467     fixPrecision : function(value){
38468         var nan = isNaN(value);
38469         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38470             return nan ? '' : value;
38471         }
38472         return parseFloat(value).toFixed(this.decimalPrecision);
38473     },
38474
38475     setValue : function(v){
38476         v = this.fixPrecision(v);
38477         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38478     },
38479
38480     // private
38481     decimalPrecisionFcn : function(v){
38482         return Math.floor(v);
38483     },
38484
38485     beforeBlur : function(){
38486         var v = this.parseValue(this.getRawValue());
38487         if(v){
38488             this.setValue(v);
38489         }
38490     }
38491 });/*
38492  * Based on:
38493  * Ext JS Library 1.1.1
38494  * Copyright(c) 2006-2007, Ext JS, LLC.
38495  *
38496  * Originally Released Under LGPL - original licence link has changed is not relivant.
38497  *
38498  * Fork - LGPL
38499  * <script type="text/javascript">
38500  */
38501  
38502 /**
38503  * @class Roo.form.DateField
38504  * @extends Roo.form.TriggerField
38505  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38506 * @constructor
38507 * Create a new DateField
38508 * @param {Object} config
38509  */
38510 Roo.form.DateField = function(config){
38511     Roo.form.DateField.superclass.constructor.call(this, config);
38512     
38513       this.addEvents({
38514          
38515         /**
38516          * @event select
38517          * Fires when a date is selected
38518              * @param {Roo.form.DateField} combo This combo box
38519              * @param {Date} date The date selected
38520              */
38521         'select' : true
38522          
38523     });
38524     
38525     
38526     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38527     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38528     this.ddMatch = null;
38529     if(this.disabledDates){
38530         var dd = this.disabledDates;
38531         var re = "(?:";
38532         for(var i = 0; i < dd.length; i++){
38533             re += dd[i];
38534             if(i != dd.length-1) re += "|";
38535         }
38536         this.ddMatch = new RegExp(re + ")");
38537     }
38538 };
38539
38540 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38541     /**
38542      * @cfg {String} format
38543      * The default date format string which can be overriden for localization support.  The format must be
38544      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38545      */
38546     format : "m/d/y",
38547     /**
38548      * @cfg {String} altFormats
38549      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38550      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38551      */
38552     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38553     /**
38554      * @cfg {Array} disabledDays
38555      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38556      */
38557     disabledDays : null,
38558     /**
38559      * @cfg {String} disabledDaysText
38560      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38561      */
38562     disabledDaysText : "Disabled",
38563     /**
38564      * @cfg {Array} disabledDates
38565      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38566      * expression so they are very powerful. Some examples:
38567      * <ul>
38568      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38569      * <li>["03/08", "09/16"] would disable those days for every year</li>
38570      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38571      * <li>["03/../2006"] would disable every day in March 2006</li>
38572      * <li>["^03"] would disable every day in every March</li>
38573      * </ul>
38574      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38575      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38576      */
38577     disabledDates : null,
38578     /**
38579      * @cfg {String} disabledDatesText
38580      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38581      */
38582     disabledDatesText : "Disabled",
38583     /**
38584      * @cfg {Date/String} minValue
38585      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38586      * valid format (defaults to null).
38587      */
38588     minValue : null,
38589     /**
38590      * @cfg {Date/String} maxValue
38591      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38592      * valid format (defaults to null).
38593      */
38594     maxValue : null,
38595     /**
38596      * @cfg {String} minText
38597      * The error text to display when the date in the cell is before minValue (defaults to
38598      * 'The date in this field must be after {minValue}').
38599      */
38600     minText : "The date in this field must be equal to or after {0}",
38601     /**
38602      * @cfg {String} maxText
38603      * The error text to display when the date in the cell is after maxValue (defaults to
38604      * 'The date in this field must be before {maxValue}').
38605      */
38606     maxText : "The date in this field must be equal to or before {0}",
38607     /**
38608      * @cfg {String} invalidText
38609      * The error text to display when the date in the field is invalid (defaults to
38610      * '{value} is not a valid date - it must be in the format {format}').
38611      */
38612     invalidText : "{0} is not a valid date - it must be in the format {1}",
38613     /**
38614      * @cfg {String} triggerClass
38615      * An additional CSS class used to style the trigger button.  The trigger will always get the
38616      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38617      * which displays a calendar icon).
38618      */
38619     triggerClass : 'x-form-date-trigger',
38620     
38621
38622     /**
38623      * @cfg {Boolean} useIso
38624      * if enabled, then the date field will use a hidden field to store the 
38625      * real value as iso formated date. default (false)
38626      */ 
38627     useIso : false,
38628     /**
38629      * @cfg {String/Object} autoCreate
38630      * A DomHelper element spec, or true for a default element spec (defaults to
38631      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38632      */ 
38633     // private
38634     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38635     
38636     // private
38637     hiddenField: false,
38638     
38639     onRender : function(ct, position)
38640     {
38641         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38642         if (this.useIso) {
38643             //this.el.dom.removeAttribute('name'); 
38644             Roo.log("Changing name?");
38645             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38646             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38647                     'before', true);
38648             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38649             // prevent input submission
38650             this.hiddenName = this.name;
38651         }
38652             
38653             
38654     },
38655     
38656     // private
38657     validateValue : function(value)
38658     {
38659         value = this.formatDate(value);
38660         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38661             Roo.log('super failed');
38662             return false;
38663         }
38664         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38665              return true;
38666         }
38667         var svalue = value;
38668         value = this.parseDate(value);
38669         if(!value){
38670             Roo.log('parse date failed' + svalue);
38671             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38672             return false;
38673         }
38674         var time = value.getTime();
38675         if(this.minValue && time < this.minValue.getTime()){
38676             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38677             return false;
38678         }
38679         if(this.maxValue && time > this.maxValue.getTime()){
38680             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38681             return false;
38682         }
38683         if(this.disabledDays){
38684             var day = value.getDay();
38685             for(var i = 0; i < this.disabledDays.length; i++) {
38686                 if(day === this.disabledDays[i]){
38687                     this.markInvalid(this.disabledDaysText);
38688                     return false;
38689                 }
38690             }
38691         }
38692         var fvalue = this.formatDate(value);
38693         if(this.ddMatch && this.ddMatch.test(fvalue)){
38694             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38695             return false;
38696         }
38697         return true;
38698     },
38699
38700     // private
38701     // Provides logic to override the default TriggerField.validateBlur which just returns true
38702     validateBlur : function(){
38703         return !this.menu || !this.menu.isVisible();
38704     },
38705     
38706     getName: function()
38707     {
38708         // returns hidden if it's set..
38709         if (!this.rendered) {return ''};
38710         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38711         
38712     },
38713
38714     /**
38715      * Returns the current date value of the date field.
38716      * @return {Date} The date value
38717      */
38718     getValue : function(){
38719         
38720         return  this.hiddenField ?
38721                 this.hiddenField.value :
38722                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38723     },
38724
38725     /**
38726      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38727      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38728      * (the default format used is "m/d/y").
38729      * <br />Usage:
38730      * <pre><code>
38731 //All of these calls set the same date value (May 4, 2006)
38732
38733 //Pass a date object:
38734 var dt = new Date('5/4/06');
38735 dateField.setValue(dt);
38736
38737 //Pass a date string (default format):
38738 dateField.setValue('5/4/06');
38739
38740 //Pass a date string (custom format):
38741 dateField.format = 'Y-m-d';
38742 dateField.setValue('2006-5-4');
38743 </code></pre>
38744      * @param {String/Date} date The date or valid date string
38745      */
38746     setValue : function(date){
38747         if (this.hiddenField) {
38748             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38749         }
38750         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38751         // make sure the value field is always stored as a date..
38752         this.value = this.parseDate(date);
38753         
38754         
38755     },
38756
38757     // private
38758     parseDate : function(value){
38759         if(!value || value instanceof Date){
38760             return value;
38761         }
38762         var v = Date.parseDate(value, this.format);
38763          if (!v && this.useIso) {
38764             v = Date.parseDate(value, 'Y-m-d');
38765         }
38766         if(!v && this.altFormats){
38767             if(!this.altFormatsArray){
38768                 this.altFormatsArray = this.altFormats.split("|");
38769             }
38770             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38771                 v = Date.parseDate(value, this.altFormatsArray[i]);
38772             }
38773         }
38774         return v;
38775     },
38776
38777     // private
38778     formatDate : function(date, fmt){
38779         return (!date || !(date instanceof Date)) ?
38780                date : date.dateFormat(fmt || this.format);
38781     },
38782
38783     // private
38784     menuListeners : {
38785         select: function(m, d){
38786             
38787             this.setValue(d);
38788             this.fireEvent('select', this, d);
38789         },
38790         show : function(){ // retain focus styling
38791             this.onFocus();
38792         },
38793         hide : function(){
38794             this.focus.defer(10, this);
38795             var ml = this.menuListeners;
38796             this.menu.un("select", ml.select,  this);
38797             this.menu.un("show", ml.show,  this);
38798             this.menu.un("hide", ml.hide,  this);
38799         }
38800     },
38801
38802     // private
38803     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38804     onTriggerClick : function(){
38805         if(this.disabled){
38806             return;
38807         }
38808         if(this.menu == null){
38809             this.menu = new Roo.menu.DateMenu();
38810         }
38811         Roo.apply(this.menu.picker,  {
38812             showClear: this.allowBlank,
38813             minDate : this.minValue,
38814             maxDate : this.maxValue,
38815             disabledDatesRE : this.ddMatch,
38816             disabledDatesText : this.disabledDatesText,
38817             disabledDays : this.disabledDays,
38818             disabledDaysText : this.disabledDaysText,
38819             format : this.useIso ? 'Y-m-d' : this.format,
38820             minText : String.format(this.minText, this.formatDate(this.minValue)),
38821             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38822         });
38823         this.menu.on(Roo.apply({}, this.menuListeners, {
38824             scope:this
38825         }));
38826         this.menu.picker.setValue(this.getValue() || new Date());
38827         this.menu.show(this.el, "tl-bl?");
38828     },
38829
38830     beforeBlur : function(){
38831         var v = this.parseDate(this.getRawValue());
38832         if(v){
38833             this.setValue(v);
38834         }
38835     },
38836
38837     /*@
38838      * overide
38839      * 
38840      */
38841     isDirty : function() {
38842         if(this.disabled) {
38843             return false;
38844         }
38845         
38846         if(typeof(this.startValue) === 'undefined'){
38847             return false;
38848         }
38849         
38850         return String(this.getValue()) !== String(this.startValue);
38851         
38852     }
38853 });/*
38854  * Based on:
38855  * Ext JS Library 1.1.1
38856  * Copyright(c) 2006-2007, Ext JS, LLC.
38857  *
38858  * Originally Released Under LGPL - original licence link has changed is not relivant.
38859  *
38860  * Fork - LGPL
38861  * <script type="text/javascript">
38862  */
38863  
38864 /**
38865  * @class Roo.form.MonthField
38866  * @extends Roo.form.TriggerField
38867  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38868 * @constructor
38869 * Create a new MonthField
38870 * @param {Object} config
38871  */
38872 Roo.form.MonthField = function(config){
38873     
38874     Roo.form.MonthField.superclass.constructor.call(this, config);
38875     
38876       this.addEvents({
38877          
38878         /**
38879          * @event select
38880          * Fires when a date is selected
38881              * @param {Roo.form.MonthFieeld} combo This combo box
38882              * @param {Date} date The date selected
38883              */
38884         'select' : true
38885          
38886     });
38887     
38888     
38889     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38890     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38891     this.ddMatch = null;
38892     if(this.disabledDates){
38893         var dd = this.disabledDates;
38894         var re = "(?:";
38895         for(var i = 0; i < dd.length; i++){
38896             re += dd[i];
38897             if(i != dd.length-1) re += "|";
38898         }
38899         this.ddMatch = new RegExp(re + ")");
38900     }
38901 };
38902
38903 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38904     /**
38905      * @cfg {String} format
38906      * The default date format string which can be overriden for localization support.  The format must be
38907      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38908      */
38909     format : "M Y",
38910     /**
38911      * @cfg {String} altFormats
38912      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38913      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38914      */
38915     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38916     /**
38917      * @cfg {Array} disabledDays
38918      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38919      */
38920     disabledDays : [0,1,2,3,4,5,6],
38921     /**
38922      * @cfg {String} disabledDaysText
38923      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38924      */
38925     disabledDaysText : "Disabled",
38926     /**
38927      * @cfg {Array} disabledDates
38928      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38929      * expression so they are very powerful. Some examples:
38930      * <ul>
38931      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38932      * <li>["03/08", "09/16"] would disable those days for every year</li>
38933      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38934      * <li>["03/../2006"] would disable every day in March 2006</li>
38935      * <li>["^03"] would disable every day in every March</li>
38936      * </ul>
38937      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38938      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38939      */
38940     disabledDates : null,
38941     /**
38942      * @cfg {String} disabledDatesText
38943      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38944      */
38945     disabledDatesText : "Disabled",
38946     /**
38947      * @cfg {Date/String} minValue
38948      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38949      * valid format (defaults to null).
38950      */
38951     minValue : null,
38952     /**
38953      * @cfg {Date/String} maxValue
38954      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38955      * valid format (defaults to null).
38956      */
38957     maxValue : null,
38958     /**
38959      * @cfg {String} minText
38960      * The error text to display when the date in the cell is before minValue (defaults to
38961      * 'The date in this field must be after {minValue}').
38962      */
38963     minText : "The date in this field must be equal to or after {0}",
38964     /**
38965      * @cfg {String} maxTextf
38966      * The error text to display when the date in the cell is after maxValue (defaults to
38967      * 'The date in this field must be before {maxValue}').
38968      */
38969     maxText : "The date in this field must be equal to or before {0}",
38970     /**
38971      * @cfg {String} invalidText
38972      * The error text to display when the date in the field is invalid (defaults to
38973      * '{value} is not a valid date - it must be in the format {format}').
38974      */
38975     invalidText : "{0} is not a valid date - it must be in the format {1}",
38976     /**
38977      * @cfg {String} triggerClass
38978      * An additional CSS class used to style the trigger button.  The trigger will always get the
38979      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38980      * which displays a calendar icon).
38981      */
38982     triggerClass : 'x-form-date-trigger',
38983     
38984
38985     /**
38986      * @cfg {Boolean} useIso
38987      * if enabled, then the date field will use a hidden field to store the 
38988      * real value as iso formated date. default (true)
38989      */ 
38990     useIso : true,
38991     /**
38992      * @cfg {String/Object} autoCreate
38993      * A DomHelper element spec, or true for a default element spec (defaults to
38994      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38995      */ 
38996     // private
38997     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
38998     
38999     // private
39000     hiddenField: false,
39001     
39002     hideMonthPicker : false,
39003     
39004     onRender : function(ct, position)
39005     {
39006         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39007         if (this.useIso) {
39008             this.el.dom.removeAttribute('name'); 
39009             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39010                     'before', true);
39011             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39012             // prevent input submission
39013             this.hiddenName = this.name;
39014         }
39015             
39016             
39017     },
39018     
39019     // private
39020     validateValue : function(value)
39021     {
39022         value = this.formatDate(value);
39023         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39024             return false;
39025         }
39026         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39027              return true;
39028         }
39029         var svalue = value;
39030         value = this.parseDate(value);
39031         if(!value){
39032             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39033             return false;
39034         }
39035         var time = value.getTime();
39036         if(this.minValue && time < this.minValue.getTime()){
39037             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39038             return false;
39039         }
39040         if(this.maxValue && time > this.maxValue.getTime()){
39041             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39042             return false;
39043         }
39044         /*if(this.disabledDays){
39045             var day = value.getDay();
39046             for(var i = 0; i < this.disabledDays.length; i++) {
39047                 if(day === this.disabledDays[i]){
39048                     this.markInvalid(this.disabledDaysText);
39049                     return false;
39050                 }
39051             }
39052         }
39053         */
39054         var fvalue = this.formatDate(value);
39055         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39056             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39057             return false;
39058         }
39059         */
39060         return true;
39061     },
39062
39063     // private
39064     // Provides logic to override the default TriggerField.validateBlur which just returns true
39065     validateBlur : function(){
39066         return !this.menu || !this.menu.isVisible();
39067     },
39068
39069     /**
39070      * Returns the current date value of the date field.
39071      * @return {Date} The date value
39072      */
39073     getValue : function(){
39074         
39075         
39076         
39077         return  this.hiddenField ?
39078                 this.hiddenField.value :
39079                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39080     },
39081
39082     /**
39083      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39084      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39085      * (the default format used is "m/d/y").
39086      * <br />Usage:
39087      * <pre><code>
39088 //All of these calls set the same date value (May 4, 2006)
39089
39090 //Pass a date object:
39091 var dt = new Date('5/4/06');
39092 monthField.setValue(dt);
39093
39094 //Pass a date string (default format):
39095 monthField.setValue('5/4/06');
39096
39097 //Pass a date string (custom format):
39098 monthField.format = 'Y-m-d';
39099 monthField.setValue('2006-5-4');
39100 </code></pre>
39101      * @param {String/Date} date The date or valid date string
39102      */
39103     setValue : function(date){
39104         Roo.log('month setValue' + date);
39105         // can only be first of month..
39106         
39107         var val = this.parseDate(date);
39108         
39109         if (this.hiddenField) {
39110             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39111         }
39112         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39113         this.value = this.parseDate(date);
39114     },
39115
39116     // private
39117     parseDate : function(value){
39118         if(!value || value instanceof Date){
39119             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39120             return value;
39121         }
39122         var v = Date.parseDate(value, this.format);
39123         if (!v && this.useIso) {
39124             v = Date.parseDate(value, 'Y-m-d');
39125         }
39126         if (v) {
39127             // 
39128             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39129         }
39130         
39131         
39132         if(!v && this.altFormats){
39133             if(!this.altFormatsArray){
39134                 this.altFormatsArray = this.altFormats.split("|");
39135             }
39136             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39137                 v = Date.parseDate(value, this.altFormatsArray[i]);
39138             }
39139         }
39140         return v;
39141     },
39142
39143     // private
39144     formatDate : function(date, fmt){
39145         return (!date || !(date instanceof Date)) ?
39146                date : date.dateFormat(fmt || this.format);
39147     },
39148
39149     // private
39150     menuListeners : {
39151         select: function(m, d){
39152             this.setValue(d);
39153             this.fireEvent('select', this, d);
39154         },
39155         show : function(){ // retain focus styling
39156             this.onFocus();
39157         },
39158         hide : function(){
39159             this.focus.defer(10, this);
39160             var ml = this.menuListeners;
39161             this.menu.un("select", ml.select,  this);
39162             this.menu.un("show", ml.show,  this);
39163             this.menu.un("hide", ml.hide,  this);
39164         }
39165     },
39166     // private
39167     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39168     onTriggerClick : function(){
39169         if(this.disabled){
39170             return;
39171         }
39172         if(this.menu == null){
39173             this.menu = new Roo.menu.DateMenu();
39174            
39175         }
39176         
39177         Roo.apply(this.menu.picker,  {
39178             
39179             showClear: this.allowBlank,
39180             minDate : this.minValue,
39181             maxDate : this.maxValue,
39182             disabledDatesRE : this.ddMatch,
39183             disabledDatesText : this.disabledDatesText,
39184             
39185             format : this.useIso ? 'Y-m-d' : this.format,
39186             minText : String.format(this.minText, this.formatDate(this.minValue)),
39187             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39188             
39189         });
39190          this.menu.on(Roo.apply({}, this.menuListeners, {
39191             scope:this
39192         }));
39193        
39194         
39195         var m = this.menu;
39196         var p = m.picker;
39197         
39198         // hide month picker get's called when we called by 'before hide';
39199         
39200         var ignorehide = true;
39201         p.hideMonthPicker  = function(disableAnim){
39202             if (ignorehide) {
39203                 return;
39204             }
39205              if(this.monthPicker){
39206                 Roo.log("hideMonthPicker called");
39207                 if(disableAnim === true){
39208                     this.monthPicker.hide();
39209                 }else{
39210                     this.monthPicker.slideOut('t', {duration:.2});
39211                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39212                     p.fireEvent("select", this, this.value);
39213                     m.hide();
39214                 }
39215             }
39216         }
39217         
39218         Roo.log('picker set value');
39219         Roo.log(this.getValue());
39220         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39221         m.show(this.el, 'tl-bl?');
39222         ignorehide  = false;
39223         // this will trigger hideMonthPicker..
39224         
39225         
39226         // hidden the day picker
39227         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39228         
39229         
39230         
39231       
39232         
39233         p.showMonthPicker.defer(100, p);
39234     
39235         
39236        
39237     },
39238
39239     beforeBlur : function(){
39240         var v = this.parseDate(this.getRawValue());
39241         if(v){
39242             this.setValue(v);
39243         }
39244     }
39245
39246     /** @cfg {Boolean} grow @hide */
39247     /** @cfg {Number} growMin @hide */
39248     /** @cfg {Number} growMax @hide */
39249     /**
39250      * @hide
39251      * @method autoSize
39252      */
39253 });/*
39254  * Based on:
39255  * Ext JS Library 1.1.1
39256  * Copyright(c) 2006-2007, Ext JS, LLC.
39257  *
39258  * Originally Released Under LGPL - original licence link has changed is not relivant.
39259  *
39260  * Fork - LGPL
39261  * <script type="text/javascript">
39262  */
39263  
39264
39265 /**
39266  * @class Roo.form.ComboBox
39267  * @extends Roo.form.TriggerField
39268  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39269  * @constructor
39270  * Create a new ComboBox.
39271  * @param {Object} config Configuration options
39272  */
39273 Roo.form.ComboBox = function(config){
39274     Roo.form.ComboBox.superclass.constructor.call(this, config);
39275     this.addEvents({
39276         /**
39277          * @event expand
39278          * Fires when the dropdown list is expanded
39279              * @param {Roo.form.ComboBox} combo This combo box
39280              */
39281         'expand' : true,
39282         /**
39283          * @event collapse
39284          * Fires when the dropdown list is collapsed
39285              * @param {Roo.form.ComboBox} combo This combo box
39286              */
39287         'collapse' : true,
39288         /**
39289          * @event beforeselect
39290          * Fires before a list item is selected. Return false to cancel the selection.
39291              * @param {Roo.form.ComboBox} combo This combo box
39292              * @param {Roo.data.Record} record The data record returned from the underlying store
39293              * @param {Number} index The index of the selected item in the dropdown list
39294              */
39295         'beforeselect' : true,
39296         /**
39297          * @event select
39298          * Fires when a list item is selected
39299              * @param {Roo.form.ComboBox} combo This combo box
39300              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39301              * @param {Number} index The index of the selected item in the dropdown list
39302              */
39303         'select' : true,
39304         /**
39305          * @event beforequery
39306          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39307          * The event object passed has these properties:
39308              * @param {Roo.form.ComboBox} combo This combo box
39309              * @param {String} query The query
39310              * @param {Boolean} forceAll true to force "all" query
39311              * @param {Boolean} cancel true to cancel the query
39312              * @param {Object} e The query event object
39313              */
39314         'beforequery': true,
39315          /**
39316          * @event add
39317          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39318              * @param {Roo.form.ComboBox} combo This combo box
39319              */
39320         'add' : true,
39321         /**
39322          * @event edit
39323          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39324              * @param {Roo.form.ComboBox} combo This combo box
39325              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39326              */
39327         'edit' : true
39328         
39329         
39330     });
39331     if(this.transform){
39332         this.allowDomMove = false;
39333         var s = Roo.getDom(this.transform);
39334         if(!this.hiddenName){
39335             this.hiddenName = s.name;
39336         }
39337         if(!this.store){
39338             this.mode = 'local';
39339             var d = [], opts = s.options;
39340             for(var i = 0, len = opts.length;i < len; i++){
39341                 var o = opts[i];
39342                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39343                 if(o.selected) {
39344                     this.value = value;
39345                 }
39346                 d.push([value, o.text]);
39347             }
39348             this.store = new Roo.data.SimpleStore({
39349                 'id': 0,
39350                 fields: ['value', 'text'],
39351                 data : d
39352             });
39353             this.valueField = 'value';
39354             this.displayField = 'text';
39355         }
39356         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39357         if(!this.lazyRender){
39358             this.target = true;
39359             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39360             s.parentNode.removeChild(s); // remove it
39361             this.render(this.el.parentNode);
39362         }else{
39363             s.parentNode.removeChild(s); // remove it
39364         }
39365
39366     }
39367     if (this.store) {
39368         this.store = Roo.factory(this.store, Roo.data);
39369     }
39370     
39371     this.selectedIndex = -1;
39372     if(this.mode == 'local'){
39373         if(config.queryDelay === undefined){
39374             this.queryDelay = 10;
39375         }
39376         if(config.minChars === undefined){
39377             this.minChars = 0;
39378         }
39379     }
39380 };
39381
39382 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39383     /**
39384      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39385      */
39386     /**
39387      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39388      * rendering into an Roo.Editor, defaults to false)
39389      */
39390     /**
39391      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39392      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39393      */
39394     /**
39395      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39396      */
39397     /**
39398      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39399      * the dropdown list (defaults to undefined, with no header element)
39400      */
39401
39402      /**
39403      * @cfg {String/Roo.Template} tpl The template to use to render the output
39404      */
39405      
39406     // private
39407     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39408     /**
39409      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39410      */
39411     listWidth: undefined,
39412     /**
39413      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39414      * mode = 'remote' or 'text' if mode = 'local')
39415      */
39416     displayField: undefined,
39417     /**
39418      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39419      * mode = 'remote' or 'value' if mode = 'local'). 
39420      * Note: use of a valueField requires the user make a selection
39421      * in order for a value to be mapped.
39422      */
39423     valueField: undefined,
39424     
39425     
39426     /**
39427      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39428      * field's data value (defaults to the underlying DOM element's name)
39429      */
39430     hiddenName: undefined,
39431     /**
39432      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39433      */
39434     listClass: '',
39435     /**
39436      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39437      */
39438     selectedClass: 'x-combo-selected',
39439     /**
39440      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39441      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39442      * which displays a downward arrow icon).
39443      */
39444     triggerClass : 'x-form-arrow-trigger',
39445     /**
39446      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39447      */
39448     shadow:'sides',
39449     /**
39450      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39451      * anchor positions (defaults to 'tl-bl')
39452      */
39453     listAlign: 'tl-bl?',
39454     /**
39455      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39456      */
39457     maxHeight: 300,
39458     /**
39459      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39460      * query specified by the allQuery config option (defaults to 'query')
39461      */
39462     triggerAction: 'query',
39463     /**
39464      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39465      * (defaults to 4, does not apply if editable = false)
39466      */
39467     minChars : 4,
39468     /**
39469      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39470      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39471      */
39472     typeAhead: false,
39473     /**
39474      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39475      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39476      */
39477     queryDelay: 500,
39478     /**
39479      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39480      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39481      */
39482     pageSize: 0,
39483     /**
39484      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39485      * when editable = true (defaults to false)
39486      */
39487     selectOnFocus:false,
39488     /**
39489      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39490      */
39491     queryParam: 'query',
39492     /**
39493      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39494      * when mode = 'remote' (defaults to 'Loading...')
39495      */
39496     loadingText: 'Loading...',
39497     /**
39498      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39499      */
39500     resizable: false,
39501     /**
39502      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39503      */
39504     handleHeight : 8,
39505     /**
39506      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39507      * traditional select (defaults to true)
39508      */
39509     editable: true,
39510     /**
39511      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39512      */
39513     allQuery: '',
39514     /**
39515      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39516      */
39517     mode: 'remote',
39518     /**
39519      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39520      * listWidth has a higher value)
39521      */
39522     minListWidth : 70,
39523     /**
39524      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39525      * allow the user to set arbitrary text into the field (defaults to false)
39526      */
39527     forceSelection:false,
39528     /**
39529      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39530      * if typeAhead = true (defaults to 250)
39531      */
39532     typeAheadDelay : 250,
39533     /**
39534      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39535      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39536      */
39537     valueNotFoundText : undefined,
39538     /**
39539      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39540      */
39541     blockFocus : false,
39542     
39543     /**
39544      * @cfg {Boolean} disableClear Disable showing of clear button.
39545      */
39546     disableClear : false,
39547     /**
39548      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39549      */
39550     alwaysQuery : false,
39551     
39552     //private
39553     addicon : false,
39554     editicon: false,
39555     
39556     // element that contains real text value.. (when hidden is used..)
39557      
39558     // private
39559     onRender : function(ct, position){
39560         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39561         if(this.hiddenName){
39562             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39563                     'before', true);
39564             this.hiddenField.value =
39565                 this.hiddenValue !== undefined ? this.hiddenValue :
39566                 this.value !== undefined ? this.value : '';
39567
39568             // prevent input submission
39569             this.el.dom.removeAttribute('name');
39570              
39571              
39572         }
39573         if(Roo.isGecko){
39574             this.el.dom.setAttribute('autocomplete', 'off');
39575         }
39576
39577         var cls = 'x-combo-list';
39578
39579         this.list = new Roo.Layer({
39580             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39581         });
39582
39583         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39584         this.list.setWidth(lw);
39585         this.list.swallowEvent('mousewheel');
39586         this.assetHeight = 0;
39587
39588         if(this.title){
39589             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39590             this.assetHeight += this.header.getHeight();
39591         }
39592
39593         this.innerList = this.list.createChild({cls:cls+'-inner'});
39594         this.innerList.on('mouseover', this.onViewOver, this);
39595         this.innerList.on('mousemove', this.onViewMove, this);
39596         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39597         
39598         if(this.allowBlank && !this.pageSize && !this.disableClear){
39599             this.footer = this.list.createChild({cls:cls+'-ft'});
39600             this.pageTb = new Roo.Toolbar(this.footer);
39601            
39602         }
39603         if(this.pageSize){
39604             this.footer = this.list.createChild({cls:cls+'-ft'});
39605             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39606                     {pageSize: this.pageSize});
39607             
39608         }
39609         
39610         if (this.pageTb && this.allowBlank && !this.disableClear) {
39611             var _this = this;
39612             this.pageTb.add(new Roo.Toolbar.Fill(), {
39613                 cls: 'x-btn-icon x-btn-clear',
39614                 text: '&#160;',
39615                 handler: function()
39616                 {
39617                     _this.collapse();
39618                     _this.clearValue();
39619                     _this.onSelect(false, -1);
39620                 }
39621             });
39622         }
39623         if (this.footer) {
39624             this.assetHeight += this.footer.getHeight();
39625         }
39626         
39627
39628         if(!this.tpl){
39629             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39630         }
39631
39632         this.view = new Roo.View(this.innerList, this.tpl, {
39633             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39634         });
39635
39636         this.view.on('click', this.onViewClick, this);
39637
39638         this.store.on('beforeload', this.onBeforeLoad, this);
39639         this.store.on('load', this.onLoad, this);
39640         this.store.on('loadexception', this.onLoadException, this);
39641
39642         if(this.resizable){
39643             this.resizer = new Roo.Resizable(this.list,  {
39644                pinned:true, handles:'se'
39645             });
39646             this.resizer.on('resize', function(r, w, h){
39647                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39648                 this.listWidth = w;
39649                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39650                 this.restrictHeight();
39651             }, this);
39652             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39653         }
39654         if(!this.editable){
39655             this.editable = true;
39656             this.setEditable(false);
39657         }  
39658         
39659         
39660         if (typeof(this.events.add.listeners) != 'undefined') {
39661             
39662             this.addicon = this.wrap.createChild(
39663                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39664        
39665             this.addicon.on('click', function(e) {
39666                 this.fireEvent('add', this);
39667             }, this);
39668         }
39669         if (typeof(this.events.edit.listeners) != 'undefined') {
39670             
39671             this.editicon = this.wrap.createChild(
39672                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39673             if (this.addicon) {
39674                 this.editicon.setStyle('margin-left', '40px');
39675             }
39676             this.editicon.on('click', function(e) {
39677                 
39678                 // we fire even  if inothing is selected..
39679                 this.fireEvent('edit', this, this.lastData );
39680                 
39681             }, this);
39682         }
39683         
39684         
39685         
39686     },
39687
39688     // private
39689     initEvents : function(){
39690         Roo.form.ComboBox.superclass.initEvents.call(this);
39691
39692         this.keyNav = new Roo.KeyNav(this.el, {
39693             "up" : function(e){
39694                 this.inKeyMode = true;
39695                 this.selectPrev();
39696             },
39697
39698             "down" : function(e){
39699                 if(!this.isExpanded()){
39700                     this.onTriggerClick();
39701                 }else{
39702                     this.inKeyMode = true;
39703                     this.selectNext();
39704                 }
39705             },
39706
39707             "enter" : function(e){
39708                 this.onViewClick();
39709                 //return true;
39710             },
39711
39712             "esc" : function(e){
39713                 this.collapse();
39714             },
39715
39716             "tab" : function(e){
39717                 this.onViewClick(false);
39718                 this.fireEvent("specialkey", this, e);
39719                 return true;
39720             },
39721
39722             scope : this,
39723
39724             doRelay : function(foo, bar, hname){
39725                 if(hname == 'down' || this.scope.isExpanded()){
39726                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39727                 }
39728                 return true;
39729             },
39730
39731             forceKeyDown: true
39732         });
39733         this.queryDelay = Math.max(this.queryDelay || 10,
39734                 this.mode == 'local' ? 10 : 250);
39735         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39736         if(this.typeAhead){
39737             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39738         }
39739         if(this.editable !== false){
39740             this.el.on("keyup", this.onKeyUp, this);
39741         }
39742         if(this.forceSelection){
39743             this.on('blur', this.doForce, this);
39744         }
39745     },
39746
39747     onDestroy : function(){
39748         if(this.view){
39749             this.view.setStore(null);
39750             this.view.el.removeAllListeners();
39751             this.view.el.remove();
39752             this.view.purgeListeners();
39753         }
39754         if(this.list){
39755             this.list.destroy();
39756         }
39757         if(this.store){
39758             this.store.un('beforeload', this.onBeforeLoad, this);
39759             this.store.un('load', this.onLoad, this);
39760             this.store.un('loadexception', this.onLoadException, this);
39761         }
39762         Roo.form.ComboBox.superclass.onDestroy.call(this);
39763     },
39764
39765     // private
39766     fireKey : function(e){
39767         if(e.isNavKeyPress() && !this.list.isVisible()){
39768             this.fireEvent("specialkey", this, e);
39769         }
39770     },
39771
39772     // private
39773     onResize: function(w, h){
39774         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39775         
39776         if(typeof w != 'number'){
39777             // we do not handle it!?!?
39778             return;
39779         }
39780         var tw = this.trigger.getWidth();
39781         tw += this.addicon ? this.addicon.getWidth() : 0;
39782         tw += this.editicon ? this.editicon.getWidth() : 0;
39783         var x = w - tw;
39784         this.el.setWidth( this.adjustWidth('input', x));
39785             
39786         this.trigger.setStyle('left', x+'px');
39787         
39788         if(this.list && this.listWidth === undefined){
39789             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39790             this.list.setWidth(lw);
39791             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39792         }
39793         
39794     
39795         
39796     },
39797
39798     /**
39799      * Allow or prevent the user from directly editing the field text.  If false is passed,
39800      * the user will only be able to select from the items defined in the dropdown list.  This method
39801      * is the runtime equivalent of setting the 'editable' config option at config time.
39802      * @param {Boolean} value True to allow the user to directly edit the field text
39803      */
39804     setEditable : function(value){
39805         if(value == this.editable){
39806             return;
39807         }
39808         this.editable = value;
39809         if(!value){
39810             this.el.dom.setAttribute('readOnly', true);
39811             this.el.on('mousedown', this.onTriggerClick,  this);
39812             this.el.addClass('x-combo-noedit');
39813         }else{
39814             this.el.dom.setAttribute('readOnly', false);
39815             this.el.un('mousedown', this.onTriggerClick,  this);
39816             this.el.removeClass('x-combo-noedit');
39817         }
39818     },
39819
39820     // private
39821     onBeforeLoad : function(){
39822         if(!this.hasFocus){
39823             return;
39824         }
39825         this.innerList.update(this.loadingText ?
39826                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39827         this.restrictHeight();
39828         this.selectedIndex = -1;
39829     },
39830
39831     // private
39832     onLoad : function(){
39833         if(!this.hasFocus){
39834             return;
39835         }
39836         if(this.store.getCount() > 0){
39837             this.expand();
39838             this.restrictHeight();
39839             if(this.lastQuery == this.allQuery){
39840                 if(this.editable){
39841                     this.el.dom.select();
39842                 }
39843                 if(!this.selectByValue(this.value, true)){
39844                     this.select(0, true);
39845                 }
39846             }else{
39847                 this.selectNext();
39848                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39849                     this.taTask.delay(this.typeAheadDelay);
39850                 }
39851             }
39852         }else{
39853             this.onEmptyResults();
39854         }
39855         //this.el.focus();
39856     },
39857     // private
39858     onLoadException : function()
39859     {
39860         this.collapse();
39861         Roo.log(this.store.reader.jsonData);
39862         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39863             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39864         }
39865         
39866         
39867     },
39868     // private
39869     onTypeAhead : function(){
39870         if(this.store.getCount() > 0){
39871             var r = this.store.getAt(0);
39872             var newValue = r.data[this.displayField];
39873             var len = newValue.length;
39874             var selStart = this.getRawValue().length;
39875             if(selStart != len){
39876                 this.setRawValue(newValue);
39877                 this.selectText(selStart, newValue.length);
39878             }
39879         }
39880     },
39881
39882     // private
39883     onSelect : function(record, index){
39884         if(this.fireEvent('beforeselect', this, record, index) !== false){
39885             this.setFromData(index > -1 ? record.data : false);
39886             this.collapse();
39887             this.fireEvent('select', this, record, index);
39888         }
39889     },
39890
39891     /**
39892      * Returns the currently selected field value or empty string if no value is set.
39893      * @return {String} value The selected value
39894      */
39895     getValue : function(){
39896         if(this.valueField){
39897             return typeof this.value != 'undefined' ? this.value : '';
39898         }
39899         return Roo.form.ComboBox.superclass.getValue.call(this);
39900     },
39901
39902     /**
39903      * Clears any text/value currently set in the field
39904      */
39905     clearValue : function(){
39906         if(this.hiddenField){
39907             this.hiddenField.value = '';
39908         }
39909         this.value = '';
39910         this.setRawValue('');
39911         this.lastSelectionText = '';
39912         
39913     },
39914
39915     /**
39916      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39917      * will be displayed in the field.  If the value does not match the data value of an existing item,
39918      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39919      * Otherwise the field will be blank (although the value will still be set).
39920      * @param {String} value The value to match
39921      */
39922     setValue : function(v){
39923         var text = v;
39924         if(this.valueField){
39925             var r = this.findRecord(this.valueField, v);
39926             if(r){
39927                 text = r.data[this.displayField];
39928             }else if(this.valueNotFoundText !== undefined){
39929                 text = this.valueNotFoundText;
39930             }
39931         }
39932         this.lastSelectionText = text;
39933         if(this.hiddenField){
39934             this.hiddenField.value = v;
39935         }
39936         Roo.form.ComboBox.superclass.setValue.call(this, text);
39937         this.value = v;
39938     },
39939     /**
39940      * @property {Object} the last set data for the element
39941      */
39942     
39943     lastData : false,
39944     /**
39945      * Sets the value of the field based on a object which is related to the record format for the store.
39946      * @param {Object} value the value to set as. or false on reset?
39947      */
39948     setFromData : function(o){
39949         var dv = ''; // display value
39950         var vv = ''; // value value..
39951         this.lastData = o;
39952         if (this.displayField) {
39953             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39954         } else {
39955             // this is an error condition!!!
39956             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39957         }
39958         
39959         if(this.valueField){
39960             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39961         }
39962         if(this.hiddenField){
39963             this.hiddenField.value = vv;
39964             
39965             this.lastSelectionText = dv;
39966             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39967             this.value = vv;
39968             return;
39969         }
39970         // no hidden field.. - we store the value in 'value', but still display
39971         // display field!!!!
39972         this.lastSelectionText = dv;
39973         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39974         this.value = vv;
39975         
39976         
39977     },
39978     // private
39979     reset : function(){
39980         // overridden so that last data is reset..
39981         this.setValue(this.resetValue);
39982         this.clearInvalid();
39983         this.lastData = false;
39984         if (this.view) {
39985             this.view.clearSelections();
39986         }
39987     },
39988     // private
39989     findRecord : function(prop, value){
39990         var record;
39991         if(this.store.getCount() > 0){
39992             this.store.each(function(r){
39993                 if(r.data[prop] == value){
39994                     record = r;
39995                     return false;
39996                 }
39997                 return true;
39998             });
39999         }
40000         return record;
40001     },
40002     
40003     getName: function()
40004     {
40005         // returns hidden if it's set..
40006         if (!this.rendered) {return ''};
40007         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40008         
40009     },
40010     // private
40011     onViewMove : function(e, t){
40012         this.inKeyMode = false;
40013     },
40014
40015     // private
40016     onViewOver : function(e, t){
40017         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40018             return;
40019         }
40020         var item = this.view.findItemFromChild(t);
40021         if(item){
40022             var index = this.view.indexOf(item);
40023             this.select(index, false);
40024         }
40025     },
40026
40027     // private
40028     onViewClick : function(doFocus)
40029     {
40030         var index = this.view.getSelectedIndexes()[0];
40031         var r = this.store.getAt(index);
40032         if(r){
40033             this.onSelect(r, index);
40034         }
40035         if(doFocus !== false && !this.blockFocus){
40036             this.el.focus();
40037         }
40038     },
40039
40040     // private
40041     restrictHeight : function(){
40042         this.innerList.dom.style.height = '';
40043         var inner = this.innerList.dom;
40044         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40045         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40046         this.list.beginUpdate();
40047         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40048         this.list.alignTo(this.el, this.listAlign);
40049         this.list.endUpdate();
40050     },
40051
40052     // private
40053     onEmptyResults : function(){
40054         this.collapse();
40055     },
40056
40057     /**
40058      * Returns true if the dropdown list is expanded, else false.
40059      */
40060     isExpanded : function(){
40061         return this.list.isVisible();
40062     },
40063
40064     /**
40065      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40066      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40067      * @param {String} value The data value of the item to select
40068      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40069      * selected item if it is not currently in view (defaults to true)
40070      * @return {Boolean} True if the value matched an item in the list, else false
40071      */
40072     selectByValue : function(v, scrollIntoView){
40073         if(v !== undefined && v !== null){
40074             var r = this.findRecord(this.valueField || this.displayField, v);
40075             if(r){
40076                 this.select(this.store.indexOf(r), scrollIntoView);
40077                 return true;
40078             }
40079         }
40080         return false;
40081     },
40082
40083     /**
40084      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40085      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40086      * @param {Number} index The zero-based index of the list item to select
40087      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40088      * selected item if it is not currently in view (defaults to true)
40089      */
40090     select : function(index, scrollIntoView){
40091         this.selectedIndex = index;
40092         this.view.select(index);
40093         if(scrollIntoView !== false){
40094             var el = this.view.getNode(index);
40095             if(el){
40096                 this.innerList.scrollChildIntoView(el, false);
40097             }
40098         }
40099     },
40100
40101     // private
40102     selectNext : function(){
40103         var ct = this.store.getCount();
40104         if(ct > 0){
40105             if(this.selectedIndex == -1){
40106                 this.select(0);
40107             }else if(this.selectedIndex < ct-1){
40108                 this.select(this.selectedIndex+1);
40109             }
40110         }
40111     },
40112
40113     // private
40114     selectPrev : function(){
40115         var ct = this.store.getCount();
40116         if(ct > 0){
40117             if(this.selectedIndex == -1){
40118                 this.select(0);
40119             }else if(this.selectedIndex != 0){
40120                 this.select(this.selectedIndex-1);
40121             }
40122         }
40123     },
40124
40125     // private
40126     onKeyUp : function(e){
40127         if(this.editable !== false && !e.isSpecialKey()){
40128             this.lastKey = e.getKey();
40129             this.dqTask.delay(this.queryDelay);
40130         }
40131     },
40132
40133     // private
40134     validateBlur : function(){
40135         return !this.list || !this.list.isVisible();   
40136     },
40137
40138     // private
40139     initQuery : function(){
40140         this.doQuery(this.getRawValue());
40141     },
40142
40143     // private
40144     doForce : function(){
40145         if(this.el.dom.value.length > 0){
40146             this.el.dom.value =
40147                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40148              
40149         }
40150     },
40151
40152     /**
40153      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40154      * query allowing the query action to be canceled if needed.
40155      * @param {String} query The SQL query to execute
40156      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40157      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40158      * saved in the current store (defaults to false)
40159      */
40160     doQuery : function(q, forceAll){
40161         if(q === undefined || q === null){
40162             q = '';
40163         }
40164         var qe = {
40165             query: q,
40166             forceAll: forceAll,
40167             combo: this,
40168             cancel:false
40169         };
40170         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40171             return false;
40172         }
40173         q = qe.query;
40174         forceAll = qe.forceAll;
40175         if(forceAll === true || (q.length >= this.minChars)){
40176             if(this.lastQuery != q || this.alwaysQuery){
40177                 this.lastQuery = q;
40178                 if(this.mode == 'local'){
40179                     this.selectedIndex = -1;
40180                     if(forceAll){
40181                         this.store.clearFilter();
40182                     }else{
40183                         this.store.filter(this.displayField, q);
40184                     }
40185                     this.onLoad();
40186                 }else{
40187                     this.store.baseParams[this.queryParam] = q;
40188                     this.store.load({
40189                         params: this.getParams(q)
40190                     });
40191                     this.expand();
40192                 }
40193             }else{
40194                 this.selectedIndex = -1;
40195                 this.onLoad();   
40196             }
40197         }
40198     },
40199
40200     // private
40201     getParams : function(q){
40202         var p = {};
40203         //p[this.queryParam] = q;
40204         if(this.pageSize){
40205             p.start = 0;
40206             p.limit = this.pageSize;
40207         }
40208         return p;
40209     },
40210
40211     /**
40212      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40213      */
40214     collapse : function(){
40215         if(!this.isExpanded()){
40216             return;
40217         }
40218         this.list.hide();
40219         Roo.get(document).un('mousedown', this.collapseIf, this);
40220         Roo.get(document).un('mousewheel', this.collapseIf, this);
40221         if (!this.editable) {
40222             Roo.get(document).un('keydown', this.listKeyPress, this);
40223         }
40224         this.fireEvent('collapse', this);
40225     },
40226
40227     // private
40228     collapseIf : function(e){
40229         if(!e.within(this.wrap) && !e.within(this.list)){
40230             this.collapse();
40231         }
40232     },
40233
40234     /**
40235      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40236      */
40237     expand : function(){
40238         if(this.isExpanded() || !this.hasFocus){
40239             return;
40240         }
40241         this.list.alignTo(this.el, this.listAlign);
40242         this.list.show();
40243         Roo.get(document).on('mousedown', this.collapseIf, this);
40244         Roo.get(document).on('mousewheel', this.collapseIf, this);
40245         if (!this.editable) {
40246             Roo.get(document).on('keydown', this.listKeyPress, this);
40247         }
40248         
40249         this.fireEvent('expand', this);
40250     },
40251
40252     // private
40253     // Implements the default empty TriggerField.onTriggerClick function
40254     onTriggerClick : function(){
40255         if(this.disabled){
40256             return;
40257         }
40258         if(this.isExpanded()){
40259             this.collapse();
40260             if (!this.blockFocus) {
40261                 this.el.focus();
40262             }
40263             
40264         }else {
40265             this.hasFocus = true;
40266             if(this.triggerAction == 'all') {
40267                 this.doQuery(this.allQuery, true);
40268             } else {
40269                 this.doQuery(this.getRawValue());
40270             }
40271             if (!this.blockFocus) {
40272                 this.el.focus();
40273             }
40274         }
40275     },
40276     listKeyPress : function(e)
40277     {
40278         //Roo.log('listkeypress');
40279         // scroll to first matching element based on key pres..
40280         if (e.isSpecialKey()) {
40281             return false;
40282         }
40283         var k = String.fromCharCode(e.getKey()).toUpperCase();
40284         //Roo.log(k);
40285         var match  = false;
40286         var csel = this.view.getSelectedNodes();
40287         var cselitem = false;
40288         if (csel.length) {
40289             var ix = this.view.indexOf(csel[0]);
40290             cselitem  = this.store.getAt(ix);
40291             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40292                 cselitem = false;
40293             }
40294             
40295         }
40296         
40297         this.store.each(function(v) { 
40298             if (cselitem) {
40299                 // start at existing selection.
40300                 if (cselitem.id == v.id) {
40301                     cselitem = false;
40302                 }
40303                 return;
40304             }
40305                 
40306             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40307                 match = this.store.indexOf(v);
40308                 return false;
40309             }
40310         }, this);
40311         
40312         if (match === false) {
40313             return true; // no more action?
40314         }
40315         // scroll to?
40316         this.view.select(match);
40317         var sn = Roo.get(this.view.getSelectedNodes()[0])
40318         sn.scrollIntoView(sn.dom.parentNode, false);
40319     }
40320
40321     /** 
40322     * @cfg {Boolean} grow 
40323     * @hide 
40324     */
40325     /** 
40326     * @cfg {Number} growMin 
40327     * @hide 
40328     */
40329     /** 
40330     * @cfg {Number} growMax 
40331     * @hide 
40332     */
40333     /**
40334      * @hide
40335      * @method autoSize
40336      */
40337 });/*
40338  * Copyright(c) 2010-2012, Roo J Solutions Limited
40339  *
40340  * Licence LGPL
40341  *
40342  */
40343
40344 /**
40345  * @class Roo.form.ComboBoxArray
40346  * @extends Roo.form.TextField
40347  * A facebook style adder... for lists of email / people / countries  etc...
40348  * pick multiple items from a combo box, and shows each one.
40349  *
40350  *  Fred [x]  Brian [x]  [Pick another |v]
40351  *
40352  *
40353  *  For this to work: it needs various extra information
40354  *    - normal combo problay has
40355  *      name, hiddenName
40356  *    + displayField, valueField
40357  *
40358  *    For our purpose...
40359  *
40360  *
40361  *   If we change from 'extends' to wrapping...
40362  *   
40363  *  
40364  *
40365  
40366  
40367  * @constructor
40368  * Create a new ComboBoxArray.
40369  * @param {Object} config Configuration options
40370  */
40371  
40372
40373 Roo.form.ComboBoxArray = function(config)
40374 {
40375     this.addEvents({
40376         /**
40377          * @event beforeremove
40378          * Fires before remove the value from the list
40379              * @param {Roo.form.ComboBoxArray} _self This combo box array
40380              * @param {Roo.form.ComboBoxArray.Item} item removed item
40381              */
40382         'beforeremove' : true,
40383         /**
40384          * @event remove
40385          * Fires when remove the value from the list
40386              * @param {Roo.form.ComboBoxArray} _self This combo box array
40387              * @param {Roo.form.ComboBoxArray.Item} item removed item
40388              */
40389         'remove' : true
40390         
40391         
40392     });
40393     
40394     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40395     
40396     this.items = new Roo.util.MixedCollection(false);
40397     
40398     // construct the child combo...
40399     
40400     
40401     
40402     
40403    
40404     
40405 }
40406
40407  
40408 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40409
40410     /**
40411      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40412      */
40413     
40414     lastData : false,
40415     
40416     // behavies liek a hiddne field
40417     inputType:      'hidden',
40418     /**
40419      * @cfg {Number} width The width of the box that displays the selected element
40420      */ 
40421     width:          300,
40422
40423     
40424     
40425     /**
40426      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40427      */
40428     name : false,
40429     /**
40430      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40431      */
40432     hiddenName : false,
40433     
40434     
40435     // private the array of items that are displayed..
40436     items  : false,
40437     // private - the hidden field el.
40438     hiddenEl : false,
40439     // private - the filed el..
40440     el : false,
40441     
40442     //validateValue : function() { return true; }, // all values are ok!
40443     //onAddClick: function() { },
40444     
40445     onRender : function(ct, position) 
40446     {
40447         
40448         // create the standard hidden element
40449         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40450         
40451         
40452         // give fake names to child combo;
40453         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40454         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40455         
40456         this.combo = Roo.factory(this.combo, Roo.form);
40457         this.combo.onRender(ct, position);
40458         if (typeof(this.combo.width) != 'undefined') {
40459             this.combo.onResize(this.combo.width,0);
40460         }
40461         
40462         this.combo.initEvents();
40463         
40464         // assigned so form know we need to do this..
40465         this.store          = this.combo.store;
40466         this.valueField     = this.combo.valueField;
40467         this.displayField   = this.combo.displayField ;
40468         
40469         
40470         this.combo.wrap.addClass('x-cbarray-grp');
40471         
40472         var cbwrap = this.combo.wrap.createChild(
40473             {tag: 'div', cls: 'x-cbarray-cb'},
40474             this.combo.el.dom
40475         );
40476         
40477              
40478         this.hiddenEl = this.combo.wrap.createChild({
40479             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40480         });
40481         this.el = this.combo.wrap.createChild({
40482             tag: 'input',  type:'hidden' , name: this.name, value : ''
40483         });
40484          //   this.el.dom.removeAttribute("name");
40485         
40486         
40487         this.outerWrap = this.combo.wrap;
40488         this.wrap = cbwrap;
40489         
40490         this.outerWrap.setWidth(this.width);
40491         this.outerWrap.dom.removeChild(this.el.dom);
40492         
40493         this.wrap.dom.appendChild(this.el.dom);
40494         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40495         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40496         
40497         this.combo.trigger.setStyle('position','relative');
40498         this.combo.trigger.setStyle('left', '0px');
40499         this.combo.trigger.setStyle('top', '2px');
40500         
40501         this.combo.el.setStyle('vertical-align', 'text-bottom');
40502         
40503         //this.trigger.setStyle('vertical-align', 'top');
40504         
40505         // this should use the code from combo really... on('add' ....)
40506         if (this.adder) {
40507             
40508         
40509             this.adder = this.outerWrap.createChild(
40510                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40511             var _t = this;
40512             this.adder.on('click', function(e) {
40513                 _t.fireEvent('adderclick', this, e);
40514             }, _t);
40515         }
40516         //var _t = this;
40517         //this.adder.on('click', this.onAddClick, _t);
40518         
40519         
40520         this.combo.on('select', function(cb, rec, ix) {
40521             this.addItem(rec.data);
40522             
40523             cb.setValue('');
40524             cb.el.dom.value = '';
40525             //cb.lastData = rec.data;
40526             // add to list
40527             
40528         }, this);
40529         
40530         
40531     },
40532     
40533     
40534     getName: function()
40535     {
40536         // returns hidden if it's set..
40537         if (!this.rendered) {return ''};
40538         return  this.hiddenName ? this.hiddenName : this.name;
40539         
40540     },
40541     
40542     
40543     onResize: function(w, h){
40544         
40545         return;
40546         // not sure if this is needed..
40547         //this.combo.onResize(w,h);
40548         
40549         if(typeof w != 'number'){
40550             // we do not handle it!?!?
40551             return;
40552         }
40553         var tw = this.combo.trigger.getWidth();
40554         tw += this.addicon ? this.addicon.getWidth() : 0;
40555         tw += this.editicon ? this.editicon.getWidth() : 0;
40556         var x = w - tw;
40557         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40558             
40559         this.combo.trigger.setStyle('left', '0px');
40560         
40561         if(this.list && this.listWidth === undefined){
40562             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40563             this.list.setWidth(lw);
40564             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40565         }
40566         
40567     
40568         
40569     },
40570     
40571     addItem: function(rec)
40572     {
40573         var valueField = this.combo.valueField;
40574         var displayField = this.combo.displayField;
40575         if (this.items.indexOfKey(rec[valueField]) > -1) {
40576             //console.log("GOT " + rec.data.id);
40577             return;
40578         }
40579         
40580         var x = new Roo.form.ComboBoxArray.Item({
40581             //id : rec[this.idField],
40582             data : rec,
40583             displayField : displayField ,
40584             tipField : displayField ,
40585             cb : this
40586         });
40587         // use the 
40588         this.items.add(rec[valueField],x);
40589         // add it before the element..
40590         this.updateHiddenEl();
40591         x.render(this.outerWrap, this.wrap.dom);
40592         // add the image handler..
40593     },
40594     
40595     updateHiddenEl : function()
40596     {
40597         this.validate();
40598         if (!this.hiddenEl) {
40599             return;
40600         }
40601         var ar = [];
40602         var idField = this.combo.valueField;
40603         
40604         this.items.each(function(f) {
40605             ar.push(f.data[idField]);
40606            
40607         });
40608         this.hiddenEl.dom.value = ar.join(',');
40609         this.validate();
40610     },
40611     
40612     reset : function()
40613     {
40614         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40615         this.items.each(function(f) {
40616            f.remove(); 
40617         });
40618         this.el.dom.value = '';
40619         if (this.hiddenEl) {
40620             this.hiddenEl.dom.value = '';
40621         }
40622         
40623     },
40624     getValue: function()
40625     {
40626         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40627     },
40628     setValue: function(v) // not a valid action - must use addItems..
40629     {
40630          
40631         this.reset();
40632         
40633         
40634         
40635         if (this.store.isLocal && (typeof(v) == 'string')) {
40636             // then we can use the store to find the values..
40637             // comma seperated at present.. this needs to allow JSON based encoding..
40638             this.hiddenEl.value  = v;
40639             var v_ar = [];
40640             Roo.each(v.split(','), function(k) {
40641                 Roo.log("CHECK " + this.valueField + ',' + k);
40642                 var li = this.store.query(this.valueField, k);
40643                 if (!li.length) {
40644                     return;
40645                 }
40646                 var add = {};
40647                 add[this.valueField] = k;
40648                 add[this.displayField] = li.item(0).data[this.displayField];
40649                 
40650                 this.addItem(add);
40651             }, this) 
40652              
40653         }
40654         if (typeof(v) == 'object' ) {
40655             // then let's assume it's an array of objects..
40656             Roo.each(v, function(l) {
40657                 this.addItem(l);
40658             }, this);
40659              
40660         }
40661         
40662         
40663     },
40664     setFromData: function(v)
40665     {
40666         // this recieves an object, if setValues is called.
40667         this.reset();
40668         this.el.dom.value = v[this.displayField];
40669         this.hiddenEl.dom.value = v[this.valueField];
40670         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40671             return;
40672         }
40673         var kv = v[this.valueField];
40674         var dv = v[this.displayField];
40675         kv = typeof(kv) != 'string' ? '' : kv;
40676         dv = typeof(dv) != 'string' ? '' : dv;
40677         
40678         
40679         var keys = kv.split(',');
40680         var display = dv.split(',');
40681         for (var i = 0 ; i < keys.length; i++) {
40682             
40683             add = {};
40684             add[this.valueField] = keys[i];
40685             add[this.displayField] = display[i];
40686             this.addItem(add);
40687         }
40688       
40689         
40690     },
40691     
40692     /**
40693      * Validates the combox array value
40694      * @return {Boolean} True if the value is valid, else false
40695      */
40696     validate : function(){
40697         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40698             this.clearInvalid();
40699             return true;
40700         }
40701         return false;
40702     },
40703     
40704     validateValue : function(value){
40705         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40706         
40707     },
40708     
40709     /*@
40710      * overide
40711      * 
40712      */
40713     isDirty : function() {
40714         if(this.disabled) {
40715             return false;
40716         }
40717         
40718         try {
40719             var d = Roo.decode(String(this.originalValue));
40720         } catch (e) {
40721             return String(this.getValue()) !== String(this.originalValue);
40722         }
40723         
40724         var originalValue = [];
40725         
40726         for (var i = 0; i < d.length; i++){
40727             originalValue.push(d[i][this.valueField]);
40728         }
40729         
40730         return String(this.getValue()) !== String(originalValue.join(','));
40731         
40732     }
40733     
40734 });
40735
40736
40737
40738 /**
40739  * @class Roo.form.ComboBoxArray.Item
40740  * @extends Roo.BoxComponent
40741  * A selected item in the list
40742  *  Fred [x]  Brian [x]  [Pick another |v]
40743  * 
40744  * @constructor
40745  * Create a new item.
40746  * @param {Object} config Configuration options
40747  */
40748  
40749 Roo.form.ComboBoxArray.Item = function(config) {
40750     config.id = Roo.id();
40751     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40752 }
40753
40754 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40755     data : {},
40756     cb: false,
40757     displayField : false,
40758     tipField : false,
40759     
40760     
40761     defaultAutoCreate : {
40762         tag: 'div',
40763         cls: 'x-cbarray-item',
40764         cn : [ 
40765             { tag: 'div' },
40766             {
40767                 tag: 'img',
40768                 width:16,
40769                 height : 16,
40770                 src : Roo.BLANK_IMAGE_URL ,
40771                 align: 'center'
40772             }
40773         ]
40774         
40775     },
40776     
40777  
40778     onRender : function(ct, position)
40779     {
40780         Roo.form.Field.superclass.onRender.call(this, ct, position);
40781         
40782         if(!this.el){
40783             var cfg = this.getAutoCreate();
40784             this.el = ct.createChild(cfg, position);
40785         }
40786         
40787         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40788         
40789         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40790             this.cb.renderer(this.data) :
40791             String.format('{0}',this.data[this.displayField]);
40792         
40793             
40794         this.el.child('div').dom.setAttribute('qtip',
40795                         String.format('{0}',this.data[this.tipField])
40796         );
40797         
40798         this.el.child('img').on('click', this.remove, this);
40799         
40800     },
40801    
40802     remove : function()
40803     {
40804         if(this.cb.disabled){
40805             return;
40806         }
40807         
40808         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40809             this.cb.items.remove(this);
40810             this.el.child('img').un('click', this.remove, this);
40811             this.el.remove();
40812             this.cb.updateHiddenEl();
40813
40814             this.cb.fireEvent('remove', this.cb, this);
40815         }
40816         
40817     }
40818 });/*
40819  * Based on:
40820  * Ext JS Library 1.1.1
40821  * Copyright(c) 2006-2007, Ext JS, LLC.
40822  *
40823  * Originally Released Under LGPL - original licence link has changed is not relivant.
40824  *
40825  * Fork - LGPL
40826  * <script type="text/javascript">
40827  */
40828 /**
40829  * @class Roo.form.Checkbox
40830  * @extends Roo.form.Field
40831  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40832  * @constructor
40833  * Creates a new Checkbox
40834  * @param {Object} config Configuration options
40835  */
40836 Roo.form.Checkbox = function(config){
40837     Roo.form.Checkbox.superclass.constructor.call(this, config);
40838     this.addEvents({
40839         /**
40840          * @event check
40841          * Fires when the checkbox is checked or unchecked.
40842              * @param {Roo.form.Checkbox} this This checkbox
40843              * @param {Boolean} checked The new checked value
40844              */
40845         check : true
40846     });
40847 };
40848
40849 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40850     /**
40851      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40852      */
40853     focusClass : undefined,
40854     /**
40855      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40856      */
40857     fieldClass: "x-form-field",
40858     /**
40859      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40860      */
40861     checked: false,
40862     /**
40863      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40864      * {tag: "input", type: "checkbox", autocomplete: "off"})
40865      */
40866     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40867     /**
40868      * @cfg {String} boxLabel The text that appears beside the checkbox
40869      */
40870     boxLabel : "",
40871     /**
40872      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40873      */  
40874     inputValue : '1',
40875     /**
40876      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40877      */
40878      valueOff: '0', // value when not checked..
40879
40880     actionMode : 'viewEl', 
40881     //
40882     // private
40883     itemCls : 'x-menu-check-item x-form-item',
40884     groupClass : 'x-menu-group-item',
40885     inputType : 'hidden',
40886     
40887     
40888     inSetChecked: false, // check that we are not calling self...
40889     
40890     inputElement: false, // real input element?
40891     basedOn: false, // ????
40892     
40893     isFormField: true, // not sure where this is needed!!!!
40894
40895     onResize : function(){
40896         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40897         if(!this.boxLabel){
40898             this.el.alignTo(this.wrap, 'c-c');
40899         }
40900     },
40901
40902     initEvents : function(){
40903         Roo.form.Checkbox.superclass.initEvents.call(this);
40904         this.el.on("click", this.onClick,  this);
40905         this.el.on("change", this.onClick,  this);
40906     },
40907
40908
40909     getResizeEl : function(){
40910         return this.wrap;
40911     },
40912
40913     getPositionEl : function(){
40914         return this.wrap;
40915     },
40916
40917     // private
40918     onRender : function(ct, position){
40919         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40920         /*
40921         if(this.inputValue !== undefined){
40922             this.el.dom.value = this.inputValue;
40923         }
40924         */
40925         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40926         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40927         var viewEl = this.wrap.createChild({ 
40928             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40929         this.viewEl = viewEl;   
40930         this.wrap.on('click', this.onClick,  this); 
40931         
40932         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40933         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40934         
40935         
40936         
40937         if(this.boxLabel){
40938             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40939         //    viewEl.on('click', this.onClick,  this); 
40940         }
40941         //if(this.checked){
40942             this.setChecked(this.checked);
40943         //}else{
40944             //this.checked = this.el.dom;
40945         //}
40946
40947     },
40948
40949     // private
40950     initValue : Roo.emptyFn,
40951
40952     /**
40953      * Returns the checked state of the checkbox.
40954      * @return {Boolean} True if checked, else false
40955      */
40956     getValue : function(){
40957         if(this.el){
40958             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40959         }
40960         return this.valueOff;
40961         
40962     },
40963
40964         // private
40965     onClick : function(){ 
40966         if (this.disabled) {
40967             return;
40968         }
40969         this.setChecked(!this.checked);
40970
40971         //if(this.el.dom.checked != this.checked){
40972         //    this.setValue(this.el.dom.checked);
40973        // }
40974     },
40975
40976     /**
40977      * Sets the checked state of the checkbox.
40978      * On is always based on a string comparison between inputValue and the param.
40979      * @param {Boolean/String} value - the value to set 
40980      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40981      */
40982     setValue : function(v,suppressEvent){
40983         
40984         
40985         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40986         //if(this.el && this.el.dom){
40987         //    this.el.dom.checked = this.checked;
40988         //    this.el.dom.defaultChecked = this.checked;
40989         //}
40990         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40991         //this.fireEvent("check", this, this.checked);
40992     },
40993     // private..
40994     setChecked : function(state,suppressEvent)
40995     {
40996         if (this.inSetChecked) {
40997             this.checked = state;
40998             return;
40999         }
41000         
41001     
41002         if(this.wrap){
41003             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41004         }
41005         this.checked = state;
41006         if(suppressEvent !== true){
41007             this.fireEvent('check', this, state);
41008         }
41009         this.inSetChecked = true;
41010         this.el.dom.value = state ? this.inputValue : this.valueOff;
41011         this.inSetChecked = false;
41012         
41013     },
41014     // handle setting of hidden value by some other method!!?!?
41015     setFromHidden: function()
41016     {
41017         if(!this.el){
41018             return;
41019         }
41020         //console.log("SET FROM HIDDEN");
41021         //alert('setFrom hidden');
41022         this.setValue(this.el.dom.value);
41023     },
41024     
41025     onDestroy : function()
41026     {
41027         if(this.viewEl){
41028             Roo.get(this.viewEl).remove();
41029         }
41030          
41031         Roo.form.Checkbox.superclass.onDestroy.call(this);
41032     }
41033
41034 });/*
41035  * Based on:
41036  * Ext JS Library 1.1.1
41037  * Copyright(c) 2006-2007, Ext JS, LLC.
41038  *
41039  * Originally Released Under LGPL - original licence link has changed is not relivant.
41040  *
41041  * Fork - LGPL
41042  * <script type="text/javascript">
41043  */
41044  
41045 /**
41046  * @class Roo.form.Radio
41047  * @extends Roo.form.Checkbox
41048  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41049  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41050  * @constructor
41051  * Creates a new Radio
41052  * @param {Object} config Configuration options
41053  */
41054 Roo.form.Radio = function(){
41055     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41056 };
41057 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41058     inputType: 'radio',
41059
41060     /**
41061      * If this radio is part of a group, it will return the selected value
41062      * @return {String}
41063      */
41064     getGroupValue : function(){
41065         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41066     },
41067     
41068     
41069     onRender : function(ct, position){
41070         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41071         
41072         if(this.inputValue !== undefined){
41073             this.el.dom.value = this.inputValue;
41074         }
41075          
41076         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41077         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41078         //var viewEl = this.wrap.createChild({ 
41079         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41080         //this.viewEl = viewEl;   
41081         //this.wrap.on('click', this.onClick,  this); 
41082         
41083         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41084         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41085         
41086         
41087         
41088         if(this.boxLabel){
41089             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41090         //    viewEl.on('click', this.onClick,  this); 
41091         }
41092          if(this.checked){
41093             this.el.dom.checked =   'checked' ;
41094         }
41095          
41096     } 
41097     
41098     
41099 });//<script type="text/javascript">
41100
41101 /*
41102  * Based  Ext JS Library 1.1.1
41103  * Copyright(c) 2006-2007, Ext JS, LLC.
41104  * LGPL
41105  *
41106  */
41107  
41108 /**
41109  * @class Roo.HtmlEditorCore
41110  * @extends Roo.Component
41111  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41112  *
41113  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41114  */
41115
41116 Roo.HtmlEditorCore = function(config){
41117     
41118     
41119     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41120     
41121     
41122     this.addEvents({
41123         /**
41124          * @event initialize
41125          * Fires when the editor is fully initialized (including the iframe)
41126          * @param {Roo.HtmlEditorCore} this
41127          */
41128         initialize: true,
41129         /**
41130          * @event activate
41131          * Fires when the editor is first receives the focus. Any insertion must wait
41132          * until after this event.
41133          * @param {Roo.HtmlEditorCore} this
41134          */
41135         activate: true,
41136          /**
41137          * @event beforesync
41138          * Fires before the textarea is updated with content from the editor iframe. Return false
41139          * to cancel the sync.
41140          * @param {Roo.HtmlEditorCore} this
41141          * @param {String} html
41142          */
41143         beforesync: true,
41144          /**
41145          * @event beforepush
41146          * Fires before the iframe editor is updated with content from the textarea. Return false
41147          * to cancel the push.
41148          * @param {Roo.HtmlEditorCore} this
41149          * @param {String} html
41150          */
41151         beforepush: true,
41152          /**
41153          * @event sync
41154          * Fires when the textarea is updated with content from the editor iframe.
41155          * @param {Roo.HtmlEditorCore} this
41156          * @param {String} html
41157          */
41158         sync: true,
41159          /**
41160          * @event push
41161          * Fires when the iframe editor is updated with content from the textarea.
41162          * @param {Roo.HtmlEditorCore} this
41163          * @param {String} html
41164          */
41165         push: true,
41166         
41167         /**
41168          * @event editorevent
41169          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41170          * @param {Roo.HtmlEditorCore} this
41171          */
41172         editorevent: true
41173         
41174     });
41175     
41176     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41177     
41178     // defaults : white / black...
41179     this.applyBlacklists();
41180     
41181     
41182     
41183 };
41184
41185
41186 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41187
41188
41189      /**
41190      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41191      */
41192     
41193     owner : false,
41194     
41195      /**
41196      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41197      *                        Roo.resizable.
41198      */
41199     resizable : false,
41200      /**
41201      * @cfg {Number} height (in pixels)
41202      */   
41203     height: 300,
41204    /**
41205      * @cfg {Number} width (in pixels)
41206      */   
41207     width: 500,
41208     
41209     /**
41210      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41211      * 
41212      */
41213     stylesheets: false,
41214     
41215     // id of frame..
41216     frameId: false,
41217     
41218     // private properties
41219     validationEvent : false,
41220     deferHeight: true,
41221     initialized : false,
41222     activated : false,
41223     sourceEditMode : false,
41224     onFocus : Roo.emptyFn,
41225     iframePad:3,
41226     hideMode:'offsets',
41227     
41228     clearUp: true,
41229     
41230     // blacklist + whitelisted elements..
41231     black: false,
41232     white: false,
41233      
41234     
41235
41236     /**
41237      * Protected method that will not generally be called directly. It
41238      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41239      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41240      */
41241     getDocMarkup : function(){
41242         // body styles..
41243         var st = '';
41244         
41245         // inherit styels from page...?? 
41246         if (this.stylesheets === false) {
41247             
41248             Roo.get(document.head).select('style').each(function(node) {
41249                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41250             });
41251             
41252             Roo.get(document.head).select('link').each(function(node) { 
41253                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41254             });
41255             
41256         } else if (!this.stylesheets.length) {
41257                 // simple..
41258                 st = '<style type="text/css">' +
41259                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41260                    '</style>';
41261         } else { 
41262             
41263         }
41264         
41265         st +=  '<style type="text/css">' +
41266             'IMG { cursor: pointer } ' +
41267         '</style>';
41268
41269         
41270         return '<html><head>' + st  +
41271             //<style type="text/css">' +
41272             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41273             //'</style>' +
41274             ' </head><body class="roo-htmleditor-body"></body></html>';
41275     },
41276
41277     // private
41278     onRender : function(ct, position)
41279     {
41280         var _t = this;
41281         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41282         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41283         
41284         
41285         this.el.dom.style.border = '0 none';
41286         this.el.dom.setAttribute('tabIndex', -1);
41287         this.el.addClass('x-hidden hide');
41288         
41289         
41290         
41291         if(Roo.isIE){ // fix IE 1px bogus margin
41292             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41293         }
41294        
41295         
41296         this.frameId = Roo.id();
41297         
41298          
41299         
41300         var iframe = this.owner.wrap.createChild({
41301             tag: 'iframe',
41302             cls: 'form-control', // bootstrap..
41303             id: this.frameId,
41304             name: this.frameId,
41305             frameBorder : 'no',
41306             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41307         }, this.el
41308         );
41309         
41310         
41311         this.iframe = iframe.dom;
41312
41313          this.assignDocWin();
41314         
41315         this.doc.designMode = 'on';
41316        
41317         this.doc.open();
41318         this.doc.write(this.getDocMarkup());
41319         this.doc.close();
41320
41321         
41322         var task = { // must defer to wait for browser to be ready
41323             run : function(){
41324                 //console.log("run task?" + this.doc.readyState);
41325                 this.assignDocWin();
41326                 if(this.doc.body || this.doc.readyState == 'complete'){
41327                     try {
41328                         this.doc.designMode="on";
41329                     } catch (e) {
41330                         return;
41331                     }
41332                     Roo.TaskMgr.stop(task);
41333                     this.initEditor.defer(10, this);
41334                 }
41335             },
41336             interval : 10,
41337             duration: 10000,
41338             scope: this
41339         };
41340         Roo.TaskMgr.start(task);
41341
41342     },
41343
41344     // private
41345     onResize : function(w, h)
41346     {
41347          Roo.log('resize: ' +w + ',' + h );
41348         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41349         if(!this.iframe){
41350             return;
41351         }
41352         if(typeof w == 'number'){
41353             
41354             this.iframe.style.width = w + 'px';
41355         }
41356         if(typeof h == 'number'){
41357             
41358             this.iframe.style.height = h + 'px';
41359             if(this.doc){
41360                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41361             }
41362         }
41363         
41364     },
41365
41366     /**
41367      * Toggles the editor between standard and source edit mode.
41368      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41369      */
41370     toggleSourceEdit : function(sourceEditMode){
41371         
41372         this.sourceEditMode = sourceEditMode === true;
41373         
41374         if(this.sourceEditMode){
41375  
41376             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41377             
41378         }else{
41379             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41380             //this.iframe.className = '';
41381             this.deferFocus();
41382         }
41383         //this.setSize(this.owner.wrap.getSize());
41384         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41385     },
41386
41387     
41388   
41389
41390     /**
41391      * Protected method that will not generally be called directly. If you need/want
41392      * custom HTML cleanup, this is the method you should override.
41393      * @param {String} html The HTML to be cleaned
41394      * return {String} The cleaned HTML
41395      */
41396     cleanHtml : function(html){
41397         html = String(html);
41398         if(html.length > 5){
41399             if(Roo.isSafari){ // strip safari nonsense
41400                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41401             }
41402         }
41403         if(html == '&nbsp;'){
41404             html = '';
41405         }
41406         return html;
41407     },
41408
41409     /**
41410      * HTML Editor -> Textarea
41411      * Protected method that will not generally be called directly. Syncs the contents
41412      * of the editor iframe with the textarea.
41413      */
41414     syncValue : function(){
41415         if(this.initialized){
41416             var bd = (this.doc.body || this.doc.documentElement);
41417             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41418             var html = bd.innerHTML;
41419             if(Roo.isSafari){
41420                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41421                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41422                 if(m && m[1]){
41423                     html = '<div style="'+m[0]+'">' + html + '</div>';
41424                 }
41425             }
41426             html = this.cleanHtml(html);
41427             // fix up the special chars.. normaly like back quotes in word...
41428             // however we do not want to do this with chinese..
41429             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41430                 var cc = b.charCodeAt();
41431                 if (
41432                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41433                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41434                     (cc >= 0xf900 && cc < 0xfb00 )
41435                 ) {
41436                         return b;
41437                 }
41438                 return "&#"+cc+";" 
41439             });
41440             if(this.owner.fireEvent('beforesync', this, html) !== false){
41441                 this.el.dom.value = html;
41442                 this.owner.fireEvent('sync', this, html);
41443             }
41444         }
41445     },
41446
41447     /**
41448      * Protected method that will not generally be called directly. Pushes the value of the textarea
41449      * into the iframe editor.
41450      */
41451     pushValue : function(){
41452         if(this.initialized){
41453             var v = this.el.dom.value.trim();
41454             
41455 //            if(v.length < 1){
41456 //                v = '&#160;';
41457 //            }
41458             
41459             if(this.owner.fireEvent('beforepush', this, v) !== false){
41460                 var d = (this.doc.body || this.doc.documentElement);
41461                 d.innerHTML = v;
41462                 this.cleanUpPaste();
41463                 this.el.dom.value = d.innerHTML;
41464                 this.owner.fireEvent('push', this, v);
41465             }
41466         }
41467     },
41468
41469     // private
41470     deferFocus : function(){
41471         this.focus.defer(10, this);
41472     },
41473
41474     // doc'ed in Field
41475     focus : function(){
41476         if(this.win && !this.sourceEditMode){
41477             this.win.focus();
41478         }else{
41479             this.el.focus();
41480         }
41481     },
41482     
41483     assignDocWin: function()
41484     {
41485         var iframe = this.iframe;
41486         
41487          if(Roo.isIE){
41488             this.doc = iframe.contentWindow.document;
41489             this.win = iframe.contentWindow;
41490         } else {
41491 //            if (!Roo.get(this.frameId)) {
41492 //                return;
41493 //            }
41494 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41495 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41496             
41497             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41498                 return;
41499             }
41500             
41501             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41502             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41503         }
41504     },
41505     
41506     // private
41507     initEditor : function(){
41508         //console.log("INIT EDITOR");
41509         this.assignDocWin();
41510         
41511         
41512         
41513         this.doc.designMode="on";
41514         this.doc.open();
41515         this.doc.write(this.getDocMarkup());
41516         this.doc.close();
41517         
41518         var dbody = (this.doc.body || this.doc.documentElement);
41519         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41520         // this copies styles from the containing element into thsi one..
41521         // not sure why we need all of this..
41522         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41523         
41524         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41525         //ss['background-attachment'] = 'fixed'; // w3c
41526         dbody.bgProperties = 'fixed'; // ie
41527         //Roo.DomHelper.applyStyles(dbody, ss);
41528         Roo.EventManager.on(this.doc, {
41529             //'mousedown': this.onEditorEvent,
41530             'mouseup': this.onEditorEvent,
41531             'dblclick': this.onEditorEvent,
41532             'click': this.onEditorEvent,
41533             'keyup': this.onEditorEvent,
41534             buffer:100,
41535             scope: this
41536         });
41537         if(Roo.isGecko){
41538             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41539         }
41540         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41541             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41542         }
41543         this.initialized = true;
41544
41545         this.owner.fireEvent('initialize', this);
41546         this.pushValue();
41547     },
41548
41549     // private
41550     onDestroy : function(){
41551         
41552         
41553         
41554         if(this.rendered){
41555             
41556             //for (var i =0; i < this.toolbars.length;i++) {
41557             //    // fixme - ask toolbars for heights?
41558             //    this.toolbars[i].onDestroy();
41559            // }
41560             
41561             //this.wrap.dom.innerHTML = '';
41562             //this.wrap.remove();
41563         }
41564     },
41565
41566     // private
41567     onFirstFocus : function(){
41568         
41569         this.assignDocWin();
41570         
41571         
41572         this.activated = true;
41573          
41574     
41575         if(Roo.isGecko){ // prevent silly gecko errors
41576             this.win.focus();
41577             var s = this.win.getSelection();
41578             if(!s.focusNode || s.focusNode.nodeType != 3){
41579                 var r = s.getRangeAt(0);
41580                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41581                 r.collapse(true);
41582                 this.deferFocus();
41583             }
41584             try{
41585                 this.execCmd('useCSS', true);
41586                 this.execCmd('styleWithCSS', false);
41587             }catch(e){}
41588         }
41589         this.owner.fireEvent('activate', this);
41590     },
41591
41592     // private
41593     adjustFont: function(btn){
41594         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41595         //if(Roo.isSafari){ // safari
41596         //    adjust *= 2;
41597        // }
41598         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41599         if(Roo.isSafari){ // safari
41600             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41601             v =  (v < 10) ? 10 : v;
41602             v =  (v > 48) ? 48 : v;
41603             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41604             
41605         }
41606         
41607         
41608         v = Math.max(1, v+adjust);
41609         
41610         this.execCmd('FontSize', v  );
41611     },
41612
41613     onEditorEvent : function(e)
41614     {
41615         this.owner.fireEvent('editorevent', this, e);
41616       //  this.updateToolbar();
41617         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41618     },
41619
41620     insertTag : function(tg)
41621     {
41622         // could be a bit smarter... -> wrap the current selected tRoo..
41623         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41624             
41625             range = this.createRange(this.getSelection());
41626             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41627             wrappingNode.appendChild(range.extractContents());
41628             range.insertNode(wrappingNode);
41629
41630             return;
41631             
41632             
41633             
41634         }
41635         this.execCmd("formatblock",   tg);
41636         
41637     },
41638     
41639     insertText : function(txt)
41640     {
41641         
41642         
41643         var range = this.createRange();
41644         range.deleteContents();
41645                //alert(Sender.getAttribute('label'));
41646                
41647         range.insertNode(this.doc.createTextNode(txt));
41648     } ,
41649     
41650      
41651
41652     /**
41653      * Executes a Midas editor command on the editor document and performs necessary focus and
41654      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41655      * @param {String} cmd The Midas command
41656      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41657      */
41658     relayCmd : function(cmd, value){
41659         this.win.focus();
41660         this.execCmd(cmd, value);
41661         this.owner.fireEvent('editorevent', this);
41662         //this.updateToolbar();
41663         this.owner.deferFocus();
41664     },
41665
41666     /**
41667      * Executes a Midas editor command directly on the editor document.
41668      * For visual commands, you should use {@link #relayCmd} instead.
41669      * <b>This should only be called after the editor is initialized.</b>
41670      * @param {String} cmd The Midas command
41671      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41672      */
41673     execCmd : function(cmd, value){
41674         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41675         this.syncValue();
41676     },
41677  
41678  
41679    
41680     /**
41681      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41682      * to insert tRoo.
41683      * @param {String} text | dom node.. 
41684      */
41685     insertAtCursor : function(text)
41686     {
41687         
41688         
41689         
41690         if(!this.activated){
41691             return;
41692         }
41693         /*
41694         if(Roo.isIE){
41695             this.win.focus();
41696             var r = this.doc.selection.createRange();
41697             if(r){
41698                 r.collapse(true);
41699                 r.pasteHTML(text);
41700                 this.syncValue();
41701                 this.deferFocus();
41702             
41703             }
41704             return;
41705         }
41706         */
41707         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41708             this.win.focus();
41709             
41710             
41711             // from jquery ui (MIT licenced)
41712             var range, node;
41713             var win = this.win;
41714             
41715             if (win.getSelection && win.getSelection().getRangeAt) {
41716                 range = win.getSelection().getRangeAt(0);
41717                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41718                 range.insertNode(node);
41719             } else if (win.document.selection && win.document.selection.createRange) {
41720                 // no firefox support
41721                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41722                 win.document.selection.createRange().pasteHTML(txt);
41723             } else {
41724                 // no firefox support
41725                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41726                 this.execCmd('InsertHTML', txt);
41727             } 
41728             
41729             this.syncValue();
41730             
41731             this.deferFocus();
41732         }
41733     },
41734  // private
41735     mozKeyPress : function(e){
41736         if(e.ctrlKey){
41737             var c = e.getCharCode(), cmd;
41738           
41739             if(c > 0){
41740                 c = String.fromCharCode(c).toLowerCase();
41741                 switch(c){
41742                     case 'b':
41743                         cmd = 'bold';
41744                         break;
41745                     case 'i':
41746                         cmd = 'italic';
41747                         break;
41748                     
41749                     case 'u':
41750                         cmd = 'underline';
41751                         break;
41752                     
41753                     case 'v':
41754                         this.cleanUpPaste.defer(100, this);
41755                         return;
41756                         
41757                 }
41758                 if(cmd){
41759                     this.win.focus();
41760                     this.execCmd(cmd);
41761                     this.deferFocus();
41762                     e.preventDefault();
41763                 }
41764                 
41765             }
41766         }
41767     },
41768
41769     // private
41770     fixKeys : function(){ // load time branching for fastest keydown performance
41771         if(Roo.isIE){
41772             return function(e){
41773                 var k = e.getKey(), r;
41774                 if(k == e.TAB){
41775                     e.stopEvent();
41776                     r = this.doc.selection.createRange();
41777                     if(r){
41778                         r.collapse(true);
41779                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41780                         this.deferFocus();
41781                     }
41782                     return;
41783                 }
41784                 
41785                 if(k == e.ENTER){
41786                     r = this.doc.selection.createRange();
41787                     if(r){
41788                         var target = r.parentElement();
41789                         if(!target || target.tagName.toLowerCase() != 'li'){
41790                             e.stopEvent();
41791                             r.pasteHTML('<br />');
41792                             r.collapse(false);
41793                             r.select();
41794                         }
41795                     }
41796                 }
41797                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41798                     this.cleanUpPaste.defer(100, this);
41799                     return;
41800                 }
41801                 
41802                 
41803             };
41804         }else if(Roo.isOpera){
41805             return function(e){
41806                 var k = e.getKey();
41807                 if(k == e.TAB){
41808                     e.stopEvent();
41809                     this.win.focus();
41810                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41811                     this.deferFocus();
41812                 }
41813                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41814                     this.cleanUpPaste.defer(100, this);
41815                     return;
41816                 }
41817                 
41818             };
41819         }else if(Roo.isSafari){
41820             return function(e){
41821                 var k = e.getKey();
41822                 
41823                 if(k == e.TAB){
41824                     e.stopEvent();
41825                     this.execCmd('InsertText','\t');
41826                     this.deferFocus();
41827                     return;
41828                 }
41829                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41830                     this.cleanUpPaste.defer(100, this);
41831                     return;
41832                 }
41833                 
41834              };
41835         }
41836     }(),
41837     
41838     getAllAncestors: function()
41839     {
41840         var p = this.getSelectedNode();
41841         var a = [];
41842         if (!p) {
41843             a.push(p); // push blank onto stack..
41844             p = this.getParentElement();
41845         }
41846         
41847         
41848         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41849             a.push(p);
41850             p = p.parentNode;
41851         }
41852         a.push(this.doc.body);
41853         return a;
41854     },
41855     lastSel : false,
41856     lastSelNode : false,
41857     
41858     
41859     getSelection : function() 
41860     {
41861         this.assignDocWin();
41862         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41863     },
41864     
41865     getSelectedNode: function() 
41866     {
41867         // this may only work on Gecko!!!
41868         
41869         // should we cache this!!!!
41870         
41871         
41872         
41873          
41874         var range = this.createRange(this.getSelection()).cloneRange();
41875         
41876         if (Roo.isIE) {
41877             var parent = range.parentElement();
41878             while (true) {
41879                 var testRange = range.duplicate();
41880                 testRange.moveToElementText(parent);
41881                 if (testRange.inRange(range)) {
41882                     break;
41883                 }
41884                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41885                     break;
41886                 }
41887                 parent = parent.parentElement;
41888             }
41889             return parent;
41890         }
41891         
41892         // is ancestor a text element.
41893         var ac =  range.commonAncestorContainer;
41894         if (ac.nodeType == 3) {
41895             ac = ac.parentNode;
41896         }
41897         
41898         var ar = ac.childNodes;
41899          
41900         var nodes = [];
41901         var other_nodes = [];
41902         var has_other_nodes = false;
41903         for (var i=0;i<ar.length;i++) {
41904             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41905                 continue;
41906             }
41907             // fullly contained node.
41908             
41909             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41910                 nodes.push(ar[i]);
41911                 continue;
41912             }
41913             
41914             // probably selected..
41915             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41916                 other_nodes.push(ar[i]);
41917                 continue;
41918             }
41919             // outer..
41920             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41921                 continue;
41922             }
41923             
41924             
41925             has_other_nodes = true;
41926         }
41927         if (!nodes.length && other_nodes.length) {
41928             nodes= other_nodes;
41929         }
41930         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41931             return false;
41932         }
41933         
41934         return nodes[0];
41935     },
41936     createRange: function(sel)
41937     {
41938         // this has strange effects when using with 
41939         // top toolbar - not sure if it's a great idea.
41940         //this.editor.contentWindow.focus();
41941         if (typeof sel != "undefined") {
41942             try {
41943                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41944             } catch(e) {
41945                 return this.doc.createRange();
41946             }
41947         } else {
41948             return this.doc.createRange();
41949         }
41950     },
41951     getParentElement: function()
41952     {
41953         
41954         this.assignDocWin();
41955         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41956         
41957         var range = this.createRange(sel);
41958          
41959         try {
41960             var p = range.commonAncestorContainer;
41961             while (p.nodeType == 3) { // text node
41962                 p = p.parentNode;
41963             }
41964             return p;
41965         } catch (e) {
41966             return null;
41967         }
41968     
41969     },
41970     /***
41971      *
41972      * Range intersection.. the hard stuff...
41973      *  '-1' = before
41974      *  '0' = hits..
41975      *  '1' = after.
41976      *         [ -- selected range --- ]
41977      *   [fail]                        [fail]
41978      *
41979      *    basically..
41980      *      if end is before start or  hits it. fail.
41981      *      if start is after end or hits it fail.
41982      *
41983      *   if either hits (but other is outside. - then it's not 
41984      *   
41985      *    
41986      **/
41987     
41988     
41989     // @see http://www.thismuchiknow.co.uk/?p=64.
41990     rangeIntersectsNode : function(range, node)
41991     {
41992         var nodeRange = node.ownerDocument.createRange();
41993         try {
41994             nodeRange.selectNode(node);
41995         } catch (e) {
41996             nodeRange.selectNodeContents(node);
41997         }
41998     
41999         var rangeStartRange = range.cloneRange();
42000         rangeStartRange.collapse(true);
42001     
42002         var rangeEndRange = range.cloneRange();
42003         rangeEndRange.collapse(false);
42004     
42005         var nodeStartRange = nodeRange.cloneRange();
42006         nodeStartRange.collapse(true);
42007     
42008         var nodeEndRange = nodeRange.cloneRange();
42009         nodeEndRange.collapse(false);
42010     
42011         return rangeStartRange.compareBoundaryPoints(
42012                  Range.START_TO_START, nodeEndRange) == -1 &&
42013                rangeEndRange.compareBoundaryPoints(
42014                  Range.START_TO_START, nodeStartRange) == 1;
42015         
42016          
42017     },
42018     rangeCompareNode : function(range, node)
42019     {
42020         var nodeRange = node.ownerDocument.createRange();
42021         try {
42022             nodeRange.selectNode(node);
42023         } catch (e) {
42024             nodeRange.selectNodeContents(node);
42025         }
42026         
42027         
42028         range.collapse(true);
42029     
42030         nodeRange.collapse(true);
42031      
42032         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42033         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42034          
42035         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42036         
42037         var nodeIsBefore   =  ss == 1;
42038         var nodeIsAfter    = ee == -1;
42039         
42040         if (nodeIsBefore && nodeIsAfter)
42041             return 0; // outer
42042         if (!nodeIsBefore && nodeIsAfter)
42043             return 1; //right trailed.
42044         
42045         if (nodeIsBefore && !nodeIsAfter)
42046             return 2;  // left trailed.
42047         // fully contined.
42048         return 3;
42049     },
42050
42051     // private? - in a new class?
42052     cleanUpPaste :  function()
42053     {
42054         // cleans up the whole document..
42055         Roo.log('cleanuppaste');
42056         
42057         this.cleanUpChildren(this.doc.body);
42058         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42059         if (clean != this.doc.body.innerHTML) {
42060             this.doc.body.innerHTML = clean;
42061         }
42062         
42063     },
42064     
42065     cleanWordChars : function(input) {// change the chars to hex code
42066         var he = Roo.HtmlEditorCore;
42067         
42068         var output = input;
42069         Roo.each(he.swapCodes, function(sw) { 
42070             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42071             
42072             output = output.replace(swapper, sw[1]);
42073         });
42074         
42075         return output;
42076     },
42077     
42078     
42079     cleanUpChildren : function (n)
42080     {
42081         if (!n.childNodes.length) {
42082             return;
42083         }
42084         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42085            this.cleanUpChild(n.childNodes[i]);
42086         }
42087     },
42088     
42089     
42090         
42091     
42092     cleanUpChild : function (node)
42093     {
42094         var ed = this;
42095         //console.log(node);
42096         if (node.nodeName == "#text") {
42097             // clean up silly Windows -- stuff?
42098             return; 
42099         }
42100         if (node.nodeName == "#comment") {
42101             node.parentNode.removeChild(node);
42102             // clean up silly Windows -- stuff?
42103             return; 
42104         }
42105         var lcname = node.tagName.toLowerCase();
42106         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42107         // whitelist of tags..
42108         
42109         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42110             // remove node.
42111             node.parentNode.removeChild(node);
42112             return;
42113             
42114         }
42115         
42116         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42117         
42118         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42119         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42120         
42121         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42122         //    remove_keep_children = true;
42123         //}
42124         
42125         if (remove_keep_children) {
42126             this.cleanUpChildren(node);
42127             // inserts everything just before this node...
42128             while (node.childNodes.length) {
42129                 var cn = node.childNodes[0];
42130                 node.removeChild(cn);
42131                 node.parentNode.insertBefore(cn, node);
42132             }
42133             node.parentNode.removeChild(node);
42134             return;
42135         }
42136         
42137         if (!node.attributes || !node.attributes.length) {
42138             this.cleanUpChildren(node);
42139             return;
42140         }
42141         
42142         function cleanAttr(n,v)
42143         {
42144             
42145             if (v.match(/^\./) || v.match(/^\//)) {
42146                 return;
42147             }
42148             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42149                 return;
42150             }
42151             if (v.match(/^#/)) {
42152                 return;
42153             }
42154 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42155             node.removeAttribute(n);
42156             
42157         }
42158         
42159         var cwhite = this.cwhite;
42160         var cblack = this.cblack;
42161             
42162         function cleanStyle(n,v)
42163         {
42164             if (v.match(/expression/)) { //XSS?? should we even bother..
42165                 node.removeAttribute(n);
42166                 return;
42167             }
42168             
42169             var parts = v.split(/;/);
42170             var clean = [];
42171             
42172             Roo.each(parts, function(p) {
42173                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42174                 if (!p.length) {
42175                     return true;
42176                 }
42177                 var l = p.split(':').shift().replace(/\s+/g,'');
42178                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42179                 
42180                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42181 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42182                     //node.removeAttribute(n);
42183                     return true;
42184                 }
42185                 //Roo.log()
42186                 // only allow 'c whitelisted system attributes'
42187                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42188 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42189                     //node.removeAttribute(n);
42190                     return true;
42191                 }
42192                 
42193                 
42194                  
42195                 
42196                 clean.push(p);
42197                 return true;
42198             });
42199             if (clean.length) { 
42200                 node.setAttribute(n, clean.join(';'));
42201             } else {
42202                 node.removeAttribute(n);
42203             }
42204             
42205         }
42206         
42207         
42208         for (var i = node.attributes.length-1; i > -1 ; i--) {
42209             var a = node.attributes[i];
42210             //console.log(a);
42211             
42212             if (a.name.toLowerCase().substr(0,2)=='on')  {
42213                 node.removeAttribute(a.name);
42214                 continue;
42215             }
42216             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42217                 node.removeAttribute(a.name);
42218                 continue;
42219             }
42220             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42221                 cleanAttr(a.name,a.value); // fixme..
42222                 continue;
42223             }
42224             if (a.name == 'style') {
42225                 cleanStyle(a.name,a.value);
42226                 continue;
42227             }
42228             /// clean up MS crap..
42229             // tecnically this should be a list of valid class'es..
42230             
42231             
42232             if (a.name == 'class') {
42233                 if (a.value.match(/^Mso/)) {
42234                     node.className = '';
42235                 }
42236                 
42237                 if (a.value.match(/body/)) {
42238                     node.className = '';
42239                 }
42240                 continue;
42241             }
42242             
42243             // style cleanup!?
42244             // class cleanup?
42245             
42246         }
42247         
42248         
42249         this.cleanUpChildren(node);
42250         
42251         
42252     },
42253     
42254     /**
42255      * Clean up MS wordisms...
42256      */
42257     cleanWord : function(node)
42258     {
42259         
42260         
42261         if (!node) {
42262             this.cleanWord(this.doc.body);
42263             return;
42264         }
42265         if (node.nodeName == "#text") {
42266             // clean up silly Windows -- stuff?
42267             return; 
42268         }
42269         if (node.nodeName == "#comment") {
42270             node.parentNode.removeChild(node);
42271             // clean up silly Windows -- stuff?
42272             return; 
42273         }
42274         
42275         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42276             node.parentNode.removeChild(node);
42277             return;
42278         }
42279         
42280         // remove - but keep children..
42281         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42282             while (node.childNodes.length) {
42283                 var cn = node.childNodes[0];
42284                 node.removeChild(cn);
42285                 node.parentNode.insertBefore(cn, node);
42286             }
42287             node.parentNode.removeChild(node);
42288             this.iterateChildren(node, this.cleanWord);
42289             return;
42290         }
42291         // clean styles
42292         if (node.className.length) {
42293             
42294             var cn = node.className.split(/\W+/);
42295             var cna = [];
42296             Roo.each(cn, function(cls) {
42297                 if (cls.match(/Mso[a-zA-Z]+/)) {
42298                     return;
42299                 }
42300                 cna.push(cls);
42301             });
42302             node.className = cna.length ? cna.join(' ') : '';
42303             if (!cna.length) {
42304                 node.removeAttribute("class");
42305             }
42306         }
42307         
42308         if (node.hasAttribute("lang")) {
42309             node.removeAttribute("lang");
42310         }
42311         
42312         if (node.hasAttribute("style")) {
42313             
42314             var styles = node.getAttribute("style").split(";");
42315             var nstyle = [];
42316             Roo.each(styles, function(s) {
42317                 if (!s.match(/:/)) {
42318                     return;
42319                 }
42320                 var kv = s.split(":");
42321                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42322                     return;
42323                 }
42324                 // what ever is left... we allow.
42325                 nstyle.push(s);
42326             });
42327             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42328             if (!nstyle.length) {
42329                 node.removeAttribute('style');
42330             }
42331         }
42332         this.iterateChildren(node, this.cleanWord);
42333         
42334         
42335         
42336     },
42337     /**
42338      * iterateChildren of a Node, calling fn each time, using this as the scole..
42339      * @param {DomNode} node node to iterate children of.
42340      * @param {Function} fn method of this class to call on each item.
42341      */
42342     iterateChildren : function(node, fn)
42343     {
42344         if (!node.childNodes.length) {
42345                 return;
42346         }
42347         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42348            fn.call(this, node.childNodes[i])
42349         }
42350     },
42351     
42352     
42353     /**
42354      * cleanTableWidths.
42355      *
42356      * Quite often pasting from word etc.. results in tables with column and widths.
42357      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42358      *
42359      */
42360     cleanTableWidths : function(node)
42361     {
42362          
42363          
42364         if (!node) {
42365             this.cleanTableWidths(this.doc.body);
42366             return;
42367         }
42368         
42369         // ignore list...
42370         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42371             return; 
42372         }
42373         Roo.log(node.tagName);
42374         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42375             this.iterateChildren(node, this.cleanTableWidths);
42376             return;
42377         }
42378         if (node.hasAttribute('width')) {
42379             node.removeAttribute('width');
42380         }
42381         
42382          
42383         if (node.hasAttribute("style")) {
42384             // pretty basic...
42385             
42386             var styles = node.getAttribute("style").split(";");
42387             var nstyle = [];
42388             Roo.each(styles, function(s) {
42389                 if (!s.match(/:/)) {
42390                     return;
42391                 }
42392                 var kv = s.split(":");
42393                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42394                     return;
42395                 }
42396                 // what ever is left... we allow.
42397                 nstyle.push(s);
42398             });
42399             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42400             if (!nstyle.length) {
42401                 node.removeAttribute('style');
42402             }
42403         }
42404         
42405         this.iterateChildren(node, this.cleanTableWidths);
42406         
42407         
42408     },
42409     
42410     
42411     
42412     
42413     domToHTML : function(currentElement, depth, nopadtext) {
42414         
42415         depth = depth || 0;
42416         nopadtext = nopadtext || false;
42417     
42418         if (!currentElement) {
42419             return this.domToHTML(this.doc.body);
42420         }
42421         
42422         //Roo.log(currentElement);
42423         var j;
42424         var allText = false;
42425         var nodeName = currentElement.nodeName;
42426         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42427         
42428         if  (nodeName == '#text') {
42429             
42430             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42431         }
42432         
42433         
42434         var ret = '';
42435         if (nodeName != 'BODY') {
42436              
42437             var i = 0;
42438             // Prints the node tagName, such as <A>, <IMG>, etc
42439             if (tagName) {
42440                 var attr = [];
42441                 for(i = 0; i < currentElement.attributes.length;i++) {
42442                     // quoting?
42443                     var aname = currentElement.attributes.item(i).name;
42444                     if (!currentElement.attributes.item(i).value.length) {
42445                         continue;
42446                     }
42447                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42448                 }
42449                 
42450                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42451             } 
42452             else {
42453                 
42454                 // eack
42455             }
42456         } else {
42457             tagName = false;
42458         }
42459         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42460             return ret;
42461         }
42462         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42463             nopadtext = true;
42464         }
42465         
42466         
42467         // Traverse the tree
42468         i = 0;
42469         var currentElementChild = currentElement.childNodes.item(i);
42470         var allText = true;
42471         var innerHTML  = '';
42472         lastnode = '';
42473         while (currentElementChild) {
42474             // Formatting code (indent the tree so it looks nice on the screen)
42475             var nopad = nopadtext;
42476             if (lastnode == 'SPAN') {
42477                 nopad  = true;
42478             }
42479             // text
42480             if  (currentElementChild.nodeName == '#text') {
42481                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42482                 toadd = nopadtext ? toadd : toadd.trim();
42483                 if (!nopad && toadd.length > 80) {
42484                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42485                 }
42486                 innerHTML  += toadd;
42487                 
42488                 i++;
42489                 currentElementChild = currentElement.childNodes.item(i);
42490                 lastNode = '';
42491                 continue;
42492             }
42493             allText = false;
42494             
42495             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42496                 
42497             // Recursively traverse the tree structure of the child node
42498             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42499             lastnode = currentElementChild.nodeName;
42500             i++;
42501             currentElementChild=currentElement.childNodes.item(i);
42502         }
42503         
42504         ret += innerHTML;
42505         
42506         if (!allText) {
42507                 // The remaining code is mostly for formatting the tree
42508             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42509         }
42510         
42511         
42512         if (tagName) {
42513             ret+= "</"+tagName+">";
42514         }
42515         return ret;
42516         
42517     },
42518         
42519     applyBlacklists : function()
42520     {
42521         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42522         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42523         
42524         this.white = [];
42525         this.black = [];
42526         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42527             if (b.indexOf(tag) > -1) {
42528                 return;
42529             }
42530             this.white.push(tag);
42531             
42532         }, this);
42533         
42534         Roo.each(w, function(tag) {
42535             if (b.indexOf(tag) > -1) {
42536                 return;
42537             }
42538             if (this.white.indexOf(tag) > -1) {
42539                 return;
42540             }
42541             this.white.push(tag);
42542             
42543         }, this);
42544         
42545         
42546         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42547             if (w.indexOf(tag) > -1) {
42548                 return;
42549             }
42550             this.black.push(tag);
42551             
42552         }, this);
42553         
42554         Roo.each(b, function(tag) {
42555             if (w.indexOf(tag) > -1) {
42556                 return;
42557             }
42558             if (this.black.indexOf(tag) > -1) {
42559                 return;
42560             }
42561             this.black.push(tag);
42562             
42563         }, this);
42564         
42565         
42566         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42567         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42568         
42569         this.cwhite = [];
42570         this.cblack = [];
42571         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42572             if (b.indexOf(tag) > -1) {
42573                 return;
42574             }
42575             this.cwhite.push(tag);
42576             
42577         }, this);
42578         
42579         Roo.each(w, function(tag) {
42580             if (b.indexOf(tag) > -1) {
42581                 return;
42582             }
42583             if (this.cwhite.indexOf(tag) > -1) {
42584                 return;
42585             }
42586             this.cwhite.push(tag);
42587             
42588         }, this);
42589         
42590         
42591         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42592             if (w.indexOf(tag) > -1) {
42593                 return;
42594             }
42595             this.cblack.push(tag);
42596             
42597         }, this);
42598         
42599         Roo.each(b, function(tag) {
42600             if (w.indexOf(tag) > -1) {
42601                 return;
42602             }
42603             if (this.cblack.indexOf(tag) > -1) {
42604                 return;
42605             }
42606             this.cblack.push(tag);
42607             
42608         }, this);
42609     },
42610     
42611     setStylesheets : function(stylesheets)
42612     {
42613         if(typeof(stylesheets) == 'string'){
42614             Roo.get(this.iframe.contentDocument.head).createChild({
42615                 tag : 'link',
42616                 rel : 'stylesheet',
42617                 type : 'text/css',
42618                 href : stylesheets
42619             });
42620             
42621             return;
42622         }
42623         var _this = this;
42624      
42625         Roo.each(stylesheets, function(s) {
42626             if(!s.length){
42627                 return;
42628             }
42629             
42630             Roo.get(_this.iframe.contentDocument.head).createChild({
42631                 tag : 'link',
42632                 rel : 'stylesheet',
42633                 type : 'text/css',
42634                 href : s
42635             });
42636         });
42637
42638         
42639     },
42640     
42641     removeStylesheets : function()
42642     {
42643         var _this = this;
42644         
42645         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42646             s.remove();
42647         });
42648     }
42649     
42650     // hide stuff that is not compatible
42651     /**
42652      * @event blur
42653      * @hide
42654      */
42655     /**
42656      * @event change
42657      * @hide
42658      */
42659     /**
42660      * @event focus
42661      * @hide
42662      */
42663     /**
42664      * @event specialkey
42665      * @hide
42666      */
42667     /**
42668      * @cfg {String} fieldClass @hide
42669      */
42670     /**
42671      * @cfg {String} focusClass @hide
42672      */
42673     /**
42674      * @cfg {String} autoCreate @hide
42675      */
42676     /**
42677      * @cfg {String} inputType @hide
42678      */
42679     /**
42680      * @cfg {String} invalidClass @hide
42681      */
42682     /**
42683      * @cfg {String} invalidText @hide
42684      */
42685     /**
42686      * @cfg {String} msgFx @hide
42687      */
42688     /**
42689      * @cfg {String} validateOnBlur @hide
42690      */
42691 });
42692
42693 Roo.HtmlEditorCore.white = [
42694         'area', 'br', 'img', 'input', 'hr', 'wbr',
42695         
42696        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42697        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42698        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42699        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42700        'table',   'ul',         'xmp', 
42701        
42702        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42703       'thead',   'tr', 
42704      
42705       'dir', 'menu', 'ol', 'ul', 'dl',
42706        
42707       'embed',  'object'
42708 ];
42709
42710
42711 Roo.HtmlEditorCore.black = [
42712     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42713         'applet', // 
42714         'base',   'basefont', 'bgsound', 'blink',  'body', 
42715         'frame',  'frameset', 'head',    'html',   'ilayer', 
42716         'iframe', 'layer',  'link',     'meta',    'object',   
42717         'script', 'style' ,'title',  'xml' // clean later..
42718 ];
42719 Roo.HtmlEditorCore.clean = [
42720     'script', 'style', 'title', 'xml'
42721 ];
42722 Roo.HtmlEditorCore.remove = [
42723     'font'
42724 ];
42725 // attributes..
42726
42727 Roo.HtmlEditorCore.ablack = [
42728     'on'
42729 ];
42730     
42731 Roo.HtmlEditorCore.aclean = [ 
42732     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42733 ];
42734
42735 // protocols..
42736 Roo.HtmlEditorCore.pwhite= [
42737         'http',  'https',  'mailto'
42738 ];
42739
42740 // white listed style attributes.
42741 Roo.HtmlEditorCore.cwhite= [
42742       //  'text-align', /// default is to allow most things..
42743       
42744          
42745 //        'font-size'//??
42746 ];
42747
42748 // black listed style attributes.
42749 Roo.HtmlEditorCore.cblack= [
42750       //  'font-size' -- this can be set by the project 
42751 ];
42752
42753
42754 Roo.HtmlEditorCore.swapCodes   =[ 
42755     [    8211, "--" ], 
42756     [    8212, "--" ], 
42757     [    8216,  "'" ],  
42758     [    8217, "'" ],  
42759     [    8220, '"' ],  
42760     [    8221, '"' ],  
42761     [    8226, "*" ],  
42762     [    8230, "..." ]
42763 ]; 
42764
42765     //<script type="text/javascript">
42766
42767 /*
42768  * Ext JS Library 1.1.1
42769  * Copyright(c) 2006-2007, Ext JS, LLC.
42770  * Licence LGPL
42771  * 
42772  */
42773  
42774  
42775 Roo.form.HtmlEditor = function(config){
42776     
42777     
42778     
42779     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42780     
42781     if (!this.toolbars) {
42782         this.toolbars = [];
42783     }
42784     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42785     
42786     
42787 };
42788
42789 /**
42790  * @class Roo.form.HtmlEditor
42791  * @extends Roo.form.Field
42792  * Provides a lightweight HTML Editor component.
42793  *
42794  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42795  * 
42796  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42797  * supported by this editor.</b><br/><br/>
42798  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42799  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42800  */
42801 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42802     /**
42803      * @cfg {Boolean} clearUp
42804      */
42805     clearUp : true,
42806       /**
42807      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42808      */
42809     toolbars : false,
42810    
42811      /**
42812      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42813      *                        Roo.resizable.
42814      */
42815     resizable : false,
42816      /**
42817      * @cfg {Number} height (in pixels)
42818      */   
42819     height: 300,
42820    /**
42821      * @cfg {Number} width (in pixels)
42822      */   
42823     width: 500,
42824     
42825     /**
42826      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42827      * 
42828      */
42829     stylesheets: false,
42830     
42831     
42832      /**
42833      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42834      * 
42835      */
42836     cblack: false,
42837     /**
42838      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42839      * 
42840      */
42841     cwhite: false,
42842     
42843      /**
42844      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42845      * 
42846      */
42847     black: false,
42848     /**
42849      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42850      * 
42851      */
42852     white: false,
42853     
42854     // id of frame..
42855     frameId: false,
42856     
42857     // private properties
42858     validationEvent : false,
42859     deferHeight: true,
42860     initialized : false,
42861     activated : false,
42862     
42863     onFocus : Roo.emptyFn,
42864     iframePad:3,
42865     hideMode:'offsets',
42866     
42867     actionMode : 'container', // defaults to hiding it...
42868     
42869     defaultAutoCreate : { // modified by initCompnoent..
42870         tag: "textarea",
42871         style:"width:500px;height:300px;",
42872         autocomplete: "new-password"
42873     },
42874
42875     // private
42876     initComponent : function(){
42877         this.addEvents({
42878             /**
42879              * @event initialize
42880              * Fires when the editor is fully initialized (including the iframe)
42881              * @param {HtmlEditor} this
42882              */
42883             initialize: true,
42884             /**
42885              * @event activate
42886              * Fires when the editor is first receives the focus. Any insertion must wait
42887              * until after this event.
42888              * @param {HtmlEditor} this
42889              */
42890             activate: true,
42891              /**
42892              * @event beforesync
42893              * Fires before the textarea is updated with content from the editor iframe. Return false
42894              * to cancel the sync.
42895              * @param {HtmlEditor} this
42896              * @param {String} html
42897              */
42898             beforesync: true,
42899              /**
42900              * @event beforepush
42901              * Fires before the iframe editor is updated with content from the textarea. Return false
42902              * to cancel the push.
42903              * @param {HtmlEditor} this
42904              * @param {String} html
42905              */
42906             beforepush: true,
42907              /**
42908              * @event sync
42909              * Fires when the textarea is updated with content from the editor iframe.
42910              * @param {HtmlEditor} this
42911              * @param {String} html
42912              */
42913             sync: true,
42914              /**
42915              * @event push
42916              * Fires when the iframe editor is updated with content from the textarea.
42917              * @param {HtmlEditor} this
42918              * @param {String} html
42919              */
42920             push: true,
42921              /**
42922              * @event editmodechange
42923              * Fires when the editor switches edit modes
42924              * @param {HtmlEditor} this
42925              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42926              */
42927             editmodechange: true,
42928             /**
42929              * @event editorevent
42930              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42931              * @param {HtmlEditor} this
42932              */
42933             editorevent: true,
42934             /**
42935              * @event firstfocus
42936              * Fires when on first focus - needed by toolbars..
42937              * @param {HtmlEditor} this
42938              */
42939             firstfocus: true,
42940             /**
42941              * @event autosave
42942              * Auto save the htmlEditor value as a file into Events
42943              * @param {HtmlEditor} this
42944              */
42945             autosave: true,
42946             /**
42947              * @event savedpreview
42948              * preview the saved version of htmlEditor
42949              * @param {HtmlEditor} this
42950              */
42951             savedpreview: true,
42952             
42953             /**
42954             * @event stylesheetsclick
42955             * Fires when press the Sytlesheets button
42956             * @param {Roo.HtmlEditorCore} this
42957             */
42958             stylesheetsclick: true
42959         });
42960         this.defaultAutoCreate =  {
42961             tag: "textarea",
42962             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42963             autocomplete: "new-password"
42964         };
42965     },
42966
42967     /**
42968      * Protected method that will not generally be called directly. It
42969      * is called when the editor creates its toolbar. Override this method if you need to
42970      * add custom toolbar buttons.
42971      * @param {HtmlEditor} editor
42972      */
42973     createToolbar : function(editor){
42974         Roo.log("create toolbars");
42975         if (!editor.toolbars || !editor.toolbars.length) {
42976             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42977         }
42978         
42979         for (var i =0 ; i < editor.toolbars.length;i++) {
42980             editor.toolbars[i] = Roo.factory(
42981                     typeof(editor.toolbars[i]) == 'string' ?
42982                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42983                 Roo.form.HtmlEditor);
42984             editor.toolbars[i].init(editor);
42985         }
42986          
42987         
42988     },
42989
42990      
42991     // private
42992     onRender : function(ct, position)
42993     {
42994         var _t = this;
42995         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42996         
42997         this.wrap = this.el.wrap({
42998             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42999         });
43000         
43001         this.editorcore.onRender(ct, position);
43002          
43003         if (this.resizable) {
43004             this.resizeEl = new Roo.Resizable(this.wrap, {
43005                 pinned : true,
43006                 wrap: true,
43007                 dynamic : true,
43008                 minHeight : this.height,
43009                 height: this.height,
43010                 handles : this.resizable,
43011                 width: this.width,
43012                 listeners : {
43013                     resize : function(r, w, h) {
43014                         _t.onResize(w,h); // -something
43015                     }
43016                 }
43017             });
43018             
43019         }
43020         this.createToolbar(this);
43021        
43022         
43023         if(!this.width){
43024             this.setSize(this.wrap.getSize());
43025         }
43026         if (this.resizeEl) {
43027             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43028             // should trigger onReize..
43029         }
43030         
43031         this.keyNav = new Roo.KeyNav(this.el, {
43032             
43033             "tab" : function(e){
43034                 e.preventDefault();
43035                 
43036                 var value = this.getValue();
43037                 
43038                 var start = this.el.dom.selectionStart;
43039                 var end = this.el.dom.selectionEnd;
43040                 
43041                 if(!e.shiftKey){
43042                     
43043                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43044                     this.el.dom.setSelectionRange(end + 1, end + 1);
43045                     return;
43046                 }
43047                 
43048                 var f = value.substring(0, start).split("\t");
43049                 
43050                 if(f.pop().length != 0){
43051                     return;
43052                 }
43053                 
43054                 this.setValue(f.join("\t") + value.substring(end));
43055                 this.el.dom.setSelectionRange(start - 1, start - 1);
43056                 
43057             },
43058             
43059             "home" : function(e){
43060                 e.preventDefault();
43061                 
43062                 var curr = this.el.dom.selectionStart;
43063                 var lines = this.getValue().split("\n");
43064                 
43065                 if(!lines.length){
43066                     return;
43067                 }
43068                 
43069                 if(e.ctrlKey){
43070                     this.el.dom.setSelectionRange(0, 0);
43071                     return;
43072                 }
43073                 
43074                 var pos = 0;
43075                 
43076                 for (var i = 0; i < lines.length;i++) {
43077                     pos += lines[i].length;
43078                     
43079                     if(i != 0){
43080                         pos += 1;
43081                     }
43082                     
43083                     if(pos < curr){
43084                         continue;
43085                     }
43086                     
43087                     pos -= lines[i].length;
43088                     
43089                     break;
43090                 }
43091                 
43092                 if(!e.shiftKey){
43093                     this.el.dom.setSelectionRange(pos, pos);
43094                     return;
43095                 }
43096                 
43097                 this.el.dom.selectionStart = pos;
43098                 this.el.dom.selectionEnd = curr;
43099             },
43100             
43101             "end" : function(e){
43102                 e.preventDefault();
43103                 
43104                 var curr = this.el.dom.selectionStart;
43105                 var lines = this.getValue().split("\n");
43106                 
43107                 if(!lines.length){
43108                     return;
43109                 }
43110                 
43111                 if(e.ctrlKey){
43112                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43113                     return;
43114                 }
43115                 
43116                 var pos = 0;
43117                 
43118                 for (var i = 0; i < lines.length;i++) {
43119                     
43120                     pos += lines[i].length;
43121                     
43122                     if(i != 0){
43123                         pos += 1;
43124                     }
43125                     
43126                     if(pos < curr){
43127                         continue;
43128                     }
43129                     
43130                     break;
43131                 }
43132                 
43133                 if(!e.shiftKey){
43134                     this.el.dom.setSelectionRange(pos, pos);
43135                     return;
43136                 }
43137                 
43138                 this.el.dom.selectionStart = curr;
43139                 this.el.dom.selectionEnd = pos;
43140             },
43141
43142             scope : this,
43143
43144             doRelay : function(foo, bar, hname){
43145                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43146             },
43147
43148             forceKeyDown: true
43149         });
43150         
43151 //        if(this.autosave && this.w){
43152 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43153 //        }
43154     },
43155
43156     // private
43157     onResize : function(w, h)
43158     {
43159         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43160         var ew = false;
43161         var eh = false;
43162         
43163         if(this.el ){
43164             if(typeof w == 'number'){
43165                 var aw = w - this.wrap.getFrameWidth('lr');
43166                 this.el.setWidth(this.adjustWidth('textarea', aw));
43167                 ew = aw;
43168             }
43169             if(typeof h == 'number'){
43170                 var tbh = 0;
43171                 for (var i =0; i < this.toolbars.length;i++) {
43172                     // fixme - ask toolbars for heights?
43173                     tbh += this.toolbars[i].tb.el.getHeight();
43174                     if (this.toolbars[i].footer) {
43175                         tbh += this.toolbars[i].footer.el.getHeight();
43176                     }
43177                 }
43178                 
43179                 
43180                 
43181                 
43182                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43183                 ah -= 5; // knock a few pixes off for look..
43184 //                Roo.log(ah);
43185                 this.el.setHeight(this.adjustWidth('textarea', ah));
43186                 var eh = ah;
43187             }
43188         }
43189         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43190         this.editorcore.onResize(ew,eh);
43191         
43192     },
43193
43194     /**
43195      * Toggles the editor between standard and source edit mode.
43196      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43197      */
43198     toggleSourceEdit : function(sourceEditMode)
43199     {
43200         this.editorcore.toggleSourceEdit(sourceEditMode);
43201         
43202         if(this.editorcore.sourceEditMode){
43203             Roo.log('editor - showing textarea');
43204             
43205 //            Roo.log('in');
43206 //            Roo.log(this.syncValue());
43207             this.editorcore.syncValue();
43208             this.el.removeClass('x-hidden');
43209             this.el.dom.removeAttribute('tabIndex');
43210             this.el.focus();
43211             
43212             for (var i = 0; i < this.toolbars.length; i++) {
43213                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43214                     this.toolbars[i].tb.hide();
43215                     this.toolbars[i].footer.hide();
43216                 }
43217             }
43218             
43219         }else{
43220             Roo.log('editor - hiding textarea');
43221 //            Roo.log('out')
43222 //            Roo.log(this.pushValue()); 
43223             this.editorcore.pushValue();
43224             
43225             this.el.addClass('x-hidden');
43226             this.el.dom.setAttribute('tabIndex', -1);
43227             
43228             for (var i = 0; i < this.toolbars.length; i++) {
43229                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43230                     this.toolbars[i].tb.show();
43231                     this.toolbars[i].footer.show();
43232                 }
43233             }
43234             
43235             //this.deferFocus();
43236         }
43237         
43238         this.setSize(this.wrap.getSize());
43239         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43240         
43241         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43242     },
43243  
43244     // private (for BoxComponent)
43245     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43246
43247     // private (for BoxComponent)
43248     getResizeEl : function(){
43249         return this.wrap;
43250     },
43251
43252     // private (for BoxComponent)
43253     getPositionEl : function(){
43254         return this.wrap;
43255     },
43256
43257     // private
43258     initEvents : function(){
43259         this.originalValue = this.getValue();
43260     },
43261
43262     /**
43263      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43264      * @method
43265      */
43266     markInvalid : Roo.emptyFn,
43267     /**
43268      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43269      * @method
43270      */
43271     clearInvalid : Roo.emptyFn,
43272
43273     setValue : function(v){
43274         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43275         this.editorcore.pushValue();
43276     },
43277
43278      
43279     // private
43280     deferFocus : function(){
43281         this.focus.defer(10, this);
43282     },
43283
43284     // doc'ed in Field
43285     focus : function(){
43286         this.editorcore.focus();
43287         
43288     },
43289       
43290
43291     // private
43292     onDestroy : function(){
43293         
43294         
43295         
43296         if(this.rendered){
43297             
43298             for (var i =0; i < this.toolbars.length;i++) {
43299                 // fixme - ask toolbars for heights?
43300                 this.toolbars[i].onDestroy();
43301             }
43302             
43303             this.wrap.dom.innerHTML = '';
43304             this.wrap.remove();
43305         }
43306     },
43307
43308     // private
43309     onFirstFocus : function(){
43310         //Roo.log("onFirstFocus");
43311         this.editorcore.onFirstFocus();
43312          for (var i =0; i < this.toolbars.length;i++) {
43313             this.toolbars[i].onFirstFocus();
43314         }
43315         
43316     },
43317     
43318     // private
43319     syncValue : function()
43320     {
43321         this.editorcore.syncValue();
43322     },
43323     
43324     pushValue : function()
43325     {
43326         this.editorcore.pushValue();
43327     },
43328     
43329     setStylesheets : function(stylesheets)
43330     {
43331         this.editorcore.setStylesheets(stylesheets);
43332     },
43333     
43334     removeStylesheets : function()
43335     {
43336         this.editorcore.removeStylesheets();
43337     }
43338      
43339     
43340     // hide stuff that is not compatible
43341     /**
43342      * @event blur
43343      * @hide
43344      */
43345     /**
43346      * @event change
43347      * @hide
43348      */
43349     /**
43350      * @event focus
43351      * @hide
43352      */
43353     /**
43354      * @event specialkey
43355      * @hide
43356      */
43357     /**
43358      * @cfg {String} fieldClass @hide
43359      */
43360     /**
43361      * @cfg {String} focusClass @hide
43362      */
43363     /**
43364      * @cfg {String} autoCreate @hide
43365      */
43366     /**
43367      * @cfg {String} inputType @hide
43368      */
43369     /**
43370      * @cfg {String} invalidClass @hide
43371      */
43372     /**
43373      * @cfg {String} invalidText @hide
43374      */
43375     /**
43376      * @cfg {String} msgFx @hide
43377      */
43378     /**
43379      * @cfg {String} validateOnBlur @hide
43380      */
43381 });
43382  
43383     // <script type="text/javascript">
43384 /*
43385  * Based on
43386  * Ext JS Library 1.1.1
43387  * Copyright(c) 2006-2007, Ext JS, LLC.
43388  *  
43389  
43390  */
43391
43392 /**
43393  * @class Roo.form.HtmlEditorToolbar1
43394  * Basic Toolbar
43395  * 
43396  * Usage:
43397  *
43398  new Roo.form.HtmlEditor({
43399     ....
43400     toolbars : [
43401         new Roo.form.HtmlEditorToolbar1({
43402             disable : { fonts: 1 , format: 1, ..., ... , ...],
43403             btns : [ .... ]
43404         })
43405     }
43406      
43407  * 
43408  * @cfg {Object} disable List of elements to disable..
43409  * @cfg {Array} btns List of additional buttons.
43410  * 
43411  * 
43412  * NEEDS Extra CSS? 
43413  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43414  */
43415  
43416 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43417 {
43418     
43419     Roo.apply(this, config);
43420     
43421     // default disabled, based on 'good practice'..
43422     this.disable = this.disable || {};
43423     Roo.applyIf(this.disable, {
43424         fontSize : true,
43425         colors : true,
43426         specialElements : true
43427     });
43428     
43429     
43430     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43431     // dont call parent... till later.
43432 }
43433
43434 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43435     
43436     tb: false,
43437     
43438     rendered: false,
43439     
43440     editor : false,
43441     editorcore : false,
43442     /**
43443      * @cfg {Object} disable  List of toolbar elements to disable
43444          
43445      */
43446     disable : false,
43447     
43448     
43449      /**
43450      * @cfg {String} createLinkText The default text for the create link prompt
43451      */
43452     createLinkText : 'Please enter the URL for the link:',
43453     /**
43454      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43455      */
43456     defaultLinkValue : 'http:/'+'/',
43457    
43458     
43459       /**
43460      * @cfg {Array} fontFamilies An array of available font families
43461      */
43462     fontFamilies : [
43463         'Arial',
43464         'Courier New',
43465         'Tahoma',
43466         'Times New Roman',
43467         'Verdana'
43468     ],
43469     
43470     specialChars : [
43471            "&#169;",
43472           "&#174;",     
43473           "&#8482;",    
43474           "&#163;" ,    
43475          // "&#8212;",    
43476           "&#8230;",    
43477           "&#247;" ,    
43478         //  "&#225;" ,     ?? a acute?
43479            "&#8364;"    , //Euro
43480        //   "&#8220;"    ,
43481         //  "&#8221;"    ,
43482         //  "&#8226;"    ,
43483           "&#176;"  //   , // degrees
43484
43485          // "&#233;"     , // e ecute
43486          // "&#250;"     , // u ecute?
43487     ],
43488     
43489     specialElements : [
43490         {
43491             text: "Insert Table",
43492             xtype: 'MenuItem',
43493             xns : Roo.Menu,
43494             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43495                 
43496         },
43497         {    
43498             text: "Insert Image",
43499             xtype: 'MenuItem',
43500             xns : Roo.Menu,
43501             ihtml : '<img src="about:blank"/>'
43502             
43503         }
43504         
43505          
43506     ],
43507     
43508     
43509     inputElements : [ 
43510             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43511             "input:submit", "input:button", "select", "textarea", "label" ],
43512     formats : [
43513         ["p"] ,  
43514         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43515         ["pre"],[ "code"], 
43516         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43517         ['div'],['span']
43518     ],
43519     
43520     cleanStyles : [
43521         "font-size"
43522     ],
43523      /**
43524      * @cfg {String} defaultFont default font to use.
43525      */
43526     defaultFont: 'tahoma',
43527    
43528     fontSelect : false,
43529     
43530     
43531     formatCombo : false,
43532     
43533     init : function(editor)
43534     {
43535         this.editor = editor;
43536         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43537         var editorcore = this.editorcore;
43538         
43539         var _t = this;
43540         
43541         var fid = editorcore.frameId;
43542         var etb = this;
43543         function btn(id, toggle, handler){
43544             var xid = fid + '-'+ id ;
43545             return {
43546                 id : xid,
43547                 cmd : id,
43548                 cls : 'x-btn-icon x-edit-'+id,
43549                 enableToggle:toggle !== false,
43550                 scope: _t, // was editor...
43551                 handler:handler||_t.relayBtnCmd,
43552                 clickEvent:'mousedown',
43553                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43554                 tabIndex:-1
43555             };
43556         }
43557         
43558         
43559         
43560         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43561         this.tb = tb;
43562          // stop form submits
43563         tb.el.on('click', function(e){
43564             e.preventDefault(); // what does this do?
43565         });
43566
43567         if(!this.disable.font) { // && !Roo.isSafari){
43568             /* why no safari for fonts 
43569             editor.fontSelect = tb.el.createChild({
43570                 tag:'select',
43571                 tabIndex: -1,
43572                 cls:'x-font-select',
43573                 html: this.createFontOptions()
43574             });
43575             
43576             editor.fontSelect.on('change', function(){
43577                 var font = editor.fontSelect.dom.value;
43578                 editor.relayCmd('fontname', font);
43579                 editor.deferFocus();
43580             }, editor);
43581             
43582             tb.add(
43583                 editor.fontSelect.dom,
43584                 '-'
43585             );
43586             */
43587             
43588         };
43589         if(!this.disable.formats){
43590             this.formatCombo = new Roo.form.ComboBox({
43591                 store: new Roo.data.SimpleStore({
43592                     id : 'tag',
43593                     fields: ['tag'],
43594                     data : this.formats // from states.js
43595                 }),
43596                 blockFocus : true,
43597                 name : '',
43598                 //autoCreate : {tag: "div",  size: "20"},
43599                 displayField:'tag',
43600                 typeAhead: false,
43601                 mode: 'local',
43602                 editable : false,
43603                 triggerAction: 'all',
43604                 emptyText:'Add tag',
43605                 selectOnFocus:true,
43606                 width:135,
43607                 listeners : {
43608                     'select': function(c, r, i) {
43609                         editorcore.insertTag(r.get('tag'));
43610                         editor.focus();
43611                     }
43612                 }
43613
43614             });
43615             tb.addField(this.formatCombo);
43616             
43617         }
43618         
43619         if(!this.disable.format){
43620             tb.add(
43621                 btn('bold'),
43622                 btn('italic'),
43623                 btn('underline')
43624             );
43625         };
43626         if(!this.disable.fontSize){
43627             tb.add(
43628                 '-',
43629                 
43630                 
43631                 btn('increasefontsize', false, editorcore.adjustFont),
43632                 btn('decreasefontsize', false, editorcore.adjustFont)
43633             );
43634         };
43635         
43636         
43637         if(!this.disable.colors){
43638             tb.add(
43639                 '-', {
43640                     id:editorcore.frameId +'-forecolor',
43641                     cls:'x-btn-icon x-edit-forecolor',
43642                     clickEvent:'mousedown',
43643                     tooltip: this.buttonTips['forecolor'] || undefined,
43644                     tabIndex:-1,
43645                     menu : new Roo.menu.ColorMenu({
43646                         allowReselect: true,
43647                         focus: Roo.emptyFn,
43648                         value:'000000',
43649                         plain:true,
43650                         selectHandler: function(cp, color){
43651                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43652                             editor.deferFocus();
43653                         },
43654                         scope: editorcore,
43655                         clickEvent:'mousedown'
43656                     })
43657                 }, {
43658                     id:editorcore.frameId +'backcolor',
43659                     cls:'x-btn-icon x-edit-backcolor',
43660                     clickEvent:'mousedown',
43661                     tooltip: this.buttonTips['backcolor'] || undefined,
43662                     tabIndex:-1,
43663                     menu : new Roo.menu.ColorMenu({
43664                         focus: Roo.emptyFn,
43665                         value:'FFFFFF',
43666                         plain:true,
43667                         allowReselect: true,
43668                         selectHandler: function(cp, color){
43669                             if(Roo.isGecko){
43670                                 editorcore.execCmd('useCSS', false);
43671                                 editorcore.execCmd('hilitecolor', color);
43672                                 editorcore.execCmd('useCSS', true);
43673                                 editor.deferFocus();
43674                             }else{
43675                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43676                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43677                                 editor.deferFocus();
43678                             }
43679                         },
43680                         scope:editorcore,
43681                         clickEvent:'mousedown'
43682                     })
43683                 }
43684             );
43685         };
43686         // now add all the items...
43687         
43688
43689         if(!this.disable.alignments){
43690             tb.add(
43691                 '-',
43692                 btn('justifyleft'),
43693                 btn('justifycenter'),
43694                 btn('justifyright')
43695             );
43696         };
43697
43698         //if(!Roo.isSafari){
43699             if(!this.disable.links){
43700                 tb.add(
43701                     '-',
43702                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43703                 );
43704             };
43705
43706             if(!this.disable.lists){
43707                 tb.add(
43708                     '-',
43709                     btn('insertorderedlist'),
43710                     btn('insertunorderedlist')
43711                 );
43712             }
43713             if(!this.disable.sourceEdit){
43714                 tb.add(
43715                     '-',
43716                     btn('sourceedit', true, function(btn){
43717                         this.toggleSourceEdit(btn.pressed);
43718                     })
43719                 );
43720             }
43721         //}
43722         
43723         var smenu = { };
43724         // special menu.. - needs to be tidied up..
43725         if (!this.disable.special) {
43726             smenu = {
43727                 text: "&#169;",
43728                 cls: 'x-edit-none',
43729                 
43730                 menu : {
43731                     items : []
43732                 }
43733             };
43734             for (var i =0; i < this.specialChars.length; i++) {
43735                 smenu.menu.items.push({
43736                     
43737                     html: this.specialChars[i],
43738                     handler: function(a,b) {
43739                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43740                         //editor.insertAtCursor(a.html);
43741                         
43742                     },
43743                     tabIndex:-1
43744                 });
43745             }
43746             
43747             
43748             tb.add(smenu);
43749             
43750             
43751         }
43752         
43753         var cmenu = { };
43754         if (!this.disable.cleanStyles) {
43755             cmenu = {
43756                 cls: 'x-btn-icon x-btn-clear',
43757                 
43758                 menu : {
43759                     items : []
43760                 }
43761             };
43762             for (var i =0; i < this.cleanStyles.length; i++) {
43763                 cmenu.menu.items.push({
43764                     actiontype : this.cleanStyles[i],
43765                     html: 'Remove ' + this.cleanStyles[i],
43766                     handler: function(a,b) {
43767 //                        Roo.log(a);
43768 //                        Roo.log(b);
43769                         var c = Roo.get(editorcore.doc.body);
43770                         c.select('[style]').each(function(s) {
43771                             s.dom.style.removeProperty(a.actiontype);
43772                         });
43773                         editorcore.syncValue();
43774                     },
43775                     tabIndex:-1
43776                 });
43777             }
43778              cmenu.menu.items.push({
43779                 actiontype : 'tablewidths',
43780                 html: 'Remove Table Widths',
43781                 handler: function(a,b) {
43782                     editorcore.cleanTableWidths();
43783                     editorcore.syncValue();
43784                 },
43785                 tabIndex:-1
43786             });
43787             cmenu.menu.items.push({
43788                 actiontype : 'word',
43789                 html: 'Remove MS Word Formating',
43790                 handler: function(a,b) {
43791                     editorcore.cleanWord();
43792                     editorcore.syncValue();
43793                 },
43794                 tabIndex:-1
43795             });
43796             
43797             cmenu.menu.items.push({
43798                 actiontype : 'all',
43799                 html: 'Remove All Styles',
43800                 handler: function(a,b) {
43801                     
43802                     var c = Roo.get(editorcore.doc.body);
43803                     c.select('[style]').each(function(s) {
43804                         s.dom.removeAttribute('style');
43805                     });
43806                     editorcore.syncValue();
43807                 },
43808                 tabIndex:-1
43809             });
43810              cmenu.menu.items.push({
43811                 actiontype : 'tidy',
43812                 html: 'Tidy HTML Source',
43813                 handler: function(a,b) {
43814                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43815                     editorcore.syncValue();
43816                 },
43817                 tabIndex:-1
43818             });
43819             
43820             
43821             tb.add(cmenu);
43822         }
43823          
43824         if (!this.disable.specialElements) {
43825             var semenu = {
43826                 text: "Other;",
43827                 cls: 'x-edit-none',
43828                 menu : {
43829                     items : []
43830                 }
43831             };
43832             for (var i =0; i < this.specialElements.length; i++) {
43833                 semenu.menu.items.push(
43834                     Roo.apply({ 
43835                         handler: function(a,b) {
43836                             editor.insertAtCursor(this.ihtml);
43837                         }
43838                     }, this.specialElements[i])
43839                 );
43840                     
43841             }
43842             
43843             tb.add(semenu);
43844             
43845             
43846         }
43847          
43848         
43849         if (this.btns) {
43850             for(var i =0; i< this.btns.length;i++) {
43851                 var b = Roo.factory(this.btns[i],Roo.form);
43852                 b.cls =  'x-edit-none';
43853                 
43854                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43855                     b.cls += ' x-init-enable';
43856                 }
43857                 
43858                 b.scope = editorcore;
43859                 tb.add(b);
43860             }
43861         
43862         }
43863         
43864         
43865         
43866         // disable everything...
43867         
43868         this.tb.items.each(function(item){
43869             
43870            if(
43871                 item.id != editorcore.frameId+ '-sourceedit' && 
43872                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43873             ){
43874                 
43875                 item.disable();
43876             }
43877         });
43878         this.rendered = true;
43879         
43880         // the all the btns;
43881         editor.on('editorevent', this.updateToolbar, this);
43882         // other toolbars need to implement this..
43883         //editor.on('editmodechange', this.updateToolbar, this);
43884     },
43885     
43886     
43887     relayBtnCmd : function(btn) {
43888         this.editorcore.relayCmd(btn.cmd);
43889     },
43890     // private used internally
43891     createLink : function(){
43892         Roo.log("create link?");
43893         var url = prompt(this.createLinkText, this.defaultLinkValue);
43894         if(url && url != 'http:/'+'/'){
43895             this.editorcore.relayCmd('createlink', url);
43896         }
43897     },
43898
43899     
43900     /**
43901      * Protected method that will not generally be called directly. It triggers
43902      * a toolbar update by reading the markup state of the current selection in the editor.
43903      */
43904     updateToolbar: function(){
43905
43906         if(!this.editorcore.activated){
43907             this.editor.onFirstFocus();
43908             return;
43909         }
43910
43911         var btns = this.tb.items.map, 
43912             doc = this.editorcore.doc,
43913             frameId = this.editorcore.frameId;
43914
43915         if(!this.disable.font && !Roo.isSafari){
43916             /*
43917             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43918             if(name != this.fontSelect.dom.value){
43919                 this.fontSelect.dom.value = name;
43920             }
43921             */
43922         }
43923         if(!this.disable.format){
43924             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43925             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43926             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43927         }
43928         if(!this.disable.alignments){
43929             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43930             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43931             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43932         }
43933         if(!Roo.isSafari && !this.disable.lists){
43934             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43935             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43936         }
43937         
43938         var ans = this.editorcore.getAllAncestors();
43939         if (this.formatCombo) {
43940             
43941             
43942             var store = this.formatCombo.store;
43943             this.formatCombo.setValue("");
43944             for (var i =0; i < ans.length;i++) {
43945                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43946                     // select it..
43947                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43948                     break;
43949                 }
43950             }
43951         }
43952         
43953         
43954         
43955         // hides menus... - so this cant be on a menu...
43956         Roo.menu.MenuMgr.hideAll();
43957
43958         //this.editorsyncValue();
43959     },
43960    
43961     
43962     createFontOptions : function(){
43963         var buf = [], fs = this.fontFamilies, ff, lc;
43964         
43965         
43966         
43967         for(var i = 0, len = fs.length; i< len; i++){
43968             ff = fs[i];
43969             lc = ff.toLowerCase();
43970             buf.push(
43971                 '<option value="',lc,'" style="font-family:',ff,';"',
43972                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43973                     ff,
43974                 '</option>'
43975             );
43976         }
43977         return buf.join('');
43978     },
43979     
43980     toggleSourceEdit : function(sourceEditMode){
43981         
43982         Roo.log("toolbar toogle");
43983         if(sourceEditMode === undefined){
43984             sourceEditMode = !this.sourceEditMode;
43985         }
43986         this.sourceEditMode = sourceEditMode === true;
43987         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43988         // just toggle the button?
43989         if(btn.pressed !== this.sourceEditMode){
43990             btn.toggle(this.sourceEditMode);
43991             return;
43992         }
43993         
43994         if(sourceEditMode){
43995             Roo.log("disabling buttons");
43996             this.tb.items.each(function(item){
43997                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
43998                     item.disable();
43999                 }
44000             });
44001           
44002         }else{
44003             Roo.log("enabling buttons");
44004             if(this.editorcore.initialized){
44005                 this.tb.items.each(function(item){
44006                     item.enable();
44007                 });
44008             }
44009             
44010         }
44011         Roo.log("calling toggole on editor");
44012         // tell the editor that it's been pressed..
44013         this.editor.toggleSourceEdit(sourceEditMode);
44014        
44015     },
44016      /**
44017      * Object collection of toolbar tooltips for the buttons in the editor. The key
44018      * is the command id associated with that button and the value is a valid QuickTips object.
44019      * For example:
44020 <pre><code>
44021 {
44022     bold : {
44023         title: 'Bold (Ctrl+B)',
44024         text: 'Make the selected text bold.',
44025         cls: 'x-html-editor-tip'
44026     },
44027     italic : {
44028         title: 'Italic (Ctrl+I)',
44029         text: 'Make the selected text italic.',
44030         cls: 'x-html-editor-tip'
44031     },
44032     ...
44033 </code></pre>
44034     * @type Object
44035      */
44036     buttonTips : {
44037         bold : {
44038             title: 'Bold (Ctrl+B)',
44039             text: 'Make the selected text bold.',
44040             cls: 'x-html-editor-tip'
44041         },
44042         italic : {
44043             title: 'Italic (Ctrl+I)',
44044             text: 'Make the selected text italic.',
44045             cls: 'x-html-editor-tip'
44046         },
44047         underline : {
44048             title: 'Underline (Ctrl+U)',
44049             text: 'Underline the selected text.',
44050             cls: 'x-html-editor-tip'
44051         },
44052         increasefontsize : {
44053             title: 'Grow Text',
44054             text: 'Increase the font size.',
44055             cls: 'x-html-editor-tip'
44056         },
44057         decreasefontsize : {
44058             title: 'Shrink Text',
44059             text: 'Decrease the font size.',
44060             cls: 'x-html-editor-tip'
44061         },
44062         backcolor : {
44063             title: 'Text Highlight Color',
44064             text: 'Change the background color of the selected text.',
44065             cls: 'x-html-editor-tip'
44066         },
44067         forecolor : {
44068             title: 'Font Color',
44069             text: 'Change the color of the selected text.',
44070             cls: 'x-html-editor-tip'
44071         },
44072         justifyleft : {
44073             title: 'Align Text Left',
44074             text: 'Align text to the left.',
44075             cls: 'x-html-editor-tip'
44076         },
44077         justifycenter : {
44078             title: 'Center Text',
44079             text: 'Center text in the editor.',
44080             cls: 'x-html-editor-tip'
44081         },
44082         justifyright : {
44083             title: 'Align Text Right',
44084             text: 'Align text to the right.',
44085             cls: 'x-html-editor-tip'
44086         },
44087         insertunorderedlist : {
44088             title: 'Bullet List',
44089             text: 'Start a bulleted list.',
44090             cls: 'x-html-editor-tip'
44091         },
44092         insertorderedlist : {
44093             title: 'Numbered List',
44094             text: 'Start a numbered list.',
44095             cls: 'x-html-editor-tip'
44096         },
44097         createlink : {
44098             title: 'Hyperlink',
44099             text: 'Make the selected text a hyperlink.',
44100             cls: 'x-html-editor-tip'
44101         },
44102         sourceedit : {
44103             title: 'Source Edit',
44104             text: 'Switch to source editing mode.',
44105             cls: 'x-html-editor-tip'
44106         }
44107     },
44108     // private
44109     onDestroy : function(){
44110         if(this.rendered){
44111             
44112             this.tb.items.each(function(item){
44113                 if(item.menu){
44114                     item.menu.removeAll();
44115                     if(item.menu.el){
44116                         item.menu.el.destroy();
44117                     }
44118                 }
44119                 item.destroy();
44120             });
44121              
44122         }
44123     },
44124     onFirstFocus: function() {
44125         this.tb.items.each(function(item){
44126            item.enable();
44127         });
44128     }
44129 });
44130
44131
44132
44133
44134 // <script type="text/javascript">
44135 /*
44136  * Based on
44137  * Ext JS Library 1.1.1
44138  * Copyright(c) 2006-2007, Ext JS, LLC.
44139  *  
44140  
44141  */
44142
44143  
44144 /**
44145  * @class Roo.form.HtmlEditor.ToolbarContext
44146  * Context Toolbar
44147  * 
44148  * Usage:
44149  *
44150  new Roo.form.HtmlEditor({
44151     ....
44152     toolbars : [
44153         { xtype: 'ToolbarStandard', styles : {} }
44154         { xtype: 'ToolbarContext', disable : {} }
44155     ]
44156 })
44157
44158      
44159  * 
44160  * @config : {Object} disable List of elements to disable.. (not done yet.)
44161  * @config : {Object} styles  Map of styles available.
44162  * 
44163  */
44164
44165 Roo.form.HtmlEditor.ToolbarContext = function(config)
44166 {
44167     
44168     Roo.apply(this, config);
44169     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44170     // dont call parent... till later.
44171     this.styles = this.styles || {};
44172 }
44173
44174  
44175
44176 Roo.form.HtmlEditor.ToolbarContext.types = {
44177     'IMG' : {
44178         width : {
44179             title: "Width",
44180             width: 40
44181         },
44182         height:  {
44183             title: "Height",
44184             width: 40
44185         },
44186         align: {
44187             title: "Align",
44188             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44189             width : 80
44190             
44191         },
44192         border: {
44193             title: "Border",
44194             width: 40
44195         },
44196         alt: {
44197             title: "Alt",
44198             width: 120
44199         },
44200         src : {
44201             title: "Src",
44202             width: 220
44203         }
44204         
44205     },
44206     'A' : {
44207         name : {
44208             title: "Name",
44209             width: 50
44210         },
44211         target:  {
44212             title: "Target",
44213             width: 120
44214         },
44215         href:  {
44216             title: "Href",
44217             width: 220
44218         } // border?
44219         
44220     },
44221     'TABLE' : {
44222         rows : {
44223             title: "Rows",
44224             width: 20
44225         },
44226         cols : {
44227             title: "Cols",
44228             width: 20
44229         },
44230         width : {
44231             title: "Width",
44232             width: 40
44233         },
44234         height : {
44235             title: "Height",
44236             width: 40
44237         },
44238         border : {
44239             title: "Border",
44240             width: 20
44241         }
44242     },
44243     'TD' : {
44244         width : {
44245             title: "Width",
44246             width: 40
44247         },
44248         height : {
44249             title: "Height",
44250             width: 40
44251         },   
44252         align: {
44253             title: "Align",
44254             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44255             width: 80
44256         },
44257         valign: {
44258             title: "Valign",
44259             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44260             width: 80
44261         },
44262         colspan: {
44263             title: "Colspan",
44264             width: 20
44265             
44266         },
44267          'font-family'  : {
44268             title : "Font",
44269             style : 'fontFamily',
44270             displayField: 'display',
44271             optname : 'font-family',
44272             width: 140
44273         }
44274     },
44275     'INPUT' : {
44276         name : {
44277             title: "name",
44278             width: 120
44279         },
44280         value : {
44281             title: "Value",
44282             width: 120
44283         },
44284         width : {
44285             title: "Width",
44286             width: 40
44287         }
44288     },
44289     'LABEL' : {
44290         'for' : {
44291             title: "For",
44292             width: 120
44293         }
44294     },
44295     'TEXTAREA' : {
44296           name : {
44297             title: "name",
44298             width: 120
44299         },
44300         rows : {
44301             title: "Rows",
44302             width: 20
44303         },
44304         cols : {
44305             title: "Cols",
44306             width: 20
44307         }
44308     },
44309     'SELECT' : {
44310         name : {
44311             title: "name",
44312             width: 120
44313         },
44314         selectoptions : {
44315             title: "Options",
44316             width: 200
44317         }
44318     },
44319     
44320     // should we really allow this??
44321     // should this just be 
44322     'BODY' : {
44323         title : {
44324             title: "Title",
44325             width: 200,
44326             disabled : true
44327         }
44328     },
44329     'SPAN' : {
44330         'font-family'  : {
44331             title : "Font",
44332             style : 'fontFamily',
44333             displayField: 'display',
44334             optname : 'font-family',
44335             width: 140
44336         }
44337     },
44338     'DIV' : {
44339         'font-family'  : {
44340             title : "Font",
44341             style : 'fontFamily',
44342             displayField: 'display',
44343             optname : 'font-family',
44344             width: 140
44345         }
44346     },
44347      'P' : {
44348         'font-family'  : {
44349             title : "Font",
44350             style : 'fontFamily',
44351             displayField: 'display',
44352             optname : 'font-family',
44353             width: 140
44354         }
44355     },
44356     
44357     '*' : {
44358         // empty..
44359     }
44360
44361 };
44362
44363 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44364 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44365
44366 Roo.form.HtmlEditor.ToolbarContext.options = {
44367         'font-family'  : [ 
44368                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44369                 [ 'Courier New', 'Courier New'],
44370                 [ 'Tahoma', 'Tahoma'],
44371                 [ 'Times New Roman,serif', 'Times'],
44372                 [ 'Verdana','Verdana' ]
44373         ]
44374 };
44375
44376 // fixme - these need to be configurable..
44377  
44378
44379 Roo.form.HtmlEditor.ToolbarContext.types
44380
44381
44382 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44383     
44384     tb: false,
44385     
44386     rendered: false,
44387     
44388     editor : false,
44389     editorcore : false,
44390     /**
44391      * @cfg {Object} disable  List of toolbar elements to disable
44392          
44393      */
44394     disable : false,
44395     /**
44396      * @cfg {Object} styles List of styles 
44397      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44398      *
44399      * These must be defined in the page, so they get rendered correctly..
44400      * .headline { }
44401      * TD.underline { }
44402      * 
44403      */
44404     styles : false,
44405     
44406     options: false,
44407     
44408     toolbars : false,
44409     
44410     init : function(editor)
44411     {
44412         this.editor = editor;
44413         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44414         var editorcore = this.editorcore;
44415         
44416         var fid = editorcore.frameId;
44417         var etb = this;
44418         function btn(id, toggle, handler){
44419             var xid = fid + '-'+ id ;
44420             return {
44421                 id : xid,
44422                 cmd : id,
44423                 cls : 'x-btn-icon x-edit-'+id,
44424                 enableToggle:toggle !== false,
44425                 scope: editorcore, // was editor...
44426                 handler:handler||editorcore.relayBtnCmd,
44427                 clickEvent:'mousedown',
44428                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44429                 tabIndex:-1
44430             };
44431         }
44432         // create a new element.
44433         var wdiv = editor.wrap.createChild({
44434                 tag: 'div'
44435             }, editor.wrap.dom.firstChild.nextSibling, true);
44436         
44437         // can we do this more than once??
44438         
44439          // stop form submits
44440       
44441  
44442         // disable everything...
44443         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44444         this.toolbars = {};
44445            
44446         for (var i in  ty) {
44447           
44448             this.toolbars[i] = this.buildToolbar(ty[i],i);
44449         }
44450         this.tb = this.toolbars.BODY;
44451         this.tb.el.show();
44452         this.buildFooter();
44453         this.footer.show();
44454         editor.on('hide', function( ) { this.footer.hide() }, this);
44455         editor.on('show', function( ) { this.footer.show() }, this);
44456         
44457          
44458         this.rendered = true;
44459         
44460         // the all the btns;
44461         editor.on('editorevent', this.updateToolbar, this);
44462         // other toolbars need to implement this..
44463         //editor.on('editmodechange', this.updateToolbar, this);
44464     },
44465     
44466     
44467     
44468     /**
44469      * Protected method that will not generally be called directly. It triggers
44470      * a toolbar update by reading the markup state of the current selection in the editor.
44471      *
44472      * Note you can force an update by calling on('editorevent', scope, false)
44473      */
44474     updateToolbar: function(editor,ev,sel){
44475
44476         //Roo.log(ev);
44477         // capture mouse up - this is handy for selecting images..
44478         // perhaps should go somewhere else...
44479         if(!this.editorcore.activated){
44480              this.editor.onFirstFocus();
44481             return;
44482         }
44483         
44484         
44485         
44486         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44487         // selectNode - might want to handle IE?
44488         if (ev &&
44489             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44490             ev.target && ev.target.tagName == 'IMG') {
44491             // they have click on an image...
44492             // let's see if we can change the selection...
44493             sel = ev.target;
44494          
44495               var nodeRange = sel.ownerDocument.createRange();
44496             try {
44497                 nodeRange.selectNode(sel);
44498             } catch (e) {
44499                 nodeRange.selectNodeContents(sel);
44500             }
44501             //nodeRange.collapse(true);
44502             var s = this.editorcore.win.getSelection();
44503             s.removeAllRanges();
44504             s.addRange(nodeRange);
44505         }  
44506         
44507       
44508         var updateFooter = sel ? false : true;
44509         
44510         
44511         var ans = this.editorcore.getAllAncestors();
44512         
44513         // pick
44514         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44515         
44516         if (!sel) { 
44517             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44518             sel = sel ? sel : this.editorcore.doc.body;
44519             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44520             
44521         }
44522         // pick a menu that exists..
44523         var tn = sel.tagName.toUpperCase();
44524         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44525         
44526         tn = sel.tagName.toUpperCase();
44527         
44528         var lastSel = this.tb.selectedNode
44529         
44530         this.tb.selectedNode = sel;
44531         
44532         // if current menu does not match..
44533         
44534         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44535                 
44536             this.tb.el.hide();
44537             ///console.log("show: " + tn);
44538             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44539             this.tb.el.show();
44540             // update name
44541             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44542             
44543             
44544             // update attributes
44545             if (this.tb.fields) {
44546                 this.tb.fields.each(function(e) {
44547                     if (e.stylename) {
44548                         e.setValue(sel.style[e.stylename]);
44549                         return;
44550                     } 
44551                    e.setValue(sel.getAttribute(e.attrname));
44552                 });
44553             }
44554             
44555             var hasStyles = false;
44556             for(var i in this.styles) {
44557                 hasStyles = true;
44558                 break;
44559             }
44560             
44561             // update styles
44562             if (hasStyles) { 
44563                 var st = this.tb.fields.item(0);
44564                 
44565                 st.store.removeAll();
44566                
44567                 
44568                 var cn = sel.className.split(/\s+/);
44569                 
44570                 var avs = [];
44571                 if (this.styles['*']) {
44572                     
44573                     Roo.each(this.styles['*'], function(v) {
44574                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44575                     });
44576                 }
44577                 if (this.styles[tn]) { 
44578                     Roo.each(this.styles[tn], function(v) {
44579                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44580                     });
44581                 }
44582                 
44583                 st.store.loadData(avs);
44584                 st.collapse();
44585                 st.setValue(cn);
44586             }
44587             // flag our selected Node.
44588             this.tb.selectedNode = sel;
44589            
44590            
44591             Roo.menu.MenuMgr.hideAll();
44592
44593         }
44594         
44595         if (!updateFooter) {
44596             //this.footDisp.dom.innerHTML = ''; 
44597             return;
44598         }
44599         // update the footer
44600         //
44601         var html = '';
44602         
44603         this.footerEls = ans.reverse();
44604         Roo.each(this.footerEls, function(a,i) {
44605             if (!a) { return; }
44606             html += html.length ? ' &gt; '  :  '';
44607             
44608             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44609             
44610         });
44611        
44612         // 
44613         var sz = this.footDisp.up('td').getSize();
44614         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44615         this.footDisp.dom.style.marginLeft = '5px';
44616         
44617         this.footDisp.dom.style.overflow = 'hidden';
44618         
44619         this.footDisp.dom.innerHTML = html;
44620             
44621         //this.editorsyncValue();
44622     },
44623      
44624     
44625    
44626        
44627     // private
44628     onDestroy : function(){
44629         if(this.rendered){
44630             
44631             this.tb.items.each(function(item){
44632                 if(item.menu){
44633                     item.menu.removeAll();
44634                     if(item.menu.el){
44635                         item.menu.el.destroy();
44636                     }
44637                 }
44638                 item.destroy();
44639             });
44640              
44641         }
44642     },
44643     onFirstFocus: function() {
44644         // need to do this for all the toolbars..
44645         this.tb.items.each(function(item){
44646            item.enable();
44647         });
44648     },
44649     buildToolbar: function(tlist, nm)
44650     {
44651         var editor = this.editor;
44652         var editorcore = this.editorcore;
44653          // create a new element.
44654         var wdiv = editor.wrap.createChild({
44655                 tag: 'div'
44656             }, editor.wrap.dom.firstChild.nextSibling, true);
44657         
44658        
44659         var tb = new Roo.Toolbar(wdiv);
44660         // add the name..
44661         
44662         tb.add(nm+ ":&nbsp;");
44663         
44664         var styles = [];
44665         for(var i in this.styles) {
44666             styles.push(i);
44667         }
44668         
44669         // styles...
44670         if (styles && styles.length) {
44671             
44672             // this needs a multi-select checkbox...
44673             tb.addField( new Roo.form.ComboBox({
44674                 store: new Roo.data.SimpleStore({
44675                     id : 'val',
44676                     fields: ['val', 'selected'],
44677                     data : [] 
44678                 }),
44679                 name : '-roo-edit-className',
44680                 attrname : 'className',
44681                 displayField: 'val',
44682                 typeAhead: false,
44683                 mode: 'local',
44684                 editable : false,
44685                 triggerAction: 'all',
44686                 emptyText:'Select Style',
44687                 selectOnFocus:true,
44688                 width: 130,
44689                 listeners : {
44690                     'select': function(c, r, i) {
44691                         // initial support only for on class per el..
44692                         tb.selectedNode.className =  r ? r.get('val') : '';
44693                         editorcore.syncValue();
44694                     }
44695                 }
44696     
44697             }));
44698         }
44699         
44700         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44701         var tbops = tbc.options;
44702         
44703         for (var i in tlist) {
44704             
44705             var item = tlist[i];
44706             tb.add(item.title + ":&nbsp;");
44707             
44708             
44709             //optname == used so you can configure the options available..
44710             var opts = item.opts ? item.opts : false;
44711             if (item.optname) {
44712                 opts = tbops[item.optname];
44713            
44714             }
44715             
44716             if (opts) {
44717                 // opts == pulldown..
44718                 tb.addField( new Roo.form.ComboBox({
44719                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44720                         id : 'val',
44721                         fields: ['val', 'display'],
44722                         data : opts  
44723                     }),
44724                     name : '-roo-edit-' + i,
44725                     attrname : i,
44726                     stylename : item.style ? item.style : false,
44727                     displayField: item.displayField ? item.displayField : 'val',
44728                     valueField :  'val',
44729                     typeAhead: false,
44730                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44731                     editable : false,
44732                     triggerAction: 'all',
44733                     emptyText:'Select',
44734                     selectOnFocus:true,
44735                     width: item.width ? item.width  : 130,
44736                     listeners : {
44737                         'select': function(c, r, i) {
44738                             if (c.stylename) {
44739                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44740                                 return;
44741                             }
44742                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44743                         }
44744                     }
44745
44746                 }));
44747                 continue;
44748                     
44749                  
44750                 
44751                 tb.addField( new Roo.form.TextField({
44752                     name: i,
44753                     width: 100,
44754                     //allowBlank:false,
44755                     value: ''
44756                 }));
44757                 continue;
44758             }
44759             tb.addField( new Roo.form.TextField({
44760                 name: '-roo-edit-' + i,
44761                 attrname : i,
44762                 
44763                 width: item.width,
44764                 //allowBlank:true,
44765                 value: '',
44766                 listeners: {
44767                     'change' : function(f, nv, ov) {
44768                         tb.selectedNode.setAttribute(f.attrname, nv);
44769                     }
44770                 }
44771             }));
44772              
44773         }
44774         
44775         var _this = this;
44776         
44777         if(nm == 'BODY'){
44778             tb.addSeparator();
44779         
44780             tb.addButton( {
44781                 text: 'Stylesheets',
44782
44783                 listeners : {
44784                     click : function ()
44785                     {
44786                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44787                     }
44788                 }
44789             });
44790         }
44791         
44792         tb.addFill();
44793         tb.addButton( {
44794             text: 'Remove Tag',
44795     
44796             listeners : {
44797                 click : function ()
44798                 {
44799                     // remove
44800                     // undo does not work.
44801                      
44802                     var sn = tb.selectedNode;
44803                     
44804                     var pn = sn.parentNode;
44805                     
44806                     var stn =  sn.childNodes[0];
44807                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44808                     while (sn.childNodes.length) {
44809                         var node = sn.childNodes[0];
44810                         sn.removeChild(node);
44811                         //Roo.log(node);
44812                         pn.insertBefore(node, sn);
44813                         
44814                     }
44815                     pn.removeChild(sn);
44816                     var range = editorcore.createRange();
44817         
44818                     range.setStart(stn,0);
44819                     range.setEnd(en,0); //????
44820                     //range.selectNode(sel);
44821                     
44822                     
44823                     var selection = editorcore.getSelection();
44824                     selection.removeAllRanges();
44825                     selection.addRange(range);
44826                     
44827                     
44828                     
44829                     //_this.updateToolbar(null, null, pn);
44830                     _this.updateToolbar(null, null, null);
44831                     _this.footDisp.dom.innerHTML = ''; 
44832                 }
44833             }
44834             
44835                     
44836                 
44837             
44838         });
44839         
44840         
44841         tb.el.on('click', function(e){
44842             e.preventDefault(); // what does this do?
44843         });
44844         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44845         tb.el.hide();
44846         tb.name = nm;
44847         // dont need to disable them... as they will get hidden
44848         return tb;
44849          
44850         
44851     },
44852     buildFooter : function()
44853     {
44854         
44855         var fel = this.editor.wrap.createChild();
44856         this.footer = new Roo.Toolbar(fel);
44857         // toolbar has scrolly on left / right?
44858         var footDisp= new Roo.Toolbar.Fill();
44859         var _t = this;
44860         this.footer.add(
44861             {
44862                 text : '&lt;',
44863                 xtype: 'Button',
44864                 handler : function() {
44865                     _t.footDisp.scrollTo('left',0,true)
44866                 }
44867             }
44868         );
44869         this.footer.add( footDisp );
44870         this.footer.add( 
44871             {
44872                 text : '&gt;',
44873                 xtype: 'Button',
44874                 handler : function() {
44875                     // no animation..
44876                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44877                 }
44878             }
44879         );
44880         var fel = Roo.get(footDisp.el);
44881         fel.addClass('x-editor-context');
44882         this.footDispWrap = fel; 
44883         this.footDispWrap.overflow  = 'hidden';
44884         
44885         this.footDisp = fel.createChild();
44886         this.footDispWrap.on('click', this.onContextClick, this)
44887         
44888         
44889     },
44890     onContextClick : function (ev,dom)
44891     {
44892         ev.preventDefault();
44893         var  cn = dom.className;
44894         //Roo.log(cn);
44895         if (!cn.match(/x-ed-loc-/)) {
44896             return;
44897         }
44898         var n = cn.split('-').pop();
44899         var ans = this.footerEls;
44900         var sel = ans[n];
44901         
44902          // pick
44903         var range = this.editorcore.createRange();
44904         
44905         range.selectNodeContents(sel);
44906         //range.selectNode(sel);
44907         
44908         
44909         var selection = this.editorcore.getSelection();
44910         selection.removeAllRanges();
44911         selection.addRange(range);
44912         
44913         
44914         
44915         this.updateToolbar(null, null, sel);
44916         
44917         
44918     }
44919     
44920     
44921     
44922     
44923     
44924 });
44925
44926
44927
44928
44929
44930 /*
44931  * Based on:
44932  * Ext JS Library 1.1.1
44933  * Copyright(c) 2006-2007, Ext JS, LLC.
44934  *
44935  * Originally Released Under LGPL - original licence link has changed is not relivant.
44936  *
44937  * Fork - LGPL
44938  * <script type="text/javascript">
44939  */
44940  
44941 /**
44942  * @class Roo.form.BasicForm
44943  * @extends Roo.util.Observable
44944  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44945  * @constructor
44946  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44947  * @param {Object} config Configuration options
44948  */
44949 Roo.form.BasicForm = function(el, config){
44950     this.allItems = [];
44951     this.childForms = [];
44952     Roo.apply(this, config);
44953     /*
44954      * The Roo.form.Field items in this form.
44955      * @type MixedCollection
44956      */
44957      
44958      
44959     this.items = new Roo.util.MixedCollection(false, function(o){
44960         return o.id || (o.id = Roo.id());
44961     });
44962     this.addEvents({
44963         /**
44964          * @event beforeaction
44965          * Fires before any action is performed. Return false to cancel the action.
44966          * @param {Form} this
44967          * @param {Action} action The action to be performed
44968          */
44969         beforeaction: true,
44970         /**
44971          * @event actionfailed
44972          * Fires when an action fails.
44973          * @param {Form} this
44974          * @param {Action} action The action that failed
44975          */
44976         actionfailed : true,
44977         /**
44978          * @event actioncomplete
44979          * Fires when an action is completed.
44980          * @param {Form} this
44981          * @param {Action} action The action that completed
44982          */
44983         actioncomplete : true
44984     });
44985     if(el){
44986         this.initEl(el);
44987     }
44988     Roo.form.BasicForm.superclass.constructor.call(this);
44989 };
44990
44991 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44992     /**
44993      * @cfg {String} method
44994      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44995      */
44996     /**
44997      * @cfg {DataReader} reader
44998      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44999      * This is optional as there is built-in support for processing JSON.
45000      */
45001     /**
45002      * @cfg {DataReader} errorReader
45003      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45004      * This is completely optional as there is built-in support for processing JSON.
45005      */
45006     /**
45007      * @cfg {String} url
45008      * The URL to use for form actions if one isn't supplied in the action options.
45009      */
45010     /**
45011      * @cfg {Boolean} fileUpload
45012      * Set to true if this form is a file upload.
45013      */
45014      
45015     /**
45016      * @cfg {Object} baseParams
45017      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45018      */
45019      /**
45020      
45021     /**
45022      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45023      */
45024     timeout: 30,
45025
45026     // private
45027     activeAction : null,
45028
45029     /**
45030      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45031      * or setValues() data instead of when the form was first created.
45032      */
45033     trackResetOnLoad : false,
45034     
45035     
45036     /**
45037      * childForms - used for multi-tab forms
45038      * @type {Array}
45039      */
45040     childForms : false,
45041     
45042     /**
45043      * allItems - full list of fields.
45044      * @type {Array}
45045      */
45046     allItems : false,
45047     
45048     /**
45049      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45050      * element by passing it or its id or mask the form itself by passing in true.
45051      * @type Mixed
45052      */
45053     waitMsgTarget : false,
45054
45055     // private
45056     initEl : function(el){
45057         this.el = Roo.get(el);
45058         this.id = this.el.id || Roo.id();
45059         this.el.on('submit', this.onSubmit, this);
45060         this.el.addClass('x-form');
45061     },
45062
45063     // private
45064     onSubmit : function(e){
45065         e.stopEvent();
45066     },
45067
45068     /**
45069      * Returns true if client-side validation on the form is successful.
45070      * @return Boolean
45071      */
45072     isValid : function(){
45073         var valid = true;
45074         this.items.each(function(f){
45075            if(!f.validate()){
45076                valid = false;
45077            }
45078         });
45079         return valid;
45080     },
45081
45082     /**
45083      * Returns true if any fields in this form have changed since their original load.
45084      * @return Boolean
45085      */
45086     isDirty : function(){
45087         var dirty = false;
45088         this.items.each(function(f){
45089            if(f.isDirty()){
45090                dirty = true;
45091                return false;
45092            }
45093         });
45094         return dirty;
45095     },
45096
45097     /**
45098      * Performs a predefined action (submit or load) or custom actions you define on this form.
45099      * @param {String} actionName The name of the action type
45100      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45101      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45102      * accept other config options):
45103      * <pre>
45104 Property          Type             Description
45105 ----------------  ---------------  ----------------------------------------------------------------------------------
45106 url               String           The url for the action (defaults to the form's url)
45107 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45108 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45109 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45110                                    validate the form on the client (defaults to false)
45111      * </pre>
45112      * @return {BasicForm} this
45113      */
45114     doAction : function(action, options){
45115         if(typeof action == 'string'){
45116             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45117         }
45118         if(this.fireEvent('beforeaction', this, action) !== false){
45119             this.beforeAction(action);
45120             action.run.defer(100, action);
45121         }
45122         return this;
45123     },
45124
45125     /**
45126      * Shortcut to do a submit action.
45127      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45128      * @return {BasicForm} this
45129      */
45130     submit : function(options){
45131         this.doAction('submit', options);
45132         return this;
45133     },
45134
45135     /**
45136      * Shortcut to do a load action.
45137      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45138      * @return {BasicForm} this
45139      */
45140     load : function(options){
45141         this.doAction('load', options);
45142         return this;
45143     },
45144
45145     /**
45146      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45147      * @param {Record} record The record to edit
45148      * @return {BasicForm} this
45149      */
45150     updateRecord : function(record){
45151         record.beginEdit();
45152         var fs = record.fields;
45153         fs.each(function(f){
45154             var field = this.findField(f.name);
45155             if(field){
45156                 record.set(f.name, field.getValue());
45157             }
45158         }, this);
45159         record.endEdit();
45160         return this;
45161     },
45162
45163     /**
45164      * Loads an Roo.data.Record into this form.
45165      * @param {Record} record The record to load
45166      * @return {BasicForm} this
45167      */
45168     loadRecord : function(record){
45169         this.setValues(record.data);
45170         return this;
45171     },
45172
45173     // private
45174     beforeAction : function(action){
45175         var o = action.options;
45176         
45177        
45178         if(this.waitMsgTarget === true){
45179             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45180         }else if(this.waitMsgTarget){
45181             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45182             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45183         }else {
45184             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45185         }
45186          
45187     },
45188
45189     // private
45190     afterAction : function(action, success){
45191         this.activeAction = null;
45192         var o = action.options;
45193         
45194         if(this.waitMsgTarget === true){
45195             this.el.unmask();
45196         }else if(this.waitMsgTarget){
45197             this.waitMsgTarget.unmask();
45198         }else{
45199             Roo.MessageBox.updateProgress(1);
45200             Roo.MessageBox.hide();
45201         }
45202          
45203         if(success){
45204             if(o.reset){
45205                 this.reset();
45206             }
45207             Roo.callback(o.success, o.scope, [this, action]);
45208             this.fireEvent('actioncomplete', this, action);
45209             
45210         }else{
45211             
45212             // failure condition..
45213             // we have a scenario where updates need confirming.
45214             // eg. if a locking scenario exists..
45215             // we look for { errors : { needs_confirm : true }} in the response.
45216             if (
45217                 (typeof(action.result) != 'undefined')  &&
45218                 (typeof(action.result.errors) != 'undefined')  &&
45219                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45220            ){
45221                 var _t = this;
45222                 Roo.MessageBox.confirm(
45223                     "Change requires confirmation",
45224                     action.result.errorMsg,
45225                     function(r) {
45226                         if (r != 'yes') {
45227                             return;
45228                         }
45229                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45230                     }
45231                     
45232                 );
45233                 
45234                 
45235                 
45236                 return;
45237             }
45238             
45239             Roo.callback(o.failure, o.scope, [this, action]);
45240             // show an error message if no failed handler is set..
45241             if (!this.hasListener('actionfailed')) {
45242                 Roo.MessageBox.alert("Error",
45243                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45244                         action.result.errorMsg :
45245                         "Saving Failed, please check your entries or try again"
45246                 );
45247             }
45248             
45249             this.fireEvent('actionfailed', this, action);
45250         }
45251         
45252     },
45253
45254     /**
45255      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45256      * @param {String} id The value to search for
45257      * @return Field
45258      */
45259     findField : function(id){
45260         var field = this.items.get(id);
45261         if(!field){
45262             this.items.each(function(f){
45263                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45264                     field = f;
45265                     return false;
45266                 }
45267             });
45268         }
45269         return field || null;
45270     },
45271
45272     /**
45273      * Add a secondary form to this one, 
45274      * Used to provide tabbed forms. One form is primary, with hidden values 
45275      * which mirror the elements from the other forms.
45276      * 
45277      * @param {Roo.form.Form} form to add.
45278      * 
45279      */
45280     addForm : function(form)
45281     {
45282        
45283         if (this.childForms.indexOf(form) > -1) {
45284             // already added..
45285             return;
45286         }
45287         this.childForms.push(form);
45288         var n = '';
45289         Roo.each(form.allItems, function (fe) {
45290             
45291             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45292             if (this.findField(n)) { // already added..
45293                 return;
45294             }
45295             var add = new Roo.form.Hidden({
45296                 name : n
45297             });
45298             add.render(this.el);
45299             
45300             this.add( add );
45301         }, this);
45302         
45303     },
45304     /**
45305      * Mark fields in this form invalid in bulk.
45306      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45307      * @return {BasicForm} this
45308      */
45309     markInvalid : function(errors){
45310         if(errors instanceof Array){
45311             for(var i = 0, len = errors.length; i < len; i++){
45312                 var fieldError = errors[i];
45313                 var f = this.findField(fieldError.id);
45314                 if(f){
45315                     f.markInvalid(fieldError.msg);
45316                 }
45317             }
45318         }else{
45319             var field, id;
45320             for(id in errors){
45321                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45322                     field.markInvalid(errors[id]);
45323                 }
45324             }
45325         }
45326         Roo.each(this.childForms || [], function (f) {
45327             f.markInvalid(errors);
45328         });
45329         
45330         return this;
45331     },
45332
45333     /**
45334      * Set values for fields in this form in bulk.
45335      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45336      * @return {BasicForm} this
45337      */
45338     setValues : function(values){
45339         if(values instanceof Array){ // array of objects
45340             for(var i = 0, len = values.length; i < len; i++){
45341                 var v = values[i];
45342                 var f = this.findField(v.id);
45343                 if(f){
45344                     f.setValue(v.value);
45345                     if(this.trackResetOnLoad){
45346                         f.originalValue = f.getValue();
45347                     }
45348                 }
45349             }
45350         }else{ // object hash
45351             var field, id;
45352             for(id in values){
45353                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45354                     
45355                     if (field.setFromData && 
45356                         field.valueField && 
45357                         field.displayField &&
45358                         // combos' with local stores can 
45359                         // be queried via setValue()
45360                         // to set their value..
45361                         (field.store && !field.store.isLocal)
45362                         ) {
45363                         // it's a combo
45364                         var sd = { };
45365                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45366                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45367                         field.setFromData(sd);
45368                         
45369                     } else {
45370                         field.setValue(values[id]);
45371                     }
45372                     
45373                     
45374                     if(this.trackResetOnLoad){
45375                         field.originalValue = field.getValue();
45376                     }
45377                 }
45378             }
45379         }
45380          
45381         Roo.each(this.childForms || [], function (f) {
45382             f.setValues(values);
45383         });
45384                 
45385         return this;
45386     },
45387
45388     /**
45389      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45390      * they are returned as an array.
45391      * @param {Boolean} asString
45392      * @return {Object}
45393      */
45394     getValues : function(asString){
45395         if (this.childForms) {
45396             // copy values from the child forms
45397             Roo.each(this.childForms, function (f) {
45398                 this.setValues(f.getValues());
45399             }, this);
45400         }
45401         
45402         
45403         
45404         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45405         if(asString === true){
45406             return fs;
45407         }
45408         return Roo.urlDecode(fs);
45409     },
45410     
45411     /**
45412      * Returns the fields in this form as an object with key/value pairs. 
45413      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45414      * @return {Object}
45415      */
45416     getFieldValues : function(with_hidden)
45417     {
45418         if (this.childForms) {
45419             // copy values from the child forms
45420             // should this call getFieldValues - probably not as we do not currently copy
45421             // hidden fields when we generate..
45422             Roo.each(this.childForms, function (f) {
45423                 this.setValues(f.getValues());
45424             }, this);
45425         }
45426         
45427         var ret = {};
45428         this.items.each(function(f){
45429             if (!f.getName()) {
45430                 return;
45431             }
45432             var v = f.getValue();
45433             if (f.inputType =='radio') {
45434                 if (typeof(ret[f.getName()]) == 'undefined') {
45435                     ret[f.getName()] = ''; // empty..
45436                 }
45437                 
45438                 if (!f.el.dom.checked) {
45439                     return;
45440                     
45441                 }
45442                 v = f.el.dom.value;
45443                 
45444             }
45445             
45446             // not sure if this supported any more..
45447             if ((typeof(v) == 'object') && f.getRawValue) {
45448                 v = f.getRawValue() ; // dates..
45449             }
45450             // combo boxes where name != hiddenName...
45451             if (f.name != f.getName()) {
45452                 ret[f.name] = f.getRawValue();
45453             }
45454             ret[f.getName()] = v;
45455         });
45456         
45457         return ret;
45458     },
45459
45460     /**
45461      * Clears all invalid messages in this form.
45462      * @return {BasicForm} this
45463      */
45464     clearInvalid : function(){
45465         this.items.each(function(f){
45466            f.clearInvalid();
45467         });
45468         
45469         Roo.each(this.childForms || [], function (f) {
45470             f.clearInvalid();
45471         });
45472         
45473         
45474         return this;
45475     },
45476
45477     /**
45478      * Resets this form.
45479      * @return {BasicForm} this
45480      */
45481     reset : function(){
45482         this.items.each(function(f){
45483             f.reset();
45484         });
45485         
45486         Roo.each(this.childForms || [], function (f) {
45487             f.reset();
45488         });
45489        
45490         
45491         return this;
45492     },
45493
45494     /**
45495      * Add Roo.form components to this form.
45496      * @param {Field} field1
45497      * @param {Field} field2 (optional)
45498      * @param {Field} etc (optional)
45499      * @return {BasicForm} this
45500      */
45501     add : function(){
45502         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45503         return this;
45504     },
45505
45506
45507     /**
45508      * Removes a field from the items collection (does NOT remove its markup).
45509      * @param {Field} field
45510      * @return {BasicForm} this
45511      */
45512     remove : function(field){
45513         this.items.remove(field);
45514         return this;
45515     },
45516
45517     /**
45518      * Looks at the fields in this form, checks them for an id attribute,
45519      * and calls applyTo on the existing dom element with that id.
45520      * @return {BasicForm} this
45521      */
45522     render : function(){
45523         this.items.each(function(f){
45524             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45525                 f.applyTo(f.id);
45526             }
45527         });
45528         return this;
45529     },
45530
45531     /**
45532      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45533      * @param {Object} values
45534      * @return {BasicForm} this
45535      */
45536     applyToFields : function(o){
45537         this.items.each(function(f){
45538            Roo.apply(f, o);
45539         });
45540         return this;
45541     },
45542
45543     /**
45544      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45545      * @param {Object} values
45546      * @return {BasicForm} this
45547      */
45548     applyIfToFields : function(o){
45549         this.items.each(function(f){
45550            Roo.applyIf(f, o);
45551         });
45552         return this;
45553     }
45554 });
45555
45556 // back compat
45557 Roo.BasicForm = Roo.form.BasicForm;/*
45558  * Based on:
45559  * Ext JS Library 1.1.1
45560  * Copyright(c) 2006-2007, Ext JS, LLC.
45561  *
45562  * Originally Released Under LGPL - original licence link has changed is not relivant.
45563  *
45564  * Fork - LGPL
45565  * <script type="text/javascript">
45566  */
45567
45568 /**
45569  * @class Roo.form.Form
45570  * @extends Roo.form.BasicForm
45571  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45572  * @constructor
45573  * @param {Object} config Configuration options
45574  */
45575 Roo.form.Form = function(config){
45576     var xitems =  [];
45577     if (config.items) {
45578         xitems = config.items;
45579         delete config.items;
45580     }
45581    
45582     
45583     Roo.form.Form.superclass.constructor.call(this, null, config);
45584     this.url = this.url || this.action;
45585     if(!this.root){
45586         this.root = new Roo.form.Layout(Roo.applyIf({
45587             id: Roo.id()
45588         }, config));
45589     }
45590     this.active = this.root;
45591     /**
45592      * Array of all the buttons that have been added to this form via {@link addButton}
45593      * @type Array
45594      */
45595     this.buttons = [];
45596     this.allItems = [];
45597     this.addEvents({
45598         /**
45599          * @event clientvalidation
45600          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45601          * @param {Form} this
45602          * @param {Boolean} valid true if the form has passed client-side validation
45603          */
45604         clientvalidation: true,
45605         /**
45606          * @event rendered
45607          * Fires when the form is rendered
45608          * @param {Roo.form.Form} form
45609          */
45610         rendered : true
45611     });
45612     
45613     if (this.progressUrl) {
45614             // push a hidden field onto the list of fields..
45615             this.addxtype( {
45616                     xns: Roo.form, 
45617                     xtype : 'Hidden', 
45618                     name : 'UPLOAD_IDENTIFIER' 
45619             });
45620         }
45621         
45622     
45623     Roo.each(xitems, this.addxtype, this);
45624     
45625     
45626     
45627 };
45628
45629 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45630     /**
45631      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45632      */
45633     /**
45634      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45635      */
45636     /**
45637      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45638      */
45639     buttonAlign:'center',
45640
45641     /**
45642      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45643      */
45644     minButtonWidth:75,
45645
45646     /**
45647      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45648      * This property cascades to child containers if not set.
45649      */
45650     labelAlign:'left',
45651
45652     /**
45653      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45654      * fires a looping event with that state. This is required to bind buttons to the valid
45655      * state using the config value formBind:true on the button.
45656      */
45657     monitorValid : false,
45658
45659     /**
45660      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45661      */
45662     monitorPoll : 200,
45663     
45664     /**
45665      * @cfg {String} progressUrl - Url to return progress data 
45666      */
45667     
45668     progressUrl : false,
45669   
45670     /**
45671      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45672      * fields are added and the column is closed. If no fields are passed the column remains open
45673      * until end() is called.
45674      * @param {Object} config The config to pass to the column
45675      * @param {Field} field1 (optional)
45676      * @param {Field} field2 (optional)
45677      * @param {Field} etc (optional)
45678      * @return Column The column container object
45679      */
45680     column : function(c){
45681         var col = new Roo.form.Column(c);
45682         this.start(col);
45683         if(arguments.length > 1){ // duplicate code required because of Opera
45684             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45685             this.end();
45686         }
45687         return col;
45688     },
45689
45690     /**
45691      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45692      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45693      * until end() is called.
45694      * @param {Object} config The config to pass to the fieldset
45695      * @param {Field} field1 (optional)
45696      * @param {Field} field2 (optional)
45697      * @param {Field} etc (optional)
45698      * @return FieldSet The fieldset container object
45699      */
45700     fieldset : function(c){
45701         var fs = new Roo.form.FieldSet(c);
45702         this.start(fs);
45703         if(arguments.length > 1){ // duplicate code required because of Opera
45704             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45705             this.end();
45706         }
45707         return fs;
45708     },
45709
45710     /**
45711      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45712      * fields are added and the container is closed. If no fields are passed the container remains open
45713      * until end() is called.
45714      * @param {Object} config The config to pass to the Layout
45715      * @param {Field} field1 (optional)
45716      * @param {Field} field2 (optional)
45717      * @param {Field} etc (optional)
45718      * @return Layout The container object
45719      */
45720     container : function(c){
45721         var l = new Roo.form.Layout(c);
45722         this.start(l);
45723         if(arguments.length > 1){ // duplicate code required because of Opera
45724             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45725             this.end();
45726         }
45727         return l;
45728     },
45729
45730     /**
45731      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45732      * @param {Object} container A Roo.form.Layout or subclass of Layout
45733      * @return {Form} this
45734      */
45735     start : function(c){
45736         // cascade label info
45737         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45738         this.active.stack.push(c);
45739         c.ownerCt = this.active;
45740         this.active = c;
45741         return this;
45742     },
45743
45744     /**
45745      * Closes the current open container
45746      * @return {Form} this
45747      */
45748     end : function(){
45749         if(this.active == this.root){
45750             return this;
45751         }
45752         this.active = this.active.ownerCt;
45753         return this;
45754     },
45755
45756     /**
45757      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45758      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45759      * as the label of the field.
45760      * @param {Field} field1
45761      * @param {Field} field2 (optional)
45762      * @param {Field} etc. (optional)
45763      * @return {Form} this
45764      */
45765     add : function(){
45766         this.active.stack.push.apply(this.active.stack, arguments);
45767         this.allItems.push.apply(this.allItems,arguments);
45768         var r = [];
45769         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45770             if(a[i].isFormField){
45771                 r.push(a[i]);
45772             }
45773         }
45774         if(r.length > 0){
45775             Roo.form.Form.superclass.add.apply(this, r);
45776         }
45777         return this;
45778     },
45779     
45780
45781     
45782     
45783     
45784      /**
45785      * Find any element that has been added to a form, using it's ID or name
45786      * This can include framesets, columns etc. along with regular fields..
45787      * @param {String} id - id or name to find.
45788      
45789      * @return {Element} e - or false if nothing found.
45790      */
45791     findbyId : function(id)
45792     {
45793         var ret = false;
45794         if (!id) {
45795             return ret;
45796         }
45797         Roo.each(this.allItems, function(f){
45798             if (f.id == id || f.name == id ){
45799                 ret = f;
45800                 return false;
45801             }
45802         });
45803         return ret;
45804     },
45805
45806     
45807     
45808     /**
45809      * Render this form into the passed container. This should only be called once!
45810      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45811      * @return {Form} this
45812      */
45813     render : function(ct)
45814     {
45815         
45816         
45817         
45818         ct = Roo.get(ct);
45819         var o = this.autoCreate || {
45820             tag: 'form',
45821             method : this.method || 'POST',
45822             id : this.id || Roo.id()
45823         };
45824         this.initEl(ct.createChild(o));
45825
45826         this.root.render(this.el);
45827         
45828        
45829              
45830         this.items.each(function(f){
45831             f.render('x-form-el-'+f.id);
45832         });
45833
45834         if(this.buttons.length > 0){
45835             // tables are required to maintain order and for correct IE layout
45836             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45837                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45838                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45839             }}, null, true);
45840             var tr = tb.getElementsByTagName('tr')[0];
45841             for(var i = 0, len = this.buttons.length; i < len; i++) {
45842                 var b = this.buttons[i];
45843                 var td = document.createElement('td');
45844                 td.className = 'x-form-btn-td';
45845                 b.render(tr.appendChild(td));
45846             }
45847         }
45848         if(this.monitorValid){ // initialize after render
45849             this.startMonitoring();
45850         }
45851         this.fireEvent('rendered', this);
45852         return this;
45853     },
45854
45855     /**
45856      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45857      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45858      * object or a valid Roo.DomHelper element config
45859      * @param {Function} handler The function called when the button is clicked
45860      * @param {Object} scope (optional) The scope of the handler function
45861      * @return {Roo.Button}
45862      */
45863     addButton : function(config, handler, scope){
45864         var bc = {
45865             handler: handler,
45866             scope: scope,
45867             minWidth: this.minButtonWidth,
45868             hideParent:true
45869         };
45870         if(typeof config == "string"){
45871             bc.text = config;
45872         }else{
45873             Roo.apply(bc, config);
45874         }
45875         var btn = new Roo.Button(null, bc);
45876         this.buttons.push(btn);
45877         return btn;
45878     },
45879
45880      /**
45881      * Adds a series of form elements (using the xtype property as the factory method.
45882      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45883      * @param {Object} config 
45884      */
45885     
45886     addxtype : function()
45887     {
45888         var ar = Array.prototype.slice.call(arguments, 0);
45889         var ret = false;
45890         for(var i = 0; i < ar.length; i++) {
45891             if (!ar[i]) {
45892                 continue; // skip -- if this happends something invalid got sent, we 
45893                 // should ignore it, as basically that interface element will not show up
45894                 // and that should be pretty obvious!!
45895             }
45896             
45897             if (Roo.form[ar[i].xtype]) {
45898                 ar[i].form = this;
45899                 var fe = Roo.factory(ar[i], Roo.form);
45900                 if (!ret) {
45901                     ret = fe;
45902                 }
45903                 fe.form = this;
45904                 if (fe.store) {
45905                     fe.store.form = this;
45906                 }
45907                 if (fe.isLayout) {  
45908                          
45909                     this.start(fe);
45910                     this.allItems.push(fe);
45911                     if (fe.items && fe.addxtype) {
45912                         fe.addxtype.apply(fe, fe.items);
45913                         delete fe.items;
45914                     }
45915                      this.end();
45916                     continue;
45917                 }
45918                 
45919                 
45920                  
45921                 this.add(fe);
45922               //  console.log('adding ' + ar[i].xtype);
45923             }
45924             if (ar[i].xtype == 'Button') {  
45925                 //console.log('adding button');
45926                 //console.log(ar[i]);
45927                 this.addButton(ar[i]);
45928                 this.allItems.push(fe);
45929                 continue;
45930             }
45931             
45932             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45933                 alert('end is not supported on xtype any more, use items');
45934             //    this.end();
45935             //    //console.log('adding end');
45936             }
45937             
45938         }
45939         return ret;
45940     },
45941     
45942     /**
45943      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45944      * option "monitorValid"
45945      */
45946     startMonitoring : function(){
45947         if(!this.bound){
45948             this.bound = true;
45949             Roo.TaskMgr.start({
45950                 run : this.bindHandler,
45951                 interval : this.monitorPoll || 200,
45952                 scope: this
45953             });
45954         }
45955     },
45956
45957     /**
45958      * Stops monitoring of the valid state of this form
45959      */
45960     stopMonitoring : function(){
45961         this.bound = false;
45962     },
45963
45964     // private
45965     bindHandler : function(){
45966         if(!this.bound){
45967             return false; // stops binding
45968         }
45969         var valid = true;
45970         this.items.each(function(f){
45971             if(!f.isValid(true)){
45972                 valid = false;
45973                 return false;
45974             }
45975         });
45976         for(var i = 0, len = this.buttons.length; i < len; i++){
45977             var btn = this.buttons[i];
45978             if(btn.formBind === true && btn.disabled === valid){
45979                 btn.setDisabled(!valid);
45980             }
45981         }
45982         this.fireEvent('clientvalidation', this, valid);
45983     }
45984     
45985     
45986     
45987     
45988     
45989     
45990     
45991     
45992 });
45993
45994
45995 // back compat
45996 Roo.Form = Roo.form.Form;
45997 /*
45998  * Based on:
45999  * Ext JS Library 1.1.1
46000  * Copyright(c) 2006-2007, Ext JS, LLC.
46001  *
46002  * Originally Released Under LGPL - original licence link has changed is not relivant.
46003  *
46004  * Fork - LGPL
46005  * <script type="text/javascript">
46006  */
46007
46008 // as we use this in bootstrap.
46009 Roo.namespace('Roo.form');
46010  /**
46011  * @class Roo.form.Action
46012  * Internal Class used to handle form actions
46013  * @constructor
46014  * @param {Roo.form.BasicForm} el The form element or its id
46015  * @param {Object} config Configuration options
46016  */
46017
46018  
46019  
46020 // define the action interface
46021 Roo.form.Action = function(form, options){
46022     this.form = form;
46023     this.options = options || {};
46024 };
46025 /**
46026  * Client Validation Failed
46027  * @const 
46028  */
46029 Roo.form.Action.CLIENT_INVALID = 'client';
46030 /**
46031  * Server Validation Failed
46032  * @const 
46033  */
46034 Roo.form.Action.SERVER_INVALID = 'server';
46035  /**
46036  * Connect to Server Failed
46037  * @const 
46038  */
46039 Roo.form.Action.CONNECT_FAILURE = 'connect';
46040 /**
46041  * Reading Data from Server Failed
46042  * @const 
46043  */
46044 Roo.form.Action.LOAD_FAILURE = 'load';
46045
46046 Roo.form.Action.prototype = {
46047     type : 'default',
46048     failureType : undefined,
46049     response : undefined,
46050     result : undefined,
46051
46052     // interface method
46053     run : function(options){
46054
46055     },
46056
46057     // interface method
46058     success : function(response){
46059
46060     },
46061
46062     // interface method
46063     handleResponse : function(response){
46064
46065     },
46066
46067     // default connection failure
46068     failure : function(response){
46069         
46070         this.response = response;
46071         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46072         this.form.afterAction(this, false);
46073     },
46074
46075     processResponse : function(response){
46076         this.response = response;
46077         if(!response.responseText){
46078             return true;
46079         }
46080         this.result = this.handleResponse(response);
46081         return this.result;
46082     },
46083
46084     // utility functions used internally
46085     getUrl : function(appendParams){
46086         var url = this.options.url || this.form.url || this.form.el.dom.action;
46087         if(appendParams){
46088             var p = this.getParams();
46089             if(p){
46090                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46091             }
46092         }
46093         return url;
46094     },
46095
46096     getMethod : function(){
46097         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46098     },
46099
46100     getParams : function(){
46101         var bp = this.form.baseParams;
46102         var p = this.options.params;
46103         if(p){
46104             if(typeof p == "object"){
46105                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46106             }else if(typeof p == 'string' && bp){
46107                 p += '&' + Roo.urlEncode(bp);
46108             }
46109         }else if(bp){
46110             p = Roo.urlEncode(bp);
46111         }
46112         return p;
46113     },
46114
46115     createCallback : function(){
46116         return {
46117             success: this.success,
46118             failure: this.failure,
46119             scope: this,
46120             timeout: (this.form.timeout*1000),
46121             upload: this.form.fileUpload ? this.success : undefined
46122         };
46123     }
46124 };
46125
46126 Roo.form.Action.Submit = function(form, options){
46127     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46128 };
46129
46130 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46131     type : 'submit',
46132
46133     haveProgress : false,
46134     uploadComplete : false,
46135     
46136     // uploadProgress indicator.
46137     uploadProgress : function()
46138     {
46139         if (!this.form.progressUrl) {
46140             return;
46141         }
46142         
46143         if (!this.haveProgress) {
46144             Roo.MessageBox.progress("Uploading", "Uploading");
46145         }
46146         if (this.uploadComplete) {
46147            Roo.MessageBox.hide();
46148            return;
46149         }
46150         
46151         this.haveProgress = true;
46152    
46153         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46154         
46155         var c = new Roo.data.Connection();
46156         c.request({
46157             url : this.form.progressUrl,
46158             params: {
46159                 id : uid
46160             },
46161             method: 'GET',
46162             success : function(req){
46163                //console.log(data);
46164                 var rdata = false;
46165                 var edata;
46166                 try  {
46167                    rdata = Roo.decode(req.responseText)
46168                 } catch (e) {
46169                     Roo.log("Invalid data from server..");
46170                     Roo.log(edata);
46171                     return;
46172                 }
46173                 if (!rdata || !rdata.success) {
46174                     Roo.log(rdata);
46175                     Roo.MessageBox.alert(Roo.encode(rdata));
46176                     return;
46177                 }
46178                 var data = rdata.data;
46179                 
46180                 if (this.uploadComplete) {
46181                    Roo.MessageBox.hide();
46182                    return;
46183                 }
46184                    
46185                 if (data){
46186                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46187                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46188                     );
46189                 }
46190                 this.uploadProgress.defer(2000,this);
46191             },
46192        
46193             failure: function(data) {
46194                 Roo.log('progress url failed ');
46195                 Roo.log(data);
46196             },
46197             scope : this
46198         });
46199            
46200     },
46201     
46202     
46203     run : function()
46204     {
46205         // run get Values on the form, so it syncs any secondary forms.
46206         this.form.getValues();
46207         
46208         var o = this.options;
46209         var method = this.getMethod();
46210         var isPost = method == 'POST';
46211         if(o.clientValidation === false || this.form.isValid()){
46212             
46213             if (this.form.progressUrl) {
46214                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46215                     (new Date() * 1) + '' + Math.random());
46216                     
46217             } 
46218             
46219             
46220             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46221                 form:this.form.el.dom,
46222                 url:this.getUrl(!isPost),
46223                 method: method,
46224                 params:isPost ? this.getParams() : null,
46225                 isUpload: this.form.fileUpload
46226             }));
46227             
46228             this.uploadProgress();
46229
46230         }else if (o.clientValidation !== false){ // client validation failed
46231             this.failureType = Roo.form.Action.CLIENT_INVALID;
46232             this.form.afterAction(this, false);
46233         }
46234     },
46235
46236     success : function(response)
46237     {
46238         this.uploadComplete= true;
46239         if (this.haveProgress) {
46240             Roo.MessageBox.hide();
46241         }
46242         
46243         
46244         var result = this.processResponse(response);
46245         if(result === true || result.success){
46246             this.form.afterAction(this, true);
46247             return;
46248         }
46249         if(result.errors){
46250             this.form.markInvalid(result.errors);
46251             this.failureType = Roo.form.Action.SERVER_INVALID;
46252         }
46253         this.form.afterAction(this, false);
46254     },
46255     failure : function(response)
46256     {
46257         this.uploadComplete= true;
46258         if (this.haveProgress) {
46259             Roo.MessageBox.hide();
46260         }
46261         
46262         this.response = response;
46263         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46264         this.form.afterAction(this, false);
46265     },
46266     
46267     handleResponse : function(response){
46268         if(this.form.errorReader){
46269             var rs = this.form.errorReader.read(response);
46270             var errors = [];
46271             if(rs.records){
46272                 for(var i = 0, len = rs.records.length; i < len; i++) {
46273                     var r = rs.records[i];
46274                     errors[i] = r.data;
46275                 }
46276             }
46277             if(errors.length < 1){
46278                 errors = null;
46279             }
46280             return {
46281                 success : rs.success,
46282                 errors : errors
46283             };
46284         }
46285         var ret = false;
46286         try {
46287             ret = Roo.decode(response.responseText);
46288         } catch (e) {
46289             ret = {
46290                 success: false,
46291                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46292                 errors : []
46293             };
46294         }
46295         return ret;
46296         
46297     }
46298 });
46299
46300
46301 Roo.form.Action.Load = function(form, options){
46302     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46303     this.reader = this.form.reader;
46304 };
46305
46306 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46307     type : 'load',
46308
46309     run : function(){
46310         
46311         Roo.Ajax.request(Roo.apply(
46312                 this.createCallback(), {
46313                     method:this.getMethod(),
46314                     url:this.getUrl(false),
46315                     params:this.getParams()
46316         }));
46317     },
46318
46319     success : function(response){
46320         
46321         var result = this.processResponse(response);
46322         if(result === true || !result.success || !result.data){
46323             this.failureType = Roo.form.Action.LOAD_FAILURE;
46324             this.form.afterAction(this, false);
46325             return;
46326         }
46327         this.form.clearInvalid();
46328         this.form.setValues(result.data);
46329         this.form.afterAction(this, true);
46330     },
46331
46332     handleResponse : function(response){
46333         if(this.form.reader){
46334             var rs = this.form.reader.read(response);
46335             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46336             return {
46337                 success : rs.success,
46338                 data : data
46339             };
46340         }
46341         return Roo.decode(response.responseText);
46342     }
46343 });
46344
46345 Roo.form.Action.ACTION_TYPES = {
46346     'load' : Roo.form.Action.Load,
46347     'submit' : Roo.form.Action.Submit
46348 };/*
46349  * Based on:
46350  * Ext JS Library 1.1.1
46351  * Copyright(c) 2006-2007, Ext JS, LLC.
46352  *
46353  * Originally Released Under LGPL - original licence link has changed is not relivant.
46354  *
46355  * Fork - LGPL
46356  * <script type="text/javascript">
46357  */
46358  
46359 /**
46360  * @class Roo.form.Layout
46361  * @extends Roo.Component
46362  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46363  * @constructor
46364  * @param {Object} config Configuration options
46365  */
46366 Roo.form.Layout = function(config){
46367     var xitems = [];
46368     if (config.items) {
46369         xitems = config.items;
46370         delete config.items;
46371     }
46372     Roo.form.Layout.superclass.constructor.call(this, config);
46373     this.stack = [];
46374     Roo.each(xitems, this.addxtype, this);
46375      
46376 };
46377
46378 Roo.extend(Roo.form.Layout, Roo.Component, {
46379     /**
46380      * @cfg {String/Object} autoCreate
46381      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46382      */
46383     /**
46384      * @cfg {String/Object/Function} style
46385      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46386      * a function which returns such a specification.
46387      */
46388     /**
46389      * @cfg {String} labelAlign
46390      * Valid values are "left," "top" and "right" (defaults to "left")
46391      */
46392     /**
46393      * @cfg {Number} labelWidth
46394      * Fixed width in pixels of all field labels (defaults to undefined)
46395      */
46396     /**
46397      * @cfg {Boolean} clear
46398      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46399      */
46400     clear : true,
46401     /**
46402      * @cfg {String} labelSeparator
46403      * The separator to use after field labels (defaults to ':')
46404      */
46405     labelSeparator : ':',
46406     /**
46407      * @cfg {Boolean} hideLabels
46408      * True to suppress the display of field labels in this layout (defaults to false)
46409      */
46410     hideLabels : false,
46411
46412     // private
46413     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46414     
46415     isLayout : true,
46416     
46417     // private
46418     onRender : function(ct, position){
46419         if(this.el){ // from markup
46420             this.el = Roo.get(this.el);
46421         }else {  // generate
46422             var cfg = this.getAutoCreate();
46423             this.el = ct.createChild(cfg, position);
46424         }
46425         if(this.style){
46426             this.el.applyStyles(this.style);
46427         }
46428         if(this.labelAlign){
46429             this.el.addClass('x-form-label-'+this.labelAlign);
46430         }
46431         if(this.hideLabels){
46432             this.labelStyle = "display:none";
46433             this.elementStyle = "padding-left:0;";
46434         }else{
46435             if(typeof this.labelWidth == 'number'){
46436                 this.labelStyle = "width:"+this.labelWidth+"px;";
46437                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46438             }
46439             if(this.labelAlign == 'top'){
46440                 this.labelStyle = "width:auto;";
46441                 this.elementStyle = "padding-left:0;";
46442             }
46443         }
46444         var stack = this.stack;
46445         var slen = stack.length;
46446         if(slen > 0){
46447             if(!this.fieldTpl){
46448                 var t = new Roo.Template(
46449                     '<div class="x-form-item {5}">',
46450                         '<label for="{0}" style="{2}">{1}{4}</label>',
46451                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46452                         '</div>',
46453                     '</div><div class="x-form-clear-left"></div>'
46454                 );
46455                 t.disableFormats = true;
46456                 t.compile();
46457                 Roo.form.Layout.prototype.fieldTpl = t;
46458             }
46459             for(var i = 0; i < slen; i++) {
46460                 if(stack[i].isFormField){
46461                     this.renderField(stack[i]);
46462                 }else{
46463                     this.renderComponent(stack[i]);
46464                 }
46465             }
46466         }
46467         if(this.clear){
46468             this.el.createChild({cls:'x-form-clear'});
46469         }
46470     },
46471
46472     // private
46473     renderField : function(f){
46474         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46475                f.id, //0
46476                f.fieldLabel, //1
46477                f.labelStyle||this.labelStyle||'', //2
46478                this.elementStyle||'', //3
46479                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46480                f.itemCls||this.itemCls||''  //5
46481        ], true).getPrevSibling());
46482     },
46483
46484     // private
46485     renderComponent : function(c){
46486         c.render(c.isLayout ? this.el : this.el.createChild());    
46487     },
46488     /**
46489      * Adds a object form elements (using the xtype property as the factory method.)
46490      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46491      * @param {Object} config 
46492      */
46493     addxtype : function(o)
46494     {
46495         // create the lement.
46496         o.form = this.form;
46497         var fe = Roo.factory(o, Roo.form);
46498         this.form.allItems.push(fe);
46499         this.stack.push(fe);
46500         
46501         if (fe.isFormField) {
46502             this.form.items.add(fe);
46503         }
46504          
46505         return fe;
46506     }
46507 });
46508
46509 /**
46510  * @class Roo.form.Column
46511  * @extends Roo.form.Layout
46512  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46513  * @constructor
46514  * @param {Object} config Configuration options
46515  */
46516 Roo.form.Column = function(config){
46517     Roo.form.Column.superclass.constructor.call(this, config);
46518 };
46519
46520 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46521     /**
46522      * @cfg {Number/String} width
46523      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46524      */
46525     /**
46526      * @cfg {String/Object} autoCreate
46527      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46528      */
46529
46530     // private
46531     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46532
46533     // private
46534     onRender : function(ct, position){
46535         Roo.form.Column.superclass.onRender.call(this, ct, position);
46536         if(this.width){
46537             this.el.setWidth(this.width);
46538         }
46539     }
46540 });
46541
46542
46543 /**
46544  * @class Roo.form.Row
46545  * @extends Roo.form.Layout
46546  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46547  * @constructor
46548  * @param {Object} config Configuration options
46549  */
46550
46551  
46552 Roo.form.Row = function(config){
46553     Roo.form.Row.superclass.constructor.call(this, config);
46554 };
46555  
46556 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46557       /**
46558      * @cfg {Number/String} width
46559      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46560      */
46561     /**
46562      * @cfg {Number/String} height
46563      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46564      */
46565     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46566     
46567     padWidth : 20,
46568     // private
46569     onRender : function(ct, position){
46570         //console.log('row render');
46571         if(!this.rowTpl){
46572             var t = new Roo.Template(
46573                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46574                     '<label for="{0}" style="{2}">{1}{4}</label>',
46575                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46576                     '</div>',
46577                 '</div>'
46578             );
46579             t.disableFormats = true;
46580             t.compile();
46581             Roo.form.Layout.prototype.rowTpl = t;
46582         }
46583         this.fieldTpl = this.rowTpl;
46584         
46585         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46586         var labelWidth = 100;
46587         
46588         if ((this.labelAlign != 'top')) {
46589             if (typeof this.labelWidth == 'number') {
46590                 labelWidth = this.labelWidth
46591             }
46592             this.padWidth =  20 + labelWidth;
46593             
46594         }
46595         
46596         Roo.form.Column.superclass.onRender.call(this, ct, position);
46597         if(this.width){
46598             this.el.setWidth(this.width);
46599         }
46600         if(this.height){
46601             this.el.setHeight(this.height);
46602         }
46603     },
46604     
46605     // private
46606     renderField : function(f){
46607         f.fieldEl = this.fieldTpl.append(this.el, [
46608                f.id, f.fieldLabel,
46609                f.labelStyle||this.labelStyle||'',
46610                this.elementStyle||'',
46611                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46612                f.itemCls||this.itemCls||'',
46613                f.width ? f.width + this.padWidth : 160 + this.padWidth
46614        ],true);
46615     }
46616 });
46617  
46618
46619 /**
46620  * @class Roo.form.FieldSet
46621  * @extends Roo.form.Layout
46622  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46623  * @constructor
46624  * @param {Object} config Configuration options
46625  */
46626 Roo.form.FieldSet = function(config){
46627     Roo.form.FieldSet.superclass.constructor.call(this, config);
46628 };
46629
46630 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46631     /**
46632      * @cfg {String} legend
46633      * The text to display as the legend for the FieldSet (defaults to '')
46634      */
46635     /**
46636      * @cfg {String/Object} autoCreate
46637      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46638      */
46639
46640     // private
46641     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46642
46643     // private
46644     onRender : function(ct, position){
46645         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46646         if(this.legend){
46647             this.setLegend(this.legend);
46648         }
46649     },
46650
46651     // private
46652     setLegend : function(text){
46653         if(this.rendered){
46654             this.el.child('legend').update(text);
46655         }
46656     }
46657 });/*
46658  * Based on:
46659  * Ext JS Library 1.1.1
46660  * Copyright(c) 2006-2007, Ext JS, LLC.
46661  *
46662  * Originally Released Under LGPL - original licence link has changed is not relivant.
46663  *
46664  * Fork - LGPL
46665  * <script type="text/javascript">
46666  */
46667 /**
46668  * @class Roo.form.VTypes
46669  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46670  * @singleton
46671  */
46672 Roo.form.VTypes = function(){
46673     // closure these in so they are only created once.
46674     var alpha = /^[a-zA-Z_]+$/;
46675     var alphanum = /^[a-zA-Z0-9_]+$/;
46676     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46677     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46678
46679     // All these messages and functions are configurable
46680     return {
46681         /**
46682          * The function used to validate email addresses
46683          * @param {String} value The email address
46684          */
46685         'email' : function(v){
46686             return email.test(v);
46687         },
46688         /**
46689          * The error text to display when the email validation function returns false
46690          * @type String
46691          */
46692         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46693         /**
46694          * The keystroke filter mask to be applied on email input
46695          * @type RegExp
46696          */
46697         'emailMask' : /[a-z0-9_\.\-@]/i,
46698
46699         /**
46700          * The function used to validate URLs
46701          * @param {String} value The URL
46702          */
46703         'url' : function(v){
46704             return url.test(v);
46705         },
46706         /**
46707          * The error text to display when the url validation function returns false
46708          * @type String
46709          */
46710         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46711         
46712         /**
46713          * The function used to validate alpha values
46714          * @param {String} value The value
46715          */
46716         'alpha' : function(v){
46717             return alpha.test(v);
46718         },
46719         /**
46720          * The error text to display when the alpha validation function returns false
46721          * @type String
46722          */
46723         'alphaText' : 'This field should only contain letters and _',
46724         /**
46725          * The keystroke filter mask to be applied on alpha input
46726          * @type RegExp
46727          */
46728         'alphaMask' : /[a-z_]/i,
46729
46730         /**
46731          * The function used to validate alphanumeric values
46732          * @param {String} value The value
46733          */
46734         'alphanum' : function(v){
46735             return alphanum.test(v);
46736         },
46737         /**
46738          * The error text to display when the alphanumeric validation function returns false
46739          * @type String
46740          */
46741         'alphanumText' : 'This field should only contain letters, numbers and _',
46742         /**
46743          * The keystroke filter mask to be applied on alphanumeric input
46744          * @type RegExp
46745          */
46746         'alphanumMask' : /[a-z0-9_]/i
46747     };
46748 }();//<script type="text/javascript">
46749
46750 /**
46751  * @class Roo.form.FCKeditor
46752  * @extends Roo.form.TextArea
46753  * Wrapper around the FCKEditor http://www.fckeditor.net
46754  * @constructor
46755  * Creates a new FCKeditor
46756  * @param {Object} config Configuration options
46757  */
46758 Roo.form.FCKeditor = function(config){
46759     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46760     this.addEvents({
46761          /**
46762          * @event editorinit
46763          * Fired when the editor is initialized - you can add extra handlers here..
46764          * @param {FCKeditor} this
46765          * @param {Object} the FCK object.
46766          */
46767         editorinit : true
46768     });
46769     
46770     
46771 };
46772 Roo.form.FCKeditor.editors = { };
46773 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46774 {
46775     //defaultAutoCreate : {
46776     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46777     //},
46778     // private
46779     /**
46780      * @cfg {Object} fck options - see fck manual for details.
46781      */
46782     fckconfig : false,
46783     
46784     /**
46785      * @cfg {Object} fck toolbar set (Basic or Default)
46786      */
46787     toolbarSet : 'Basic',
46788     /**
46789      * @cfg {Object} fck BasePath
46790      */ 
46791     basePath : '/fckeditor/',
46792     
46793     
46794     frame : false,
46795     
46796     value : '',
46797     
46798    
46799     onRender : function(ct, position)
46800     {
46801         if(!this.el){
46802             this.defaultAutoCreate = {
46803                 tag: "textarea",
46804                 style:"width:300px;height:60px;",
46805                 autocomplete: "new-password"
46806             };
46807         }
46808         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46809         /*
46810         if(this.grow){
46811             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46812             if(this.preventScrollbars){
46813                 this.el.setStyle("overflow", "hidden");
46814             }
46815             this.el.setHeight(this.growMin);
46816         }
46817         */
46818         //console.log('onrender' + this.getId() );
46819         Roo.form.FCKeditor.editors[this.getId()] = this;
46820          
46821
46822         this.replaceTextarea() ;
46823         
46824     },
46825     
46826     getEditor : function() {
46827         return this.fckEditor;
46828     },
46829     /**
46830      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46831      * @param {Mixed} value The value to set
46832      */
46833     
46834     
46835     setValue : function(value)
46836     {
46837         //console.log('setValue: ' + value);
46838         
46839         if(typeof(value) == 'undefined') { // not sure why this is happending...
46840             return;
46841         }
46842         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46843         
46844         //if(!this.el || !this.getEditor()) {
46845         //    this.value = value;
46846             //this.setValue.defer(100,this,[value]);    
46847         //    return;
46848         //} 
46849         
46850         if(!this.getEditor()) {
46851             return;
46852         }
46853         
46854         this.getEditor().SetData(value);
46855         
46856         //
46857
46858     },
46859
46860     /**
46861      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46862      * @return {Mixed} value The field value
46863      */
46864     getValue : function()
46865     {
46866         
46867         if (this.frame && this.frame.dom.style.display == 'none') {
46868             return Roo.form.FCKeditor.superclass.getValue.call(this);
46869         }
46870         
46871         if(!this.el || !this.getEditor()) {
46872            
46873            // this.getValue.defer(100,this); 
46874             return this.value;
46875         }
46876        
46877         
46878         var value=this.getEditor().GetData();
46879         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46880         return Roo.form.FCKeditor.superclass.getValue.call(this);
46881         
46882
46883     },
46884
46885     /**
46886      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46887      * @return {Mixed} value The field value
46888      */
46889     getRawValue : function()
46890     {
46891         if (this.frame && this.frame.dom.style.display == 'none') {
46892             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46893         }
46894         
46895         if(!this.el || !this.getEditor()) {
46896             //this.getRawValue.defer(100,this); 
46897             return this.value;
46898             return;
46899         }
46900         
46901         
46902         
46903         var value=this.getEditor().GetData();
46904         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46905         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46906          
46907     },
46908     
46909     setSize : function(w,h) {
46910         
46911         
46912         
46913         //if (this.frame && this.frame.dom.style.display == 'none') {
46914         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46915         //    return;
46916         //}
46917         //if(!this.el || !this.getEditor()) {
46918         //    this.setSize.defer(100,this, [w,h]); 
46919         //    return;
46920         //}
46921         
46922         
46923         
46924         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46925         
46926         this.frame.dom.setAttribute('width', w);
46927         this.frame.dom.setAttribute('height', h);
46928         this.frame.setSize(w,h);
46929         
46930     },
46931     
46932     toggleSourceEdit : function(value) {
46933         
46934       
46935          
46936         this.el.dom.style.display = value ? '' : 'none';
46937         this.frame.dom.style.display = value ?  'none' : '';
46938         
46939     },
46940     
46941     
46942     focus: function(tag)
46943     {
46944         if (this.frame.dom.style.display == 'none') {
46945             return Roo.form.FCKeditor.superclass.focus.call(this);
46946         }
46947         if(!this.el || !this.getEditor()) {
46948             this.focus.defer(100,this, [tag]); 
46949             return;
46950         }
46951         
46952         
46953         
46954         
46955         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46956         this.getEditor().Focus();
46957         if (tgs.length) {
46958             if (!this.getEditor().Selection.GetSelection()) {
46959                 this.focus.defer(100,this, [tag]); 
46960                 return;
46961             }
46962             
46963             
46964             var r = this.getEditor().EditorDocument.createRange();
46965             r.setStart(tgs[0],0);
46966             r.setEnd(tgs[0],0);
46967             this.getEditor().Selection.GetSelection().removeAllRanges();
46968             this.getEditor().Selection.GetSelection().addRange(r);
46969             this.getEditor().Focus();
46970         }
46971         
46972     },
46973     
46974     
46975     
46976     replaceTextarea : function()
46977     {
46978         if ( document.getElementById( this.getId() + '___Frame' ) )
46979             return ;
46980         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46981         //{
46982             // We must check the elements firstly using the Id and then the name.
46983         var oTextarea = document.getElementById( this.getId() );
46984         
46985         var colElementsByName = document.getElementsByName( this.getId() ) ;
46986          
46987         oTextarea.style.display = 'none' ;
46988
46989         if ( oTextarea.tabIndex ) {            
46990             this.TabIndex = oTextarea.tabIndex ;
46991         }
46992         
46993         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46994         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46995         this.frame = Roo.get(this.getId() + '___Frame')
46996     },
46997     
46998     _getConfigHtml : function()
46999     {
47000         var sConfig = '' ;
47001
47002         for ( var o in this.fckconfig ) {
47003             sConfig += sConfig.length > 0  ? '&amp;' : '';
47004             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47005         }
47006
47007         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47008     },
47009     
47010     
47011     _getIFrameHtml : function()
47012     {
47013         var sFile = 'fckeditor.html' ;
47014         /* no idea what this is about..
47015         try
47016         {
47017             if ( (/fcksource=true/i).test( window.top.location.search ) )
47018                 sFile = 'fckeditor.original.html' ;
47019         }
47020         catch (e) { 
47021         */
47022
47023         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47024         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47025         
47026         
47027         var html = '<iframe id="' + this.getId() +
47028             '___Frame" src="' + sLink +
47029             '" width="' + this.width +
47030             '" height="' + this.height + '"' +
47031             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47032             ' frameborder="0" scrolling="no"></iframe>' ;
47033
47034         return html ;
47035     },
47036     
47037     _insertHtmlBefore : function( html, element )
47038     {
47039         if ( element.insertAdjacentHTML )       {
47040             // IE
47041             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47042         } else { // Gecko
47043             var oRange = document.createRange() ;
47044             oRange.setStartBefore( element ) ;
47045             var oFragment = oRange.createContextualFragment( html );
47046             element.parentNode.insertBefore( oFragment, element ) ;
47047         }
47048     }
47049     
47050     
47051   
47052     
47053     
47054     
47055     
47056
47057 });
47058
47059 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47060
47061 function FCKeditor_OnComplete(editorInstance){
47062     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47063     f.fckEditor = editorInstance;
47064     //console.log("loaded");
47065     f.fireEvent('editorinit', f, editorInstance);
47066
47067   
47068
47069  
47070
47071
47072
47073
47074
47075
47076
47077
47078
47079
47080
47081
47082
47083
47084
47085 //<script type="text/javascript">
47086 /**
47087  * @class Roo.form.GridField
47088  * @extends Roo.form.Field
47089  * Embed a grid (or editable grid into a form)
47090  * STATUS ALPHA
47091  * 
47092  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47093  * it needs 
47094  * xgrid.store = Roo.data.Store
47095  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47096  * xgrid.store.reader = Roo.data.JsonReader 
47097  * 
47098  * 
47099  * @constructor
47100  * Creates a new GridField
47101  * @param {Object} config Configuration options
47102  */
47103 Roo.form.GridField = function(config){
47104     Roo.form.GridField.superclass.constructor.call(this, config);
47105      
47106 };
47107
47108 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47109     /**
47110      * @cfg {Number} width  - used to restrict width of grid..
47111      */
47112     width : 100,
47113     /**
47114      * @cfg {Number} height - used to restrict height of grid..
47115      */
47116     height : 50,
47117      /**
47118      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47119          * 
47120          *}
47121      */
47122     xgrid : false, 
47123     /**
47124      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47125      * {tag: "input", type: "checkbox", autocomplete: "off"})
47126      */
47127    // defaultAutoCreate : { tag: 'div' },
47128     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47129     /**
47130      * @cfg {String} addTitle Text to include for adding a title.
47131      */
47132     addTitle : false,
47133     //
47134     onResize : function(){
47135         Roo.form.Field.superclass.onResize.apply(this, arguments);
47136     },
47137
47138     initEvents : function(){
47139         // Roo.form.Checkbox.superclass.initEvents.call(this);
47140         // has no events...
47141        
47142     },
47143
47144
47145     getResizeEl : function(){
47146         return this.wrap;
47147     },
47148
47149     getPositionEl : function(){
47150         return this.wrap;
47151     },
47152
47153     // private
47154     onRender : function(ct, position){
47155         
47156         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47157         var style = this.style;
47158         delete this.style;
47159         
47160         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47161         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47162         this.viewEl = this.wrap.createChild({ tag: 'div' });
47163         if (style) {
47164             this.viewEl.applyStyles(style);
47165         }
47166         if (this.width) {
47167             this.viewEl.setWidth(this.width);
47168         }
47169         if (this.height) {
47170             this.viewEl.setHeight(this.height);
47171         }
47172         //if(this.inputValue !== undefined){
47173         //this.setValue(this.value);
47174         
47175         
47176         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47177         
47178         
47179         this.grid.render();
47180         this.grid.getDataSource().on('remove', this.refreshValue, this);
47181         this.grid.getDataSource().on('update', this.refreshValue, this);
47182         this.grid.on('afteredit', this.refreshValue, this);
47183  
47184     },
47185      
47186     
47187     /**
47188      * Sets the value of the item. 
47189      * @param {String} either an object  or a string..
47190      */
47191     setValue : function(v){
47192         //this.value = v;
47193         v = v || []; // empty set..
47194         // this does not seem smart - it really only affects memoryproxy grids..
47195         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47196             var ds = this.grid.getDataSource();
47197             // assumes a json reader..
47198             var data = {}
47199             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47200             ds.loadData( data);
47201         }
47202         // clear selection so it does not get stale.
47203         if (this.grid.sm) { 
47204             this.grid.sm.clearSelections();
47205         }
47206         
47207         Roo.form.GridField.superclass.setValue.call(this, v);
47208         this.refreshValue();
47209         // should load data in the grid really....
47210     },
47211     
47212     // private
47213     refreshValue: function() {
47214          var val = [];
47215         this.grid.getDataSource().each(function(r) {
47216             val.push(r.data);
47217         });
47218         this.el.dom.value = Roo.encode(val);
47219     }
47220     
47221      
47222     
47223     
47224 });/*
47225  * Based on:
47226  * Ext JS Library 1.1.1
47227  * Copyright(c) 2006-2007, Ext JS, LLC.
47228  *
47229  * Originally Released Under LGPL - original licence link has changed is not relivant.
47230  *
47231  * Fork - LGPL
47232  * <script type="text/javascript">
47233  */
47234 /**
47235  * @class Roo.form.DisplayField
47236  * @extends Roo.form.Field
47237  * A generic Field to display non-editable data.
47238  * @constructor
47239  * Creates a new Display Field item.
47240  * @param {Object} config Configuration options
47241  */
47242 Roo.form.DisplayField = function(config){
47243     Roo.form.DisplayField.superclass.constructor.call(this, config);
47244     
47245 };
47246
47247 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47248     inputType:      'hidden',
47249     allowBlank:     true,
47250     readOnly:         true,
47251     
47252  
47253     /**
47254      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47255      */
47256     focusClass : undefined,
47257     /**
47258      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47259      */
47260     fieldClass: 'x-form-field',
47261     
47262      /**
47263      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47264      */
47265     valueRenderer: undefined,
47266     
47267     width: 100,
47268     /**
47269      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47270      * {tag: "input", type: "checkbox", autocomplete: "off"})
47271      */
47272      
47273  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47274
47275     onResize : function(){
47276         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47277         
47278     },
47279
47280     initEvents : function(){
47281         // Roo.form.Checkbox.superclass.initEvents.call(this);
47282         // has no events...
47283        
47284     },
47285
47286
47287     getResizeEl : function(){
47288         return this.wrap;
47289     },
47290
47291     getPositionEl : function(){
47292         return this.wrap;
47293     },
47294
47295     // private
47296     onRender : function(ct, position){
47297         
47298         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47299         //if(this.inputValue !== undefined){
47300         this.wrap = this.el.wrap();
47301         
47302         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47303         
47304         if (this.bodyStyle) {
47305             this.viewEl.applyStyles(this.bodyStyle);
47306         }
47307         //this.viewEl.setStyle('padding', '2px');
47308         
47309         this.setValue(this.value);
47310         
47311     },
47312 /*
47313     // private
47314     initValue : Roo.emptyFn,
47315
47316   */
47317
47318         // private
47319     onClick : function(){
47320         
47321     },
47322
47323     /**
47324      * Sets the checked state of the checkbox.
47325      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47326      */
47327     setValue : function(v){
47328         this.value = v;
47329         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47330         // this might be called before we have a dom element..
47331         if (!this.viewEl) {
47332             return;
47333         }
47334         this.viewEl.dom.innerHTML = html;
47335         Roo.form.DisplayField.superclass.setValue.call(this, v);
47336
47337     }
47338 });/*
47339  * 
47340  * Licence- LGPL
47341  * 
47342  */
47343
47344 /**
47345  * @class Roo.form.DayPicker
47346  * @extends Roo.form.Field
47347  * A Day picker show [M] [T] [W] ....
47348  * @constructor
47349  * Creates a new Day Picker
47350  * @param {Object} config Configuration options
47351  */
47352 Roo.form.DayPicker= function(config){
47353     Roo.form.DayPicker.superclass.constructor.call(this, config);
47354      
47355 };
47356
47357 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47358     /**
47359      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47360      */
47361     focusClass : undefined,
47362     /**
47363      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47364      */
47365     fieldClass: "x-form-field",
47366    
47367     /**
47368      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47369      * {tag: "input", type: "checkbox", autocomplete: "off"})
47370      */
47371     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47372     
47373    
47374     actionMode : 'viewEl', 
47375     //
47376     // private
47377  
47378     inputType : 'hidden',
47379     
47380      
47381     inputElement: false, // real input element?
47382     basedOn: false, // ????
47383     
47384     isFormField: true, // not sure where this is needed!!!!
47385
47386     onResize : function(){
47387         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47388         if(!this.boxLabel){
47389             this.el.alignTo(this.wrap, 'c-c');
47390         }
47391     },
47392
47393     initEvents : function(){
47394         Roo.form.Checkbox.superclass.initEvents.call(this);
47395         this.el.on("click", this.onClick,  this);
47396         this.el.on("change", this.onClick,  this);
47397     },
47398
47399
47400     getResizeEl : function(){
47401         return this.wrap;
47402     },
47403
47404     getPositionEl : function(){
47405         return this.wrap;
47406     },
47407
47408     
47409     // private
47410     onRender : function(ct, position){
47411         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47412        
47413         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47414         
47415         var r1 = '<table><tr>';
47416         var r2 = '<tr class="x-form-daypick-icons">';
47417         for (var i=0; i < 7; i++) {
47418             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47419             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47420         }
47421         
47422         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47423         viewEl.select('img').on('click', this.onClick, this);
47424         this.viewEl = viewEl;   
47425         
47426         
47427         // this will not work on Chrome!!!
47428         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47429         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47430         
47431         
47432           
47433
47434     },
47435
47436     // private
47437     initValue : Roo.emptyFn,
47438
47439     /**
47440      * Returns the checked state of the checkbox.
47441      * @return {Boolean} True if checked, else false
47442      */
47443     getValue : function(){
47444         return this.el.dom.value;
47445         
47446     },
47447
47448         // private
47449     onClick : function(e){ 
47450         //this.setChecked(!this.checked);
47451         Roo.get(e.target).toggleClass('x-menu-item-checked');
47452         this.refreshValue();
47453         //if(this.el.dom.checked != this.checked){
47454         //    this.setValue(this.el.dom.checked);
47455        // }
47456     },
47457     
47458     // private
47459     refreshValue : function()
47460     {
47461         var val = '';
47462         this.viewEl.select('img',true).each(function(e,i,n)  {
47463             val += e.is(".x-menu-item-checked") ? String(n) : '';
47464         });
47465         this.setValue(val, true);
47466     },
47467
47468     /**
47469      * Sets the checked state of the checkbox.
47470      * On is always based on a string comparison between inputValue and the param.
47471      * @param {Boolean/String} value - the value to set 
47472      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47473      */
47474     setValue : function(v,suppressEvent){
47475         if (!this.el.dom) {
47476             return;
47477         }
47478         var old = this.el.dom.value ;
47479         this.el.dom.value = v;
47480         if (suppressEvent) {
47481             return ;
47482         }
47483          
47484         // update display..
47485         this.viewEl.select('img',true).each(function(e,i,n)  {
47486             
47487             var on = e.is(".x-menu-item-checked");
47488             var newv = v.indexOf(String(n)) > -1;
47489             if (on != newv) {
47490                 e.toggleClass('x-menu-item-checked');
47491             }
47492             
47493         });
47494         
47495         
47496         this.fireEvent('change', this, v, old);
47497         
47498         
47499     },
47500    
47501     // handle setting of hidden value by some other method!!?!?
47502     setFromHidden: function()
47503     {
47504         if(!this.el){
47505             return;
47506         }
47507         //console.log("SET FROM HIDDEN");
47508         //alert('setFrom hidden');
47509         this.setValue(this.el.dom.value);
47510     },
47511     
47512     onDestroy : function()
47513     {
47514         if(this.viewEl){
47515             Roo.get(this.viewEl).remove();
47516         }
47517          
47518         Roo.form.DayPicker.superclass.onDestroy.call(this);
47519     }
47520
47521 });/*
47522  * RooJS Library 1.1.1
47523  * Copyright(c) 2008-2011  Alan Knowles
47524  *
47525  * License - LGPL
47526  */
47527  
47528
47529 /**
47530  * @class Roo.form.ComboCheck
47531  * @extends Roo.form.ComboBox
47532  * A combobox for multiple select items.
47533  *
47534  * FIXME - could do with a reset button..
47535  * 
47536  * @constructor
47537  * Create a new ComboCheck
47538  * @param {Object} config Configuration options
47539  */
47540 Roo.form.ComboCheck = function(config){
47541     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47542     // should verify some data...
47543     // like
47544     // hiddenName = required..
47545     // displayField = required
47546     // valudField == required
47547     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47548     var _t = this;
47549     Roo.each(req, function(e) {
47550         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47551             throw "Roo.form.ComboCheck : missing value for: " + e;
47552         }
47553     });
47554     
47555     
47556 };
47557
47558 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47559      
47560      
47561     editable : false,
47562      
47563     selectedClass: 'x-menu-item-checked', 
47564     
47565     // private
47566     onRender : function(ct, position){
47567         var _t = this;
47568         
47569         
47570         
47571         if(!this.tpl){
47572             var cls = 'x-combo-list';
47573
47574             
47575             this.tpl =  new Roo.Template({
47576                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47577                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47578                    '<span>{' + this.displayField + '}</span>' +
47579                     '</div>' 
47580                 
47581             });
47582         }
47583  
47584         
47585         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47586         this.view.singleSelect = false;
47587         this.view.multiSelect = true;
47588         this.view.toggleSelect = true;
47589         this.pageTb.add(new Roo.Toolbar.Fill(), {
47590             
47591             text: 'Done',
47592             handler: function()
47593             {
47594                 _t.collapse();
47595             }
47596         });
47597     },
47598     
47599     onViewOver : function(e, t){
47600         // do nothing...
47601         return;
47602         
47603     },
47604     
47605     onViewClick : function(doFocus,index){
47606         return;
47607         
47608     },
47609     select: function () {
47610         //Roo.log("SELECT CALLED");
47611     },
47612      
47613     selectByValue : function(xv, scrollIntoView){
47614         var ar = this.getValueArray();
47615         var sels = [];
47616         
47617         Roo.each(ar, function(v) {
47618             if(v === undefined || v === null){
47619                 return;
47620             }
47621             var r = this.findRecord(this.valueField, v);
47622             if(r){
47623                 sels.push(this.store.indexOf(r))
47624                 
47625             }
47626         },this);
47627         this.view.select(sels);
47628         return false;
47629     },
47630     
47631     
47632     
47633     onSelect : function(record, index){
47634        // Roo.log("onselect Called");
47635        // this is only called by the clear button now..
47636         this.view.clearSelections();
47637         this.setValue('[]');
47638         if (this.value != this.valueBefore) {
47639             this.fireEvent('change', this, this.value, this.valueBefore);
47640             this.valueBefore = this.value;
47641         }
47642     },
47643     getValueArray : function()
47644     {
47645         var ar = [] ;
47646         
47647         try {
47648             //Roo.log(this.value);
47649             if (typeof(this.value) == 'undefined') {
47650                 return [];
47651             }
47652             var ar = Roo.decode(this.value);
47653             return  ar instanceof Array ? ar : []; //?? valid?
47654             
47655         } catch(e) {
47656             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47657             return [];
47658         }
47659          
47660     },
47661     expand : function ()
47662     {
47663         
47664         Roo.form.ComboCheck.superclass.expand.call(this);
47665         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47666         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47667         
47668
47669     },
47670     
47671     collapse : function(){
47672         Roo.form.ComboCheck.superclass.collapse.call(this);
47673         var sl = this.view.getSelectedIndexes();
47674         var st = this.store;
47675         var nv = [];
47676         var tv = [];
47677         var r;
47678         Roo.each(sl, function(i) {
47679             r = st.getAt(i);
47680             nv.push(r.get(this.valueField));
47681         },this);
47682         this.setValue(Roo.encode(nv));
47683         if (this.value != this.valueBefore) {
47684
47685             this.fireEvent('change', this, this.value, this.valueBefore);
47686             this.valueBefore = this.value;
47687         }
47688         
47689     },
47690     
47691     setValue : function(v){
47692         // Roo.log(v);
47693         this.value = v;
47694         
47695         var vals = this.getValueArray();
47696         var tv = [];
47697         Roo.each(vals, function(k) {
47698             var r = this.findRecord(this.valueField, k);
47699             if(r){
47700                 tv.push(r.data[this.displayField]);
47701             }else if(this.valueNotFoundText !== undefined){
47702                 tv.push( this.valueNotFoundText );
47703             }
47704         },this);
47705        // Roo.log(tv);
47706         
47707         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47708         this.hiddenField.value = v;
47709         this.value = v;
47710     }
47711     
47712 });/*
47713  * Based on:
47714  * Ext JS Library 1.1.1
47715  * Copyright(c) 2006-2007, Ext JS, LLC.
47716  *
47717  * Originally Released Under LGPL - original licence link has changed is not relivant.
47718  *
47719  * Fork - LGPL
47720  * <script type="text/javascript">
47721  */
47722  
47723 /**
47724  * @class Roo.form.Signature
47725  * @extends Roo.form.Field
47726  * Signature field.  
47727  * @constructor
47728  * 
47729  * @param {Object} config Configuration options
47730  */
47731
47732 Roo.form.Signature = function(config){
47733     Roo.form.Signature.superclass.constructor.call(this, config);
47734     
47735     this.addEvents({// not in used??
47736          /**
47737          * @event confirm
47738          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47739              * @param {Roo.form.Signature} combo This combo box
47740              */
47741         'confirm' : true,
47742         /**
47743          * @event reset
47744          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47745              * @param {Roo.form.ComboBox} combo This combo box
47746              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47747              */
47748         'reset' : true
47749     });
47750 };
47751
47752 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47753     /**
47754      * @cfg {Object} labels Label to use when rendering a form.
47755      * defaults to 
47756      * labels : { 
47757      *      clear : "Clear",
47758      *      confirm : "Confirm"
47759      *  }
47760      */
47761     labels : { 
47762         clear : "Clear",
47763         confirm : "Confirm"
47764     },
47765     /**
47766      * @cfg {Number} width The signature panel width (defaults to 300)
47767      */
47768     width: 300,
47769     /**
47770      * @cfg {Number} height The signature panel height (defaults to 100)
47771      */
47772     height : 100,
47773     /**
47774      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47775      */
47776     allowBlank : false,
47777     
47778     //private
47779     // {Object} signPanel The signature SVG panel element (defaults to {})
47780     signPanel : {},
47781     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47782     isMouseDown : false,
47783     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47784     isConfirmed : false,
47785     // {String} signatureTmp SVG mapping string (defaults to empty string)
47786     signatureTmp : '',
47787     
47788     
47789     defaultAutoCreate : { // modified by initCompnoent..
47790         tag: "input",
47791         type:"hidden"
47792     },
47793
47794     // private
47795     onRender : function(ct, position){
47796         
47797         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47798         
47799         this.wrap = this.el.wrap({
47800             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47801         });
47802         
47803         this.createToolbar(this);
47804         this.signPanel = this.wrap.createChild({
47805                 tag: 'div',
47806                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47807             }, this.el
47808         );
47809             
47810         this.svgID = Roo.id();
47811         this.svgEl = this.signPanel.createChild({
47812               xmlns : 'http://www.w3.org/2000/svg',
47813               tag : 'svg',
47814               id : this.svgID + "-svg",
47815               width: this.width,
47816               height: this.height,
47817               viewBox: '0 0 '+this.width+' '+this.height,
47818               cn : [
47819                 {
47820                     tag: "rect",
47821                     id: this.svgID + "-svg-r",
47822                     width: this.width,
47823                     height: this.height,
47824                     fill: "#ffa"
47825                 },
47826                 {
47827                     tag: "line",
47828                     id: this.svgID + "-svg-l",
47829                     x1: "0", // start
47830                     y1: (this.height*0.8), // start set the line in 80% of height
47831                     x2: this.width, // end
47832                     y2: (this.height*0.8), // end set the line in 80% of height
47833                     'stroke': "#666",
47834                     'stroke-width': "1",
47835                     'stroke-dasharray': "3",
47836                     'shape-rendering': "crispEdges",
47837                     'pointer-events': "none"
47838                 },
47839                 {
47840                     tag: "path",
47841                     id: this.svgID + "-svg-p",
47842                     'stroke': "navy",
47843                     'stroke-width': "3",
47844                     'fill': "none",
47845                     'pointer-events': 'none'
47846                 }
47847               ]
47848         });
47849         this.createSVG();
47850         this.svgBox = this.svgEl.dom.getScreenCTM();
47851     },
47852     createSVG : function(){ 
47853         var svg = this.signPanel;
47854         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47855         var t = this;
47856
47857         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47858         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47859         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47860         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47861         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47862         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47863         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47864         
47865     },
47866     isTouchEvent : function(e){
47867         return e.type.match(/^touch/);
47868     },
47869     getCoords : function (e) {
47870         var pt    = this.svgEl.dom.createSVGPoint();
47871         pt.x = e.clientX; 
47872         pt.y = e.clientY;
47873         if (this.isTouchEvent(e)) {
47874             pt.x =  e.targetTouches[0].clientX 
47875             pt.y = e.targetTouches[0].clientY;
47876         }
47877         var a = this.svgEl.dom.getScreenCTM();
47878         var b = a.inverse();
47879         var mx = pt.matrixTransform(b);
47880         return mx.x + ',' + mx.y;
47881     },
47882     //mouse event headler 
47883     down : function (e) {
47884         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47885         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47886         
47887         this.isMouseDown = true;
47888         
47889         e.preventDefault();
47890     },
47891     move : function (e) {
47892         if (this.isMouseDown) {
47893             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47894             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47895         }
47896         
47897         e.preventDefault();
47898     },
47899     up : function (e) {
47900         this.isMouseDown = false;
47901         var sp = this.signatureTmp.split(' ');
47902         
47903         if(sp.length > 1){
47904             if(!sp[sp.length-2].match(/^L/)){
47905                 sp.pop();
47906                 sp.pop();
47907                 sp.push("");
47908                 this.signatureTmp = sp.join(" ");
47909             }
47910         }
47911         if(this.getValue() != this.signatureTmp){
47912             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47913             this.isConfirmed = false;
47914         }
47915         e.preventDefault();
47916     },
47917     
47918     /**
47919      * Protected method that will not generally be called directly. It
47920      * is called when the editor creates its toolbar. Override this method if you need to
47921      * add custom toolbar buttons.
47922      * @param {HtmlEditor} editor
47923      */
47924     createToolbar : function(editor){
47925          function btn(id, toggle, handler){
47926             var xid = fid + '-'+ id ;
47927             return {
47928                 id : xid,
47929                 cmd : id,
47930                 cls : 'x-btn-icon x-edit-'+id,
47931                 enableToggle:toggle !== false,
47932                 scope: editor, // was editor...
47933                 handler:handler||editor.relayBtnCmd,
47934                 clickEvent:'mousedown',
47935                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47936                 tabIndex:-1
47937             };
47938         }
47939         
47940         
47941         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47942         this.tb = tb;
47943         this.tb.add(
47944            {
47945                 cls : ' x-signature-btn x-signature-'+id,
47946                 scope: editor, // was editor...
47947                 handler: this.reset,
47948                 clickEvent:'mousedown',
47949                 text: this.labels.clear
47950             },
47951             {
47952                  xtype : 'Fill',
47953                  xns: Roo.Toolbar
47954             }, 
47955             {
47956                 cls : '  x-signature-btn x-signature-'+id,
47957                 scope: editor, // was editor...
47958                 handler: this.confirmHandler,
47959                 clickEvent:'mousedown',
47960                 text: this.labels.confirm
47961             }
47962         );
47963     
47964     },
47965     //public
47966     /**
47967      * when user is clicked confirm then show this image.....
47968      * 
47969      * @return {String} Image Data URI
47970      */
47971     getImageDataURI : function(){
47972         var svg = this.svgEl.dom.parentNode.innerHTML;
47973         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47974         return src; 
47975     },
47976     /**
47977      * 
47978      * @return {Boolean} this.isConfirmed
47979      */
47980     getConfirmed : function(){
47981         return this.isConfirmed;
47982     },
47983     /**
47984      * 
47985      * @return {Number} this.width
47986      */
47987     getWidth : function(){
47988         return this.width;
47989     },
47990     /**
47991      * 
47992      * @return {Number} this.height
47993      */
47994     getHeight : function(){
47995         return this.height;
47996     },
47997     // private
47998     getSignature : function(){
47999         return this.signatureTmp;
48000     },
48001     // private
48002     reset : function(){
48003         this.signatureTmp = '';
48004         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48005         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48006         this.isConfirmed = false;
48007         Roo.form.Signature.superclass.reset.call(this);
48008     },
48009     setSignature : function(s){
48010         this.signatureTmp = s;
48011         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48012         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48013         this.setValue(s);
48014         this.isConfirmed = false;
48015         Roo.form.Signature.superclass.reset.call(this);
48016     }, 
48017     test : function(){
48018 //        Roo.log(this.signPanel.dom.contentWindow.up())
48019     },
48020     //private
48021     setConfirmed : function(){
48022         
48023         
48024         
48025 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48026     },
48027     // private
48028     confirmHandler : function(){
48029         if(!this.getSignature()){
48030             return;
48031         }
48032         
48033         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48034         this.setValue(this.getSignature());
48035         this.isConfirmed = true;
48036         
48037         this.fireEvent('confirm', this);
48038     },
48039     // private
48040     // Subclasses should provide the validation implementation by overriding this
48041     validateValue : function(value){
48042         if(this.allowBlank){
48043             return true;
48044         }
48045         
48046         if(this.isConfirmed){
48047             return true;
48048         }
48049         return false;
48050     }
48051 });/*
48052  * Based on:
48053  * Ext JS Library 1.1.1
48054  * Copyright(c) 2006-2007, Ext JS, LLC.
48055  *
48056  * Originally Released Under LGPL - original licence link has changed is not relivant.
48057  *
48058  * Fork - LGPL
48059  * <script type="text/javascript">
48060  */
48061  
48062
48063 /**
48064  * @class Roo.form.ComboBox
48065  * @extends Roo.form.TriggerField
48066  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48067  * @constructor
48068  * Create a new ComboBox.
48069  * @param {Object} config Configuration options
48070  */
48071 Roo.form.Select = function(config){
48072     Roo.form.Select.superclass.constructor.call(this, config);
48073      
48074 };
48075
48076 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48077     /**
48078      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48079      */
48080     /**
48081      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48082      * rendering into an Roo.Editor, defaults to false)
48083      */
48084     /**
48085      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48086      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48087      */
48088     /**
48089      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48090      */
48091     /**
48092      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48093      * the dropdown list (defaults to undefined, with no header element)
48094      */
48095
48096      /**
48097      * @cfg {String/Roo.Template} tpl The template to use to render the output
48098      */
48099      
48100     // private
48101     defaultAutoCreate : {tag: "select"  },
48102     /**
48103      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48104      */
48105     listWidth: undefined,
48106     /**
48107      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48108      * mode = 'remote' or 'text' if mode = 'local')
48109      */
48110     displayField: undefined,
48111     /**
48112      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48113      * mode = 'remote' or 'value' if mode = 'local'). 
48114      * Note: use of a valueField requires the user make a selection
48115      * in order for a value to be mapped.
48116      */
48117     valueField: undefined,
48118     
48119     
48120     /**
48121      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48122      * field's data value (defaults to the underlying DOM element's name)
48123      */
48124     hiddenName: undefined,
48125     /**
48126      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48127      */
48128     listClass: '',
48129     /**
48130      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48131      */
48132     selectedClass: 'x-combo-selected',
48133     /**
48134      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48135      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48136      * which displays a downward arrow icon).
48137      */
48138     triggerClass : 'x-form-arrow-trigger',
48139     /**
48140      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48141      */
48142     shadow:'sides',
48143     /**
48144      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48145      * anchor positions (defaults to 'tl-bl')
48146      */
48147     listAlign: 'tl-bl?',
48148     /**
48149      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48150      */
48151     maxHeight: 300,
48152     /**
48153      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48154      * query specified by the allQuery config option (defaults to 'query')
48155      */
48156     triggerAction: 'query',
48157     /**
48158      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48159      * (defaults to 4, does not apply if editable = false)
48160      */
48161     minChars : 4,
48162     /**
48163      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48164      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48165      */
48166     typeAhead: false,
48167     /**
48168      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48169      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48170      */
48171     queryDelay: 500,
48172     /**
48173      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48174      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48175      */
48176     pageSize: 0,
48177     /**
48178      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48179      * when editable = true (defaults to false)
48180      */
48181     selectOnFocus:false,
48182     /**
48183      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48184      */
48185     queryParam: 'query',
48186     /**
48187      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48188      * when mode = 'remote' (defaults to 'Loading...')
48189      */
48190     loadingText: 'Loading...',
48191     /**
48192      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48193      */
48194     resizable: false,
48195     /**
48196      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48197      */
48198     handleHeight : 8,
48199     /**
48200      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48201      * traditional select (defaults to true)
48202      */
48203     editable: true,
48204     /**
48205      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48206      */
48207     allQuery: '',
48208     /**
48209      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48210      */
48211     mode: 'remote',
48212     /**
48213      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48214      * listWidth has a higher value)
48215      */
48216     minListWidth : 70,
48217     /**
48218      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48219      * allow the user to set arbitrary text into the field (defaults to false)
48220      */
48221     forceSelection:false,
48222     /**
48223      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48224      * if typeAhead = true (defaults to 250)
48225      */
48226     typeAheadDelay : 250,
48227     /**
48228      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48229      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48230      */
48231     valueNotFoundText : undefined,
48232     
48233     /**
48234      * @cfg {String} defaultValue The value displayed after loading the store.
48235      */
48236     defaultValue: '',
48237     
48238     /**
48239      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48240      */
48241     blockFocus : false,
48242     
48243     /**
48244      * @cfg {Boolean} disableClear Disable showing of clear button.
48245      */
48246     disableClear : false,
48247     /**
48248      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48249      */
48250     alwaysQuery : false,
48251     
48252     //private
48253     addicon : false,
48254     editicon: false,
48255     
48256     // element that contains real text value.. (when hidden is used..)
48257      
48258     // private
48259     onRender : function(ct, position){
48260         Roo.form.Field.prototype.onRender.call(this, ct, position);
48261         
48262         if(this.store){
48263             this.store.on('beforeload', this.onBeforeLoad, this);
48264             this.store.on('load', this.onLoad, this);
48265             this.store.on('loadexception', this.onLoadException, this);
48266             this.store.load({});
48267         }
48268         
48269         
48270         
48271     },
48272
48273     // private
48274     initEvents : function(){
48275         //Roo.form.ComboBox.superclass.initEvents.call(this);
48276  
48277     },
48278
48279     onDestroy : function(){
48280        
48281         if(this.store){
48282             this.store.un('beforeload', this.onBeforeLoad, this);
48283             this.store.un('load', this.onLoad, this);
48284             this.store.un('loadexception', this.onLoadException, this);
48285         }
48286         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48287     },
48288
48289     // private
48290     fireKey : function(e){
48291         if(e.isNavKeyPress() && !this.list.isVisible()){
48292             this.fireEvent("specialkey", this, e);
48293         }
48294     },
48295
48296     // private
48297     onResize: function(w, h){
48298         
48299         return; 
48300     
48301         
48302     },
48303
48304     /**
48305      * Allow or prevent the user from directly editing the field text.  If false is passed,
48306      * the user will only be able to select from the items defined in the dropdown list.  This method
48307      * is the runtime equivalent of setting the 'editable' config option at config time.
48308      * @param {Boolean} value True to allow the user to directly edit the field text
48309      */
48310     setEditable : function(value){
48311          
48312     },
48313
48314     // private
48315     onBeforeLoad : function(){
48316         
48317         Roo.log("Select before load");
48318         return;
48319     
48320         this.innerList.update(this.loadingText ?
48321                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48322         //this.restrictHeight();
48323         this.selectedIndex = -1;
48324     },
48325
48326     // private
48327     onLoad : function(){
48328
48329     
48330         var dom = this.el.dom;
48331         dom.innerHTML = '';
48332          var od = dom.ownerDocument;
48333          
48334         if (this.emptyText) {
48335             var op = od.createElement('option');
48336             op.setAttribute('value', '');
48337             op.innerHTML = String.format('{0}', this.emptyText);
48338             dom.appendChild(op);
48339         }
48340         if(this.store.getCount() > 0){
48341            
48342             var vf = this.valueField;
48343             var df = this.displayField;
48344             this.store.data.each(function(r) {
48345                 // which colmsn to use... testing - cdoe / title..
48346                 var op = od.createElement('option');
48347                 op.setAttribute('value', r.data[vf]);
48348                 op.innerHTML = String.format('{0}', r.data[df]);
48349                 dom.appendChild(op);
48350             });
48351             if (typeof(this.defaultValue != 'undefined')) {
48352                 this.setValue(this.defaultValue);
48353             }
48354             
48355              
48356         }else{
48357             //this.onEmptyResults();
48358         }
48359         //this.el.focus();
48360     },
48361     // private
48362     onLoadException : function()
48363     {
48364         dom.innerHTML = '';
48365             
48366         Roo.log("Select on load exception");
48367         return;
48368     
48369         this.collapse();
48370         Roo.log(this.store.reader.jsonData);
48371         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48372             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48373         }
48374         
48375         
48376     },
48377     // private
48378     onTypeAhead : function(){
48379          
48380     },
48381
48382     // private
48383     onSelect : function(record, index){
48384         Roo.log('on select?');
48385         return;
48386         if(this.fireEvent('beforeselect', this, record, index) !== false){
48387             this.setFromData(index > -1 ? record.data : false);
48388             this.collapse();
48389             this.fireEvent('select', this, record, index);
48390         }
48391     },
48392
48393     /**
48394      * Returns the currently selected field value or empty string if no value is set.
48395      * @return {String} value The selected value
48396      */
48397     getValue : function(){
48398         var dom = this.el.dom;
48399         this.value = dom.options[dom.selectedIndex].value;
48400         return this.value;
48401         
48402     },
48403
48404     /**
48405      * Clears any text/value currently set in the field
48406      */
48407     clearValue : function(){
48408         this.value = '';
48409         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48410         
48411     },
48412
48413     /**
48414      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48415      * will be displayed in the field.  If the value does not match the data value of an existing item,
48416      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48417      * Otherwise the field will be blank (although the value will still be set).
48418      * @param {String} value The value to match
48419      */
48420     setValue : function(v){
48421         var d = this.el.dom;
48422         for (var i =0; i < d.options.length;i++) {
48423             if (v == d.options[i].value) {
48424                 d.selectedIndex = i;
48425                 this.value = v;
48426                 return;
48427             }
48428         }
48429         this.clearValue();
48430     },
48431     /**
48432      * @property {Object} the last set data for the element
48433      */
48434     
48435     lastData : false,
48436     /**
48437      * Sets the value of the field based on a object which is related to the record format for the store.
48438      * @param {Object} value the value to set as. or false on reset?
48439      */
48440     setFromData : function(o){
48441         Roo.log('setfrom data?');
48442          
48443         
48444         
48445     },
48446     // private
48447     reset : function(){
48448         this.clearValue();
48449     },
48450     // private
48451     findRecord : function(prop, value){
48452         
48453         return false;
48454     
48455         var record;
48456         if(this.store.getCount() > 0){
48457             this.store.each(function(r){
48458                 if(r.data[prop] == value){
48459                     record = r;
48460                     return false;
48461                 }
48462                 return true;
48463             });
48464         }
48465         return record;
48466     },
48467     
48468     getName: function()
48469     {
48470         // returns hidden if it's set..
48471         if (!this.rendered) {return ''};
48472         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48473         
48474     },
48475      
48476
48477     
48478
48479     // private
48480     onEmptyResults : function(){
48481         Roo.log('empty results');
48482         //this.collapse();
48483     },
48484
48485     /**
48486      * Returns true if the dropdown list is expanded, else false.
48487      */
48488     isExpanded : function(){
48489         return false;
48490     },
48491
48492     /**
48493      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48494      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48495      * @param {String} value The data value of the item to select
48496      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48497      * selected item if it is not currently in view (defaults to true)
48498      * @return {Boolean} True if the value matched an item in the list, else false
48499      */
48500     selectByValue : function(v, scrollIntoView){
48501         Roo.log('select By Value');
48502         return false;
48503     
48504         if(v !== undefined && v !== null){
48505             var r = this.findRecord(this.valueField || this.displayField, v);
48506             if(r){
48507                 this.select(this.store.indexOf(r), scrollIntoView);
48508                 return true;
48509             }
48510         }
48511         return false;
48512     },
48513
48514     /**
48515      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48516      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48517      * @param {Number} index The zero-based index of the list item to select
48518      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48519      * selected item if it is not currently in view (defaults to true)
48520      */
48521     select : function(index, scrollIntoView){
48522         Roo.log('select ');
48523         return  ;
48524         
48525         this.selectedIndex = index;
48526         this.view.select(index);
48527         if(scrollIntoView !== false){
48528             var el = this.view.getNode(index);
48529             if(el){
48530                 this.innerList.scrollChildIntoView(el, false);
48531             }
48532         }
48533     },
48534
48535       
48536
48537     // private
48538     validateBlur : function(){
48539         
48540         return;
48541         
48542     },
48543
48544     // private
48545     initQuery : function(){
48546         this.doQuery(this.getRawValue());
48547     },
48548
48549     // private
48550     doForce : function(){
48551         if(this.el.dom.value.length > 0){
48552             this.el.dom.value =
48553                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48554              
48555         }
48556     },
48557
48558     /**
48559      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48560      * query allowing the query action to be canceled if needed.
48561      * @param {String} query The SQL query to execute
48562      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48563      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48564      * saved in the current store (defaults to false)
48565      */
48566     doQuery : function(q, forceAll){
48567         
48568         Roo.log('doQuery?');
48569         if(q === undefined || q === null){
48570             q = '';
48571         }
48572         var qe = {
48573             query: q,
48574             forceAll: forceAll,
48575             combo: this,
48576             cancel:false
48577         };
48578         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48579             return false;
48580         }
48581         q = qe.query;
48582         forceAll = qe.forceAll;
48583         if(forceAll === true || (q.length >= this.minChars)){
48584             if(this.lastQuery != q || this.alwaysQuery){
48585                 this.lastQuery = q;
48586                 if(this.mode == 'local'){
48587                     this.selectedIndex = -1;
48588                     if(forceAll){
48589                         this.store.clearFilter();
48590                     }else{
48591                         this.store.filter(this.displayField, q);
48592                     }
48593                     this.onLoad();
48594                 }else{
48595                     this.store.baseParams[this.queryParam] = q;
48596                     this.store.load({
48597                         params: this.getParams(q)
48598                     });
48599                     this.expand();
48600                 }
48601             }else{
48602                 this.selectedIndex = -1;
48603                 this.onLoad();   
48604             }
48605         }
48606     },
48607
48608     // private
48609     getParams : function(q){
48610         var p = {};
48611         //p[this.queryParam] = q;
48612         if(this.pageSize){
48613             p.start = 0;
48614             p.limit = this.pageSize;
48615         }
48616         return p;
48617     },
48618
48619     /**
48620      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48621      */
48622     collapse : function(){
48623         
48624     },
48625
48626     // private
48627     collapseIf : function(e){
48628         
48629     },
48630
48631     /**
48632      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48633      */
48634     expand : function(){
48635         
48636     } ,
48637
48638     // private
48639      
48640
48641     /** 
48642     * @cfg {Boolean} grow 
48643     * @hide 
48644     */
48645     /** 
48646     * @cfg {Number} growMin 
48647     * @hide 
48648     */
48649     /** 
48650     * @cfg {Number} growMax 
48651     * @hide 
48652     */
48653     /**
48654      * @hide
48655      * @method autoSize
48656      */
48657     
48658     setWidth : function()
48659     {
48660         
48661     },
48662     getResizeEl : function(){
48663         return this.el;
48664     }
48665 });//<script type="text/javasscript">
48666  
48667
48668 /**
48669  * @class Roo.DDView
48670  * A DnD enabled version of Roo.View.
48671  * @param {Element/String} container The Element in which to create the View.
48672  * @param {String} tpl The template string used to create the markup for each element of the View
48673  * @param {Object} config The configuration properties. These include all the config options of
48674  * {@link Roo.View} plus some specific to this class.<br>
48675  * <p>
48676  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48677  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48678  * <p>
48679  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48680 .x-view-drag-insert-above {
48681         border-top:1px dotted #3366cc;
48682 }
48683 .x-view-drag-insert-below {
48684         border-bottom:1px dotted #3366cc;
48685 }
48686 </code></pre>
48687  * 
48688  */
48689  
48690 Roo.DDView = function(container, tpl, config) {
48691     Roo.DDView.superclass.constructor.apply(this, arguments);
48692     this.getEl().setStyle("outline", "0px none");
48693     this.getEl().unselectable();
48694     if (this.dragGroup) {
48695                 this.setDraggable(this.dragGroup.split(","));
48696     }
48697     if (this.dropGroup) {
48698                 this.setDroppable(this.dropGroup.split(","));
48699     }
48700     if (this.deletable) {
48701         this.setDeletable();
48702     }
48703     this.isDirtyFlag = false;
48704         this.addEvents({
48705                 "drop" : true
48706         });
48707 };
48708
48709 Roo.extend(Roo.DDView, Roo.View, {
48710 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48711 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48712 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48713 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48714
48715         isFormField: true,
48716
48717         reset: Roo.emptyFn,
48718         
48719         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48720
48721         validate: function() {
48722                 return true;
48723         },
48724         
48725         destroy: function() {
48726                 this.purgeListeners();
48727                 this.getEl.removeAllListeners();
48728                 this.getEl().remove();
48729                 if (this.dragZone) {
48730                         if (this.dragZone.destroy) {
48731                                 this.dragZone.destroy();
48732                         }
48733                 }
48734                 if (this.dropZone) {
48735                         if (this.dropZone.destroy) {
48736                                 this.dropZone.destroy();
48737                         }
48738                 }
48739         },
48740
48741 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48742         getName: function() {
48743                 return this.name;
48744         },
48745
48746 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48747         setValue: function(v) {
48748                 if (!this.store) {
48749                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48750                 }
48751                 var data = {};
48752                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48753                 this.store.proxy = new Roo.data.MemoryProxy(data);
48754                 this.store.load();
48755         },
48756
48757 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48758         getValue: function() {
48759                 var result = '(';
48760                 this.store.each(function(rec) {
48761                         result += rec.id + ',';
48762                 });
48763                 return result.substr(0, result.length - 1) + ')';
48764         },
48765         
48766         getIds: function() {
48767                 var i = 0, result = new Array(this.store.getCount());
48768                 this.store.each(function(rec) {
48769                         result[i++] = rec.id;
48770                 });
48771                 return result;
48772         },
48773         
48774         isDirty: function() {
48775                 return this.isDirtyFlag;
48776         },
48777
48778 /**
48779  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48780  *      whole Element becomes the target, and this causes the drop gesture to append.
48781  */
48782     getTargetFromEvent : function(e) {
48783                 var target = e.getTarget();
48784                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48785                 target = target.parentNode;
48786                 }
48787                 if (!target) {
48788                         target = this.el.dom.lastChild || this.el.dom;
48789                 }
48790                 return target;
48791     },
48792
48793 /**
48794  *      Create the drag data which consists of an object which has the property "ddel" as
48795  *      the drag proxy element. 
48796  */
48797     getDragData : function(e) {
48798         var target = this.findItemFromChild(e.getTarget());
48799                 if(target) {
48800                         this.handleSelection(e);
48801                         var selNodes = this.getSelectedNodes();
48802             var dragData = {
48803                 source: this,
48804                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48805                 nodes: selNodes,
48806                 records: []
48807                         };
48808                         var selectedIndices = this.getSelectedIndexes();
48809                         for (var i = 0; i < selectedIndices.length; i++) {
48810                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48811                         }
48812                         if (selNodes.length == 1) {
48813                                 dragData.ddel = target.cloneNode(true); // the div element
48814                         } else {
48815                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48816                                 div.className = 'multi-proxy';
48817                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48818                                         div.appendChild(selNodes[i].cloneNode(true));
48819                                 }
48820                                 dragData.ddel = div;
48821                         }
48822             //console.log(dragData)
48823             //console.log(dragData.ddel.innerHTML)
48824                         return dragData;
48825                 }
48826         //console.log('nodragData')
48827                 return false;
48828     },
48829     
48830 /**     Specify to which ddGroup items in this DDView may be dragged. */
48831     setDraggable: function(ddGroup) {
48832         if (ddGroup instanceof Array) {
48833                 Roo.each(ddGroup, this.setDraggable, this);
48834                 return;
48835         }
48836         if (this.dragZone) {
48837                 this.dragZone.addToGroup(ddGroup);
48838         } else {
48839                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48840                                 containerScroll: true,
48841                                 ddGroup: ddGroup 
48842
48843                         });
48844 //                      Draggability implies selection. DragZone's mousedown selects the element.
48845                         if (!this.multiSelect) { this.singleSelect = true; }
48846
48847 //                      Wire the DragZone's handlers up to methods in *this*
48848                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48849                 }
48850     },
48851
48852 /**     Specify from which ddGroup this DDView accepts drops. */
48853     setDroppable: function(ddGroup) {
48854         if (ddGroup instanceof Array) {
48855                 Roo.each(ddGroup, this.setDroppable, this);
48856                 return;
48857         }
48858         if (this.dropZone) {
48859                 this.dropZone.addToGroup(ddGroup);
48860         } else {
48861                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48862                                 containerScroll: true,
48863                                 ddGroup: ddGroup
48864                         });
48865
48866 //                      Wire the DropZone's handlers up to methods in *this*
48867                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48868                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48869                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48870                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48871                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48872                 }
48873     },
48874
48875 /**     Decide whether to drop above or below a View node. */
48876     getDropPoint : function(e, n, dd){
48877         if (n == this.el.dom) { return "above"; }
48878                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48879                 var c = t + (b - t) / 2;
48880                 var y = Roo.lib.Event.getPageY(e);
48881                 if(y <= c) {
48882                         return "above";
48883                 }else{
48884                         return "below";
48885                 }
48886     },
48887
48888     onNodeEnter : function(n, dd, e, data){
48889                 return false;
48890     },
48891     
48892     onNodeOver : function(n, dd, e, data){
48893                 var pt = this.getDropPoint(e, n, dd);
48894                 // set the insert point style on the target node
48895                 var dragElClass = this.dropNotAllowed;
48896                 if (pt) {
48897                         var targetElClass;
48898                         if (pt == "above"){
48899                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48900                                 targetElClass = "x-view-drag-insert-above";
48901                         } else {
48902                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48903                                 targetElClass = "x-view-drag-insert-below";
48904                         }
48905                         if (this.lastInsertClass != targetElClass){
48906                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48907                                 this.lastInsertClass = targetElClass;
48908                         }
48909                 }
48910                 return dragElClass;
48911         },
48912
48913     onNodeOut : function(n, dd, e, data){
48914                 this.removeDropIndicators(n);
48915     },
48916
48917     onNodeDrop : function(n, dd, e, data){
48918         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48919                 return false;
48920         }
48921         var pt = this.getDropPoint(e, n, dd);
48922                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48923                 if (pt == "below") { insertAt++; }
48924                 for (var i = 0; i < data.records.length; i++) {
48925                         var r = data.records[i];
48926                         var dup = this.store.getById(r.id);
48927                         if (dup && (dd != this.dragZone)) {
48928                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48929                         } else {
48930                                 if (data.copy) {
48931                                         this.store.insert(insertAt++, r.copy());
48932                                 } else {
48933                                         data.source.isDirtyFlag = true;
48934                                         r.store.remove(r);
48935                                         this.store.insert(insertAt++, r);
48936                                 }
48937                                 this.isDirtyFlag = true;
48938                         }
48939                 }
48940                 this.dragZone.cachedTarget = null;
48941                 return true;
48942     },
48943
48944     removeDropIndicators : function(n){
48945                 if(n){
48946                         Roo.fly(n).removeClass([
48947                                 "x-view-drag-insert-above",
48948                                 "x-view-drag-insert-below"]);
48949                         this.lastInsertClass = "_noclass";
48950                 }
48951     },
48952
48953 /**
48954  *      Utility method. Add a delete option to the DDView's context menu.
48955  *      @param {String} imageUrl The URL of the "delete" icon image.
48956  */
48957         setDeletable: function(imageUrl) {
48958                 if (!this.singleSelect && !this.multiSelect) {
48959                         this.singleSelect = true;
48960                 }
48961                 var c = this.getContextMenu();
48962                 this.contextMenu.on("itemclick", function(item) {
48963                         switch (item.id) {
48964                                 case "delete":
48965                                         this.remove(this.getSelectedIndexes());
48966                                         break;
48967                         }
48968                 }, this);
48969                 this.contextMenu.add({
48970                         icon: imageUrl,
48971                         id: "delete",
48972                         text: 'Delete'
48973                 });
48974         },
48975         
48976 /**     Return the context menu for this DDView. */
48977         getContextMenu: function() {
48978                 if (!this.contextMenu) {
48979 //                      Create the View's context menu
48980                         this.contextMenu = new Roo.menu.Menu({
48981                                 id: this.id + "-contextmenu"
48982                         });
48983                         this.el.on("contextmenu", this.showContextMenu, this);
48984                 }
48985                 return this.contextMenu;
48986         },
48987         
48988         disableContextMenu: function() {
48989                 if (this.contextMenu) {
48990                         this.el.un("contextmenu", this.showContextMenu, this);
48991                 }
48992         },
48993
48994         showContextMenu: function(e, item) {
48995         item = this.findItemFromChild(e.getTarget());
48996                 if (item) {
48997                         e.stopEvent();
48998                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48999                         this.contextMenu.showAt(e.getXY());
49000             }
49001     },
49002
49003 /**
49004  *      Remove {@link Roo.data.Record}s at the specified indices.
49005  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49006  */
49007     remove: function(selectedIndices) {
49008                 selectedIndices = [].concat(selectedIndices);
49009                 for (var i = 0; i < selectedIndices.length; i++) {
49010                         var rec = this.store.getAt(selectedIndices[i]);
49011                         this.store.remove(rec);
49012                 }
49013     },
49014
49015 /**
49016  *      Double click fires the event, but also, if this is draggable, and there is only one other
49017  *      related DropZone, it transfers the selected node.
49018  */
49019     onDblClick : function(e){
49020         var item = this.findItemFromChild(e.getTarget());
49021         if(item){
49022             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49023                 return false;
49024             }
49025             if (this.dragGroup) {
49026                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49027                     while (targets.indexOf(this.dropZone) > -1) {
49028                             targets.remove(this.dropZone);
49029                                 }
49030                     if (targets.length == 1) {
49031                                         this.dragZone.cachedTarget = null;
49032                         var el = Roo.get(targets[0].getEl());
49033                         var box = el.getBox(true);
49034                         targets[0].onNodeDrop(el.dom, {
49035                                 target: el.dom,
49036                                 xy: [box.x, box.y + box.height - 1]
49037                         }, null, this.getDragData(e));
49038                     }
49039                 }
49040         }
49041     },
49042     
49043     handleSelection: function(e) {
49044                 this.dragZone.cachedTarget = null;
49045         var item = this.findItemFromChild(e.getTarget());
49046         if (!item) {
49047                 this.clearSelections(true);
49048                 return;
49049         }
49050                 if (item && (this.multiSelect || this.singleSelect)){
49051                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49052                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49053                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49054                                 this.unselect(item);
49055                         } else {
49056                                 this.select(item, this.multiSelect && e.ctrlKey);
49057                                 this.lastSelection = item;
49058                         }
49059                 }
49060     },
49061
49062     onItemClick : function(item, index, e){
49063                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49064                         return false;
49065                 }
49066                 return true;
49067     },
49068
49069     unselect : function(nodeInfo, suppressEvent){
49070                 var node = this.getNode(nodeInfo);
49071                 if(node && this.isSelected(node)){
49072                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49073                                 Roo.fly(node).removeClass(this.selectedClass);
49074                                 this.selections.remove(node);
49075                                 if(!suppressEvent){
49076                                         this.fireEvent("selectionchange", this, this.selections);
49077                                 }
49078                         }
49079                 }
49080     }
49081 });
49082 /*
49083  * Based on:
49084  * Ext JS Library 1.1.1
49085  * Copyright(c) 2006-2007, Ext JS, LLC.
49086  *
49087  * Originally Released Under LGPL - original licence link has changed is not relivant.
49088  *
49089  * Fork - LGPL
49090  * <script type="text/javascript">
49091  */
49092  
49093 /**
49094  * @class Roo.LayoutManager
49095  * @extends Roo.util.Observable
49096  * Base class for layout managers.
49097  */
49098 Roo.LayoutManager = function(container, config){
49099     Roo.LayoutManager.superclass.constructor.call(this);
49100     this.el = Roo.get(container);
49101     // ie scrollbar fix
49102     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49103         document.body.scroll = "no";
49104     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49105         this.el.position('relative');
49106     }
49107     this.id = this.el.id;
49108     this.el.addClass("x-layout-container");
49109     /** false to disable window resize monitoring @type Boolean */
49110     this.monitorWindowResize = true;
49111     this.regions = {};
49112     this.addEvents({
49113         /**
49114          * @event layout
49115          * Fires when a layout is performed. 
49116          * @param {Roo.LayoutManager} this
49117          */
49118         "layout" : true,
49119         /**
49120          * @event regionresized
49121          * Fires when the user resizes a region. 
49122          * @param {Roo.LayoutRegion} region The resized region
49123          * @param {Number} newSize The new size (width for east/west, height for north/south)
49124          */
49125         "regionresized" : true,
49126         /**
49127          * @event regioncollapsed
49128          * Fires when a region is collapsed. 
49129          * @param {Roo.LayoutRegion} region The collapsed region
49130          */
49131         "regioncollapsed" : true,
49132         /**
49133          * @event regionexpanded
49134          * Fires when a region is expanded.  
49135          * @param {Roo.LayoutRegion} region The expanded region
49136          */
49137         "regionexpanded" : true
49138     });
49139     this.updating = false;
49140     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49141 };
49142
49143 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49144     /**
49145      * Returns true if this layout is currently being updated
49146      * @return {Boolean}
49147      */
49148     isUpdating : function(){
49149         return this.updating; 
49150     },
49151     
49152     /**
49153      * Suspend the LayoutManager from doing auto-layouts while
49154      * making multiple add or remove calls
49155      */
49156     beginUpdate : function(){
49157         this.updating = true;    
49158     },
49159     
49160     /**
49161      * Restore auto-layouts and optionally disable the manager from performing a layout
49162      * @param {Boolean} noLayout true to disable a layout update 
49163      */
49164     endUpdate : function(noLayout){
49165         this.updating = false;
49166         if(!noLayout){
49167             this.layout();
49168         }    
49169     },
49170     
49171     layout: function(){
49172         
49173     },
49174     
49175     onRegionResized : function(region, newSize){
49176         this.fireEvent("regionresized", region, newSize);
49177         this.layout();
49178     },
49179     
49180     onRegionCollapsed : function(region){
49181         this.fireEvent("regioncollapsed", region);
49182     },
49183     
49184     onRegionExpanded : function(region){
49185         this.fireEvent("regionexpanded", region);
49186     },
49187         
49188     /**
49189      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49190      * performs box-model adjustments.
49191      * @return {Object} The size as an object {width: (the width), height: (the height)}
49192      */
49193     getViewSize : function(){
49194         var size;
49195         if(this.el.dom != document.body){
49196             size = this.el.getSize();
49197         }else{
49198             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49199         }
49200         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49201         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49202         return size;
49203     },
49204     
49205     /**
49206      * Returns the Element this layout is bound to.
49207      * @return {Roo.Element}
49208      */
49209     getEl : function(){
49210         return this.el;
49211     },
49212     
49213     /**
49214      * Returns the specified region.
49215      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49216      * @return {Roo.LayoutRegion}
49217      */
49218     getRegion : function(target){
49219         return this.regions[target.toLowerCase()];
49220     },
49221     
49222     onWindowResize : function(){
49223         if(this.monitorWindowResize){
49224             this.layout();
49225         }
49226     }
49227 });/*
49228  * Based on:
49229  * Ext JS Library 1.1.1
49230  * Copyright(c) 2006-2007, Ext JS, LLC.
49231  *
49232  * Originally Released Under LGPL - original licence link has changed is not relivant.
49233  *
49234  * Fork - LGPL
49235  * <script type="text/javascript">
49236  */
49237 /**
49238  * @class Roo.BorderLayout
49239  * @extends Roo.LayoutManager
49240  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49241  * please see: <br><br>
49242  * <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>
49243  * <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>
49244  * Example:
49245  <pre><code>
49246  var layout = new Roo.BorderLayout(document.body, {
49247     north: {
49248         initialSize: 25,
49249         titlebar: false
49250     },
49251     west: {
49252         split:true,
49253         initialSize: 200,
49254         minSize: 175,
49255         maxSize: 400,
49256         titlebar: true,
49257         collapsible: true
49258     },
49259     east: {
49260         split:true,
49261         initialSize: 202,
49262         minSize: 175,
49263         maxSize: 400,
49264         titlebar: true,
49265         collapsible: true
49266     },
49267     south: {
49268         split:true,
49269         initialSize: 100,
49270         minSize: 100,
49271         maxSize: 200,
49272         titlebar: true,
49273         collapsible: true
49274     },
49275     center: {
49276         titlebar: true,
49277         autoScroll:true,
49278         resizeTabs: true,
49279         minTabWidth: 50,
49280         preferredTabWidth: 150
49281     }
49282 });
49283
49284 // shorthand
49285 var CP = Roo.ContentPanel;
49286
49287 layout.beginUpdate();
49288 layout.add("north", new CP("north", "North"));
49289 layout.add("south", new CP("south", {title: "South", closable: true}));
49290 layout.add("west", new CP("west", {title: "West"}));
49291 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49292 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49293 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49294 layout.getRegion("center").showPanel("center1");
49295 layout.endUpdate();
49296 </code></pre>
49297
49298 <b>The container the layout is rendered into can be either the body element or any other element.
49299 If it is not the body element, the container needs to either be an absolute positioned element,
49300 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49301 the container size if it is not the body element.</b>
49302
49303 * @constructor
49304 * Create a new BorderLayout
49305 * @param {String/HTMLElement/Element} container The container this layout is bound to
49306 * @param {Object} config Configuration options
49307  */
49308 Roo.BorderLayout = function(container, config){
49309     config = config || {};
49310     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49311     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49312     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49313         var target = this.factory.validRegions[i];
49314         if(config[target]){
49315             this.addRegion(target, config[target]);
49316         }
49317     }
49318 };
49319
49320 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49321     /**
49322      * Creates and adds a new region if it doesn't already exist.
49323      * @param {String} target The target region key (north, south, east, west or center).
49324      * @param {Object} config The regions config object
49325      * @return {BorderLayoutRegion} The new region
49326      */
49327     addRegion : function(target, config){
49328         if(!this.regions[target]){
49329             var r = this.factory.create(target, this, config);
49330             this.bindRegion(target, r);
49331         }
49332         return this.regions[target];
49333     },
49334
49335     // private (kinda)
49336     bindRegion : function(name, r){
49337         this.regions[name] = r;
49338         r.on("visibilitychange", this.layout, this);
49339         r.on("paneladded", this.layout, this);
49340         r.on("panelremoved", this.layout, this);
49341         r.on("invalidated", this.layout, this);
49342         r.on("resized", this.onRegionResized, this);
49343         r.on("collapsed", this.onRegionCollapsed, this);
49344         r.on("expanded", this.onRegionExpanded, this);
49345     },
49346
49347     /**
49348      * Performs a layout update.
49349      */
49350     layout : function(){
49351         if(this.updating) return;
49352         var size = this.getViewSize();
49353         var w = size.width;
49354         var h = size.height;
49355         var centerW = w;
49356         var centerH = h;
49357         var centerY = 0;
49358         var centerX = 0;
49359         //var x = 0, y = 0;
49360
49361         var rs = this.regions;
49362         var north = rs["north"];
49363         var south = rs["south"]; 
49364         var west = rs["west"];
49365         var east = rs["east"];
49366         var center = rs["center"];
49367         //if(this.hideOnLayout){ // not supported anymore
49368             //c.el.setStyle("display", "none");
49369         //}
49370         if(north && north.isVisible()){
49371             var b = north.getBox();
49372             var m = north.getMargins();
49373             b.width = w - (m.left+m.right);
49374             b.x = m.left;
49375             b.y = m.top;
49376             centerY = b.height + b.y + m.bottom;
49377             centerH -= centerY;
49378             north.updateBox(this.safeBox(b));
49379         }
49380         if(south && south.isVisible()){
49381             var b = south.getBox();
49382             var m = south.getMargins();
49383             b.width = w - (m.left+m.right);
49384             b.x = m.left;
49385             var totalHeight = (b.height + m.top + m.bottom);
49386             b.y = h - totalHeight + m.top;
49387             centerH -= totalHeight;
49388             south.updateBox(this.safeBox(b));
49389         }
49390         if(west && west.isVisible()){
49391             var b = west.getBox();
49392             var m = west.getMargins();
49393             b.height = centerH - (m.top+m.bottom);
49394             b.x = m.left;
49395             b.y = centerY + m.top;
49396             var totalWidth = (b.width + m.left + m.right);
49397             centerX += totalWidth;
49398             centerW -= totalWidth;
49399             west.updateBox(this.safeBox(b));
49400         }
49401         if(east && east.isVisible()){
49402             var b = east.getBox();
49403             var m = east.getMargins();
49404             b.height = centerH - (m.top+m.bottom);
49405             var totalWidth = (b.width + m.left + m.right);
49406             b.x = w - totalWidth + m.left;
49407             b.y = centerY + m.top;
49408             centerW -= totalWidth;
49409             east.updateBox(this.safeBox(b));
49410         }
49411         if(center){
49412             var m = center.getMargins();
49413             var centerBox = {
49414                 x: centerX + m.left,
49415                 y: centerY + m.top,
49416                 width: centerW - (m.left+m.right),
49417                 height: centerH - (m.top+m.bottom)
49418             };
49419             //if(this.hideOnLayout){
49420                 //center.el.setStyle("display", "block");
49421             //}
49422             center.updateBox(this.safeBox(centerBox));
49423         }
49424         this.el.repaint();
49425         this.fireEvent("layout", this);
49426     },
49427
49428     // private
49429     safeBox : function(box){
49430         box.width = Math.max(0, box.width);
49431         box.height = Math.max(0, box.height);
49432         return box;
49433     },
49434
49435     /**
49436      * Adds a ContentPanel (or subclass) to this layout.
49437      * @param {String} target The target region key (north, south, east, west or center).
49438      * @param {Roo.ContentPanel} panel The panel to add
49439      * @return {Roo.ContentPanel} The added panel
49440      */
49441     add : function(target, panel){
49442          
49443         target = target.toLowerCase();
49444         return this.regions[target].add(panel);
49445     },
49446
49447     /**
49448      * Remove a ContentPanel (or subclass) to this layout.
49449      * @param {String} target The target region key (north, south, east, west or center).
49450      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49451      * @return {Roo.ContentPanel} The removed panel
49452      */
49453     remove : function(target, panel){
49454         target = target.toLowerCase();
49455         return this.regions[target].remove(panel);
49456     },
49457
49458     /**
49459      * Searches all regions for a panel with the specified id
49460      * @param {String} panelId
49461      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49462      */
49463     findPanel : function(panelId){
49464         var rs = this.regions;
49465         for(var target in rs){
49466             if(typeof rs[target] != "function"){
49467                 var p = rs[target].getPanel(panelId);
49468                 if(p){
49469                     return p;
49470                 }
49471             }
49472         }
49473         return null;
49474     },
49475
49476     /**
49477      * Searches all regions for a panel with the specified id and activates (shows) it.
49478      * @param {String/ContentPanel} panelId The panels id or the panel itself
49479      * @return {Roo.ContentPanel} The shown panel or null
49480      */
49481     showPanel : function(panelId) {
49482       var rs = this.regions;
49483       for(var target in rs){
49484          var r = rs[target];
49485          if(typeof r != "function"){
49486             if(r.hasPanel(panelId)){
49487                return r.showPanel(panelId);
49488             }
49489          }
49490       }
49491       return null;
49492    },
49493
49494    /**
49495      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49496      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49497      */
49498     restoreState : function(provider){
49499         if(!provider){
49500             provider = Roo.state.Manager;
49501         }
49502         var sm = new Roo.LayoutStateManager();
49503         sm.init(this, provider);
49504     },
49505
49506     /**
49507      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49508      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49509      * a valid ContentPanel config object.  Example:
49510      * <pre><code>
49511 // Create the main layout
49512 var layout = new Roo.BorderLayout('main-ct', {
49513     west: {
49514         split:true,
49515         minSize: 175,
49516         titlebar: true
49517     },
49518     center: {
49519         title:'Components'
49520     }
49521 }, 'main-ct');
49522
49523 // Create and add multiple ContentPanels at once via configs
49524 layout.batchAdd({
49525    west: {
49526        id: 'source-files',
49527        autoCreate:true,
49528        title:'Ext Source Files',
49529        autoScroll:true,
49530        fitToFrame:true
49531    },
49532    center : {
49533        el: cview,
49534        autoScroll:true,
49535        fitToFrame:true,
49536        toolbar: tb,
49537        resizeEl:'cbody'
49538    }
49539 });
49540 </code></pre>
49541      * @param {Object} regions An object containing ContentPanel configs by region name
49542      */
49543     batchAdd : function(regions){
49544         this.beginUpdate();
49545         for(var rname in regions){
49546             var lr = this.regions[rname];
49547             if(lr){
49548                 this.addTypedPanels(lr, regions[rname]);
49549             }
49550         }
49551         this.endUpdate();
49552     },
49553
49554     // private
49555     addTypedPanels : function(lr, ps){
49556         if(typeof ps == 'string'){
49557             lr.add(new Roo.ContentPanel(ps));
49558         }
49559         else if(ps instanceof Array){
49560             for(var i =0, len = ps.length; i < len; i++){
49561                 this.addTypedPanels(lr, ps[i]);
49562             }
49563         }
49564         else if(!ps.events){ // raw config?
49565             var el = ps.el;
49566             delete ps.el; // prevent conflict
49567             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49568         }
49569         else {  // panel object assumed!
49570             lr.add(ps);
49571         }
49572     },
49573     /**
49574      * Adds a xtype elements to the layout.
49575      * <pre><code>
49576
49577 layout.addxtype({
49578        xtype : 'ContentPanel',
49579        region: 'west',
49580        items: [ .... ]
49581    }
49582 );
49583
49584 layout.addxtype({
49585         xtype : 'NestedLayoutPanel',
49586         region: 'west',
49587         layout: {
49588            center: { },
49589            west: { }   
49590         },
49591         items : [ ... list of content panels or nested layout panels.. ]
49592    }
49593 );
49594 </code></pre>
49595      * @param {Object} cfg Xtype definition of item to add.
49596      */
49597     addxtype : function(cfg)
49598     {
49599         // basically accepts a pannel...
49600         // can accept a layout region..!?!?
49601         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49602         
49603         if (!cfg.xtype.match(/Panel$/)) {
49604             return false;
49605         }
49606         var ret = false;
49607         
49608         if (typeof(cfg.region) == 'undefined') {
49609             Roo.log("Failed to add Panel, region was not set");
49610             Roo.log(cfg);
49611             return false;
49612         }
49613         var region = cfg.region;
49614         delete cfg.region;
49615         
49616           
49617         var xitems = [];
49618         if (cfg.items) {
49619             xitems = cfg.items;
49620             delete cfg.items;
49621         }
49622         var nb = false;
49623         
49624         switch(cfg.xtype) 
49625         {
49626             case 'ContentPanel':  // ContentPanel (el, cfg)
49627             case 'ScrollPanel':  // ContentPanel (el, cfg)
49628             case 'ViewPanel': 
49629                 if(cfg.autoCreate) {
49630                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49631                 } else {
49632                     var el = this.el.createChild();
49633                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49634                 }
49635                 
49636                 this.add(region, ret);
49637                 break;
49638             
49639             
49640             case 'TreePanel': // our new panel!
49641                 cfg.el = this.el.createChild();
49642                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49643                 this.add(region, ret);
49644                 break;
49645             
49646             case 'NestedLayoutPanel': 
49647                 // create a new Layout (which is  a Border Layout...
49648                 var el = this.el.createChild();
49649                 var clayout = cfg.layout;
49650                 delete cfg.layout;
49651                 clayout.items   = clayout.items  || [];
49652                 // replace this exitems with the clayout ones..
49653                 xitems = clayout.items;
49654                  
49655                 
49656                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49657                     cfg.background = false;
49658                 }
49659                 var layout = new Roo.BorderLayout(el, clayout);
49660                 
49661                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49662                 //console.log('adding nested layout panel '  + cfg.toSource());
49663                 this.add(region, ret);
49664                 nb = {}; /// find first...
49665                 break;
49666                 
49667             case 'GridPanel': 
49668             
49669                 // needs grid and region
49670                 
49671                 //var el = this.getRegion(region).el.createChild();
49672                 var el = this.el.createChild();
49673                 // create the grid first...
49674                 
49675                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49676                 delete cfg.grid;
49677                 if (region == 'center' && this.active ) {
49678                     cfg.background = false;
49679                 }
49680                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49681                 
49682                 this.add(region, ret);
49683                 if (cfg.background) {
49684                     ret.on('activate', function(gp) {
49685                         if (!gp.grid.rendered) {
49686                             gp.grid.render();
49687                         }
49688                     });
49689                 } else {
49690                     grid.render();
49691                 }
49692                 break;
49693            
49694            
49695            
49696                 
49697                 
49698                 
49699             default:
49700                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49701                     
49702                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49703                     this.add(region, ret);
49704                 } else {
49705                 
49706                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49707                     return null;
49708                 }
49709                 
49710              // GridPanel (grid, cfg)
49711             
49712         }
49713         this.beginUpdate();
49714         // add children..
49715         var region = '';
49716         var abn = {};
49717         Roo.each(xitems, function(i)  {
49718             region = nb && i.region ? i.region : false;
49719             
49720             var add = ret.addxtype(i);
49721            
49722             if (region) {
49723                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49724                 if (!i.background) {
49725                     abn[region] = nb[region] ;
49726                 }
49727             }
49728             
49729         });
49730         this.endUpdate();
49731
49732         // make the last non-background panel active..
49733         //if (nb) { Roo.log(abn); }
49734         if (nb) {
49735             
49736             for(var r in abn) {
49737                 region = this.getRegion(r);
49738                 if (region) {
49739                     // tried using nb[r], but it does not work..
49740                      
49741                     region.showPanel(abn[r]);
49742                    
49743                 }
49744             }
49745         }
49746         return ret;
49747         
49748     }
49749 });
49750
49751 /**
49752  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49753  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49754  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49755  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49756  * <pre><code>
49757 // shorthand
49758 var CP = Roo.ContentPanel;
49759
49760 var layout = Roo.BorderLayout.create({
49761     north: {
49762         initialSize: 25,
49763         titlebar: false,
49764         panels: [new CP("north", "North")]
49765     },
49766     west: {
49767         split:true,
49768         initialSize: 200,
49769         minSize: 175,
49770         maxSize: 400,
49771         titlebar: true,
49772         collapsible: true,
49773         panels: [new CP("west", {title: "West"})]
49774     },
49775     east: {
49776         split:true,
49777         initialSize: 202,
49778         minSize: 175,
49779         maxSize: 400,
49780         titlebar: true,
49781         collapsible: true,
49782         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49783     },
49784     south: {
49785         split:true,
49786         initialSize: 100,
49787         minSize: 100,
49788         maxSize: 200,
49789         titlebar: true,
49790         collapsible: true,
49791         panels: [new CP("south", {title: "South", closable: true})]
49792     },
49793     center: {
49794         titlebar: true,
49795         autoScroll:true,
49796         resizeTabs: true,
49797         minTabWidth: 50,
49798         preferredTabWidth: 150,
49799         panels: [
49800             new CP("center1", {title: "Close Me", closable: true}),
49801             new CP("center2", {title: "Center Panel", closable: false})
49802         ]
49803     }
49804 }, document.body);
49805
49806 layout.getRegion("center").showPanel("center1");
49807 </code></pre>
49808  * @param config
49809  * @param targetEl
49810  */
49811 Roo.BorderLayout.create = function(config, targetEl){
49812     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49813     layout.beginUpdate();
49814     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49815     for(var j = 0, jlen = regions.length; j < jlen; j++){
49816         var lr = regions[j];
49817         if(layout.regions[lr] && config[lr].panels){
49818             var r = layout.regions[lr];
49819             var ps = config[lr].panels;
49820             layout.addTypedPanels(r, ps);
49821         }
49822     }
49823     layout.endUpdate();
49824     return layout;
49825 };
49826
49827 // private
49828 Roo.BorderLayout.RegionFactory = {
49829     // private
49830     validRegions : ["north","south","east","west","center"],
49831
49832     // private
49833     create : function(target, mgr, config){
49834         target = target.toLowerCase();
49835         if(config.lightweight || config.basic){
49836             return new Roo.BasicLayoutRegion(mgr, config, target);
49837         }
49838         switch(target){
49839             case "north":
49840                 return new Roo.NorthLayoutRegion(mgr, config);
49841             case "south":
49842                 return new Roo.SouthLayoutRegion(mgr, config);
49843             case "east":
49844                 return new Roo.EastLayoutRegion(mgr, config);
49845             case "west":
49846                 return new Roo.WestLayoutRegion(mgr, config);
49847             case "center":
49848                 return new Roo.CenterLayoutRegion(mgr, config);
49849         }
49850         throw 'Layout region "'+target+'" not supported.';
49851     }
49852 };/*
49853  * Based on:
49854  * Ext JS Library 1.1.1
49855  * Copyright(c) 2006-2007, Ext JS, LLC.
49856  *
49857  * Originally Released Under LGPL - original licence link has changed is not relivant.
49858  *
49859  * Fork - LGPL
49860  * <script type="text/javascript">
49861  */
49862  
49863 /**
49864  * @class Roo.BasicLayoutRegion
49865  * @extends Roo.util.Observable
49866  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49867  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49868  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49869  */
49870 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49871     this.mgr = mgr;
49872     this.position  = pos;
49873     this.events = {
49874         /**
49875          * @scope Roo.BasicLayoutRegion
49876          */
49877         
49878         /**
49879          * @event beforeremove
49880          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49881          * @param {Roo.LayoutRegion} this
49882          * @param {Roo.ContentPanel} panel The panel
49883          * @param {Object} e The cancel event object
49884          */
49885         "beforeremove" : true,
49886         /**
49887          * @event invalidated
49888          * Fires when the layout for this region is changed.
49889          * @param {Roo.LayoutRegion} this
49890          */
49891         "invalidated" : true,
49892         /**
49893          * @event visibilitychange
49894          * Fires when this region is shown or hidden 
49895          * @param {Roo.LayoutRegion} this
49896          * @param {Boolean} visibility true or false
49897          */
49898         "visibilitychange" : true,
49899         /**
49900          * @event paneladded
49901          * Fires when a panel is added. 
49902          * @param {Roo.LayoutRegion} this
49903          * @param {Roo.ContentPanel} panel The panel
49904          */
49905         "paneladded" : true,
49906         /**
49907          * @event panelremoved
49908          * Fires when a panel is removed. 
49909          * @param {Roo.LayoutRegion} this
49910          * @param {Roo.ContentPanel} panel The panel
49911          */
49912         "panelremoved" : true,
49913         /**
49914          * @event collapsed
49915          * Fires when this region is collapsed.
49916          * @param {Roo.LayoutRegion} this
49917          */
49918         "collapsed" : true,
49919         /**
49920          * @event expanded
49921          * Fires when this region is expanded.
49922          * @param {Roo.LayoutRegion} this
49923          */
49924         "expanded" : true,
49925         /**
49926          * @event slideshow
49927          * Fires when this region is slid into view.
49928          * @param {Roo.LayoutRegion} this
49929          */
49930         "slideshow" : true,
49931         /**
49932          * @event slidehide
49933          * Fires when this region slides out of view. 
49934          * @param {Roo.LayoutRegion} this
49935          */
49936         "slidehide" : true,
49937         /**
49938          * @event panelactivated
49939          * Fires when a panel is activated. 
49940          * @param {Roo.LayoutRegion} this
49941          * @param {Roo.ContentPanel} panel The activated panel
49942          */
49943         "panelactivated" : true,
49944         /**
49945          * @event resized
49946          * Fires when the user resizes this region. 
49947          * @param {Roo.LayoutRegion} this
49948          * @param {Number} newSize The new size (width for east/west, height for north/south)
49949          */
49950         "resized" : true
49951     };
49952     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49953     this.panels = new Roo.util.MixedCollection();
49954     this.panels.getKey = this.getPanelId.createDelegate(this);
49955     this.box = null;
49956     this.activePanel = null;
49957     // ensure listeners are added...
49958     
49959     if (config.listeners || config.events) {
49960         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49961             listeners : config.listeners || {},
49962             events : config.events || {}
49963         });
49964     }
49965     
49966     if(skipConfig !== true){
49967         this.applyConfig(config);
49968     }
49969 };
49970
49971 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49972     getPanelId : function(p){
49973         return p.getId();
49974     },
49975     
49976     applyConfig : function(config){
49977         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49978         this.config = config;
49979         
49980     },
49981     
49982     /**
49983      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49984      * the width, for horizontal (north, south) the height.
49985      * @param {Number} newSize The new width or height
49986      */
49987     resizeTo : function(newSize){
49988         var el = this.el ? this.el :
49989                  (this.activePanel ? this.activePanel.getEl() : null);
49990         if(el){
49991             switch(this.position){
49992                 case "east":
49993                 case "west":
49994                     el.setWidth(newSize);
49995                     this.fireEvent("resized", this, newSize);
49996                 break;
49997                 case "north":
49998                 case "south":
49999                     el.setHeight(newSize);
50000                     this.fireEvent("resized", this, newSize);
50001                 break;                
50002             }
50003         }
50004     },
50005     
50006     getBox : function(){
50007         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50008     },
50009     
50010     getMargins : function(){
50011         return this.margins;
50012     },
50013     
50014     updateBox : function(box){
50015         this.box = box;
50016         var el = this.activePanel.getEl();
50017         el.dom.style.left = box.x + "px";
50018         el.dom.style.top = box.y + "px";
50019         this.activePanel.setSize(box.width, box.height);
50020     },
50021     
50022     /**
50023      * Returns the container element for this region.
50024      * @return {Roo.Element}
50025      */
50026     getEl : function(){
50027         return this.activePanel;
50028     },
50029     
50030     /**
50031      * Returns true if this region is currently visible.
50032      * @return {Boolean}
50033      */
50034     isVisible : function(){
50035         return this.activePanel ? true : false;
50036     },
50037     
50038     setActivePanel : function(panel){
50039         panel = this.getPanel(panel);
50040         if(this.activePanel && this.activePanel != panel){
50041             this.activePanel.setActiveState(false);
50042             this.activePanel.getEl().setLeftTop(-10000,-10000);
50043         }
50044         this.activePanel = panel;
50045         panel.setActiveState(true);
50046         if(this.box){
50047             panel.setSize(this.box.width, this.box.height);
50048         }
50049         this.fireEvent("panelactivated", this, panel);
50050         this.fireEvent("invalidated");
50051     },
50052     
50053     /**
50054      * Show the specified panel.
50055      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50056      * @return {Roo.ContentPanel} The shown panel or null
50057      */
50058     showPanel : function(panel){
50059         if(panel = this.getPanel(panel)){
50060             this.setActivePanel(panel);
50061         }
50062         return panel;
50063     },
50064     
50065     /**
50066      * Get the active panel for this region.
50067      * @return {Roo.ContentPanel} The active panel or null
50068      */
50069     getActivePanel : function(){
50070         return this.activePanel;
50071     },
50072     
50073     /**
50074      * Add the passed ContentPanel(s)
50075      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50076      * @return {Roo.ContentPanel} The panel added (if only one was added)
50077      */
50078     add : function(panel){
50079         if(arguments.length > 1){
50080             for(var i = 0, len = arguments.length; i < len; i++) {
50081                 this.add(arguments[i]);
50082             }
50083             return null;
50084         }
50085         if(this.hasPanel(panel)){
50086             this.showPanel(panel);
50087             return panel;
50088         }
50089         var el = panel.getEl();
50090         if(el.dom.parentNode != this.mgr.el.dom){
50091             this.mgr.el.dom.appendChild(el.dom);
50092         }
50093         if(panel.setRegion){
50094             panel.setRegion(this);
50095         }
50096         this.panels.add(panel);
50097         el.setStyle("position", "absolute");
50098         if(!panel.background){
50099             this.setActivePanel(panel);
50100             if(this.config.initialSize && this.panels.getCount()==1){
50101                 this.resizeTo(this.config.initialSize);
50102             }
50103         }
50104         this.fireEvent("paneladded", this, panel);
50105         return panel;
50106     },
50107     
50108     /**
50109      * Returns true if the panel is in this region.
50110      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50111      * @return {Boolean}
50112      */
50113     hasPanel : function(panel){
50114         if(typeof panel == "object"){ // must be panel obj
50115             panel = panel.getId();
50116         }
50117         return this.getPanel(panel) ? true : false;
50118     },
50119     
50120     /**
50121      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50122      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50123      * @param {Boolean} preservePanel Overrides the config preservePanel option
50124      * @return {Roo.ContentPanel} The panel that was removed
50125      */
50126     remove : function(panel, preservePanel){
50127         panel = this.getPanel(panel);
50128         if(!panel){
50129             return null;
50130         }
50131         var e = {};
50132         this.fireEvent("beforeremove", this, panel, e);
50133         if(e.cancel === true){
50134             return null;
50135         }
50136         var panelId = panel.getId();
50137         this.panels.removeKey(panelId);
50138         return panel;
50139     },
50140     
50141     /**
50142      * Returns the panel specified or null if it's not in this region.
50143      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50144      * @return {Roo.ContentPanel}
50145      */
50146     getPanel : function(id){
50147         if(typeof id == "object"){ // must be panel obj
50148             return id;
50149         }
50150         return this.panels.get(id);
50151     },
50152     
50153     /**
50154      * Returns this regions position (north/south/east/west/center).
50155      * @return {String} 
50156      */
50157     getPosition: function(){
50158         return this.position;    
50159     }
50160 });/*
50161  * Based on:
50162  * Ext JS Library 1.1.1
50163  * Copyright(c) 2006-2007, Ext JS, LLC.
50164  *
50165  * Originally Released Under LGPL - original licence link has changed is not relivant.
50166  *
50167  * Fork - LGPL
50168  * <script type="text/javascript">
50169  */
50170  
50171 /**
50172  * @class Roo.LayoutRegion
50173  * @extends Roo.BasicLayoutRegion
50174  * This class represents a region in a layout manager.
50175  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50176  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50177  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50178  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50179  * @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})
50180  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50181  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50182  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50183  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50184  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50185  * @cfg {String}    title           The title for the region (overrides panel titles)
50186  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50187  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50188  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50189  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50190  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50191  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50192  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50193  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50194  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50195  * @cfg {Boolean}   showPin         True to show a pin button
50196  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50197  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50198  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50199  * @cfg {Number}    width           For East/West panels
50200  * @cfg {Number}    height          For North/South panels
50201  * @cfg {Boolean}   split           To show the splitter
50202  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50203  */
50204 Roo.LayoutRegion = function(mgr, config, pos){
50205     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50206     var dh = Roo.DomHelper;
50207     /** This region's container element 
50208     * @type Roo.Element */
50209     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50210     /** This region's title element 
50211     * @type Roo.Element */
50212
50213     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50214         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50215         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50216     ]}, true);
50217     this.titleEl.enableDisplayMode();
50218     /** This region's title text element 
50219     * @type HTMLElement */
50220     this.titleTextEl = this.titleEl.dom.firstChild;
50221     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50222     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50223     this.closeBtn.enableDisplayMode();
50224     this.closeBtn.on("click", this.closeClicked, this);
50225     this.closeBtn.hide();
50226
50227     this.createBody(config);
50228     this.visible = true;
50229     this.collapsed = false;
50230
50231     if(config.hideWhenEmpty){
50232         this.hide();
50233         this.on("paneladded", this.validateVisibility, this);
50234         this.on("panelremoved", this.validateVisibility, this);
50235     }
50236     this.applyConfig(config);
50237 };
50238
50239 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50240
50241     createBody : function(){
50242         /** This region's body element 
50243         * @type Roo.Element */
50244         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50245     },
50246
50247     applyConfig : function(c){
50248         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50249             var dh = Roo.DomHelper;
50250             if(c.titlebar !== false){
50251                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50252                 this.collapseBtn.on("click", this.collapse, this);
50253                 this.collapseBtn.enableDisplayMode();
50254
50255                 if(c.showPin === true || this.showPin){
50256                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50257                     this.stickBtn.enableDisplayMode();
50258                     this.stickBtn.on("click", this.expand, this);
50259                     this.stickBtn.hide();
50260                 }
50261             }
50262             /** This region's collapsed element
50263             * @type Roo.Element */
50264             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50265                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50266             ]}, true);
50267             if(c.floatable !== false){
50268                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50269                this.collapsedEl.on("click", this.collapseClick, this);
50270             }
50271
50272             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50273                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50274                    id: "message", unselectable: "on", style:{"float":"left"}});
50275                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50276              }
50277             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50278             this.expandBtn.on("click", this.expand, this);
50279         }
50280         if(this.collapseBtn){
50281             this.collapseBtn.setVisible(c.collapsible == true);
50282         }
50283         this.cmargins = c.cmargins || this.cmargins ||
50284                          (this.position == "west" || this.position == "east" ?
50285                              {top: 0, left: 2, right:2, bottom: 0} :
50286                              {top: 2, left: 0, right:0, bottom: 2});
50287         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50288         this.bottomTabs = c.tabPosition != "top";
50289         this.autoScroll = c.autoScroll || false;
50290         if(this.autoScroll){
50291             this.bodyEl.setStyle("overflow", "auto");
50292         }else{
50293             this.bodyEl.setStyle("overflow", "hidden");
50294         }
50295         //if(c.titlebar !== false){
50296             if((!c.titlebar && !c.title) || c.titlebar === false){
50297                 this.titleEl.hide();
50298             }else{
50299                 this.titleEl.show();
50300                 if(c.title){
50301                     this.titleTextEl.innerHTML = c.title;
50302                 }
50303             }
50304         //}
50305         this.duration = c.duration || .30;
50306         this.slideDuration = c.slideDuration || .45;
50307         this.config = c;
50308         if(c.collapsed){
50309             this.collapse(true);
50310         }
50311         if(c.hidden){
50312             this.hide();
50313         }
50314     },
50315     /**
50316      * Returns true if this region is currently visible.
50317      * @return {Boolean}
50318      */
50319     isVisible : function(){
50320         return this.visible;
50321     },
50322
50323     /**
50324      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50325      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50326      */
50327     setCollapsedTitle : function(title){
50328         title = title || "&#160;";
50329         if(this.collapsedTitleTextEl){
50330             this.collapsedTitleTextEl.innerHTML = title;
50331         }
50332     },
50333
50334     getBox : function(){
50335         var b;
50336         if(!this.collapsed){
50337             b = this.el.getBox(false, true);
50338         }else{
50339             b = this.collapsedEl.getBox(false, true);
50340         }
50341         return b;
50342     },
50343
50344     getMargins : function(){
50345         return this.collapsed ? this.cmargins : this.margins;
50346     },
50347
50348     highlight : function(){
50349         this.el.addClass("x-layout-panel-dragover");
50350     },
50351
50352     unhighlight : function(){
50353         this.el.removeClass("x-layout-panel-dragover");
50354     },
50355
50356     updateBox : function(box){
50357         this.box = box;
50358         if(!this.collapsed){
50359             this.el.dom.style.left = box.x + "px";
50360             this.el.dom.style.top = box.y + "px";
50361             this.updateBody(box.width, box.height);
50362         }else{
50363             this.collapsedEl.dom.style.left = box.x + "px";
50364             this.collapsedEl.dom.style.top = box.y + "px";
50365             this.collapsedEl.setSize(box.width, box.height);
50366         }
50367         if(this.tabs){
50368             this.tabs.autoSizeTabs();
50369         }
50370     },
50371
50372     updateBody : function(w, h){
50373         if(w !== null){
50374             this.el.setWidth(w);
50375             w -= this.el.getBorderWidth("rl");
50376             if(this.config.adjustments){
50377                 w += this.config.adjustments[0];
50378             }
50379         }
50380         if(h !== null){
50381             this.el.setHeight(h);
50382             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50383             h -= this.el.getBorderWidth("tb");
50384             if(this.config.adjustments){
50385                 h += this.config.adjustments[1];
50386             }
50387             this.bodyEl.setHeight(h);
50388             if(this.tabs){
50389                 h = this.tabs.syncHeight(h);
50390             }
50391         }
50392         if(this.panelSize){
50393             w = w !== null ? w : this.panelSize.width;
50394             h = h !== null ? h : this.panelSize.height;
50395         }
50396         if(this.activePanel){
50397             var el = this.activePanel.getEl();
50398             w = w !== null ? w : el.getWidth();
50399             h = h !== null ? h : el.getHeight();
50400             this.panelSize = {width: w, height: h};
50401             this.activePanel.setSize(w, h);
50402         }
50403         if(Roo.isIE && this.tabs){
50404             this.tabs.el.repaint();
50405         }
50406     },
50407
50408     /**
50409      * Returns the container element for this region.
50410      * @return {Roo.Element}
50411      */
50412     getEl : function(){
50413         return this.el;
50414     },
50415
50416     /**
50417      * Hides this region.
50418      */
50419     hide : function(){
50420         if(!this.collapsed){
50421             this.el.dom.style.left = "-2000px";
50422             this.el.hide();
50423         }else{
50424             this.collapsedEl.dom.style.left = "-2000px";
50425             this.collapsedEl.hide();
50426         }
50427         this.visible = false;
50428         this.fireEvent("visibilitychange", this, false);
50429     },
50430
50431     /**
50432      * Shows this region if it was previously hidden.
50433      */
50434     show : function(){
50435         if(!this.collapsed){
50436             this.el.show();
50437         }else{
50438             this.collapsedEl.show();
50439         }
50440         this.visible = true;
50441         this.fireEvent("visibilitychange", this, true);
50442     },
50443
50444     closeClicked : function(){
50445         if(this.activePanel){
50446             this.remove(this.activePanel);
50447         }
50448     },
50449
50450     collapseClick : function(e){
50451         if(this.isSlid){
50452            e.stopPropagation();
50453            this.slideIn();
50454         }else{
50455            e.stopPropagation();
50456            this.slideOut();
50457         }
50458     },
50459
50460     /**
50461      * Collapses this region.
50462      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50463      */
50464     collapse : function(skipAnim){
50465         if(this.collapsed) return;
50466         this.collapsed = true;
50467         if(this.split){
50468             this.split.el.hide();
50469         }
50470         if(this.config.animate && skipAnim !== true){
50471             this.fireEvent("invalidated", this);
50472             this.animateCollapse();
50473         }else{
50474             this.el.setLocation(-20000,-20000);
50475             this.el.hide();
50476             this.collapsedEl.show();
50477             this.fireEvent("collapsed", this);
50478             this.fireEvent("invalidated", this);
50479         }
50480     },
50481
50482     animateCollapse : function(){
50483         // overridden
50484     },
50485
50486     /**
50487      * Expands this region if it was previously collapsed.
50488      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50489      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50490      */
50491     expand : function(e, skipAnim){
50492         if(e) e.stopPropagation();
50493         if(!this.collapsed || this.el.hasActiveFx()) return;
50494         if(this.isSlid){
50495             this.afterSlideIn();
50496             skipAnim = true;
50497         }
50498         this.collapsed = false;
50499         if(this.config.animate && skipAnim !== true){
50500             this.animateExpand();
50501         }else{
50502             this.el.show();
50503             if(this.split){
50504                 this.split.el.show();
50505             }
50506             this.collapsedEl.setLocation(-2000,-2000);
50507             this.collapsedEl.hide();
50508             this.fireEvent("invalidated", this);
50509             this.fireEvent("expanded", this);
50510         }
50511     },
50512
50513     animateExpand : function(){
50514         // overridden
50515     },
50516
50517     initTabs : function()
50518     {
50519         this.bodyEl.setStyle("overflow", "hidden");
50520         var ts = new Roo.TabPanel(
50521                 this.bodyEl.dom,
50522                 {
50523                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50524                     disableTooltips: this.config.disableTabTips,
50525                     toolbar : this.config.toolbar
50526                 }
50527         );
50528         if(this.config.hideTabs){
50529             ts.stripWrap.setDisplayed(false);
50530         }
50531         this.tabs = ts;
50532         ts.resizeTabs = this.config.resizeTabs === true;
50533         ts.minTabWidth = this.config.minTabWidth || 40;
50534         ts.maxTabWidth = this.config.maxTabWidth || 250;
50535         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50536         ts.monitorResize = false;
50537         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50538         ts.bodyEl.addClass('x-layout-tabs-body');
50539         this.panels.each(this.initPanelAsTab, this);
50540     },
50541
50542     initPanelAsTab : function(panel){
50543         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50544                     this.config.closeOnTab && panel.isClosable());
50545         if(panel.tabTip !== undefined){
50546             ti.setTooltip(panel.tabTip);
50547         }
50548         ti.on("activate", function(){
50549               this.setActivePanel(panel);
50550         }, this);
50551         if(this.config.closeOnTab){
50552             ti.on("beforeclose", function(t, e){
50553                 e.cancel = true;
50554                 this.remove(panel);
50555             }, this);
50556         }
50557         return ti;
50558     },
50559
50560     updatePanelTitle : function(panel, title){
50561         if(this.activePanel == panel){
50562             this.updateTitle(title);
50563         }
50564         if(this.tabs){
50565             var ti = this.tabs.getTab(panel.getEl().id);
50566             ti.setText(title);
50567             if(panel.tabTip !== undefined){
50568                 ti.setTooltip(panel.tabTip);
50569             }
50570         }
50571     },
50572
50573     updateTitle : function(title){
50574         if(this.titleTextEl && !this.config.title){
50575             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50576         }
50577     },
50578
50579     setActivePanel : function(panel){
50580         panel = this.getPanel(panel);
50581         if(this.activePanel && this.activePanel != panel){
50582             this.activePanel.setActiveState(false);
50583         }
50584         this.activePanel = panel;
50585         panel.setActiveState(true);
50586         if(this.panelSize){
50587             panel.setSize(this.panelSize.width, this.panelSize.height);
50588         }
50589         if(this.closeBtn){
50590             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50591         }
50592         this.updateTitle(panel.getTitle());
50593         if(this.tabs){
50594             this.fireEvent("invalidated", this);
50595         }
50596         this.fireEvent("panelactivated", this, panel);
50597     },
50598
50599     /**
50600      * Shows the specified panel.
50601      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50602      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50603      */
50604     showPanel : function(panel)
50605     {
50606         panel = this.getPanel(panel);
50607         if(panel){
50608             if(this.tabs){
50609                 var tab = this.tabs.getTab(panel.getEl().id);
50610                 if(tab.isHidden()){
50611                     this.tabs.unhideTab(tab.id);
50612                 }
50613                 tab.activate();
50614             }else{
50615                 this.setActivePanel(panel);
50616             }
50617         }
50618         return panel;
50619     },
50620
50621     /**
50622      * Get the active panel for this region.
50623      * @return {Roo.ContentPanel} The active panel or null
50624      */
50625     getActivePanel : function(){
50626         return this.activePanel;
50627     },
50628
50629     validateVisibility : function(){
50630         if(this.panels.getCount() < 1){
50631             this.updateTitle("&#160;");
50632             this.closeBtn.hide();
50633             this.hide();
50634         }else{
50635             if(!this.isVisible()){
50636                 this.show();
50637             }
50638         }
50639     },
50640
50641     /**
50642      * Adds the passed ContentPanel(s) to this region.
50643      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50644      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50645      */
50646     add : function(panel){
50647         if(arguments.length > 1){
50648             for(var i = 0, len = arguments.length; i < len; i++) {
50649                 this.add(arguments[i]);
50650             }
50651             return null;
50652         }
50653         if(this.hasPanel(panel)){
50654             this.showPanel(panel);
50655             return panel;
50656         }
50657         panel.setRegion(this);
50658         this.panels.add(panel);
50659         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50660             this.bodyEl.dom.appendChild(panel.getEl().dom);
50661             if(panel.background !== true){
50662                 this.setActivePanel(panel);
50663             }
50664             this.fireEvent("paneladded", this, panel);
50665             return panel;
50666         }
50667         if(!this.tabs){
50668             this.initTabs();
50669         }else{
50670             this.initPanelAsTab(panel);
50671         }
50672         if(panel.background !== true){
50673             this.tabs.activate(panel.getEl().id);
50674         }
50675         this.fireEvent("paneladded", this, panel);
50676         return panel;
50677     },
50678
50679     /**
50680      * Hides the tab for the specified panel.
50681      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50682      */
50683     hidePanel : function(panel){
50684         if(this.tabs && (panel = this.getPanel(panel))){
50685             this.tabs.hideTab(panel.getEl().id);
50686         }
50687     },
50688
50689     /**
50690      * Unhides the tab for a previously hidden panel.
50691      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50692      */
50693     unhidePanel : function(panel){
50694         if(this.tabs && (panel = this.getPanel(panel))){
50695             this.tabs.unhideTab(panel.getEl().id);
50696         }
50697     },
50698
50699     clearPanels : function(){
50700         while(this.panels.getCount() > 0){
50701              this.remove(this.panels.first());
50702         }
50703     },
50704
50705     /**
50706      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50707      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50708      * @param {Boolean} preservePanel Overrides the config preservePanel option
50709      * @return {Roo.ContentPanel} The panel that was removed
50710      */
50711     remove : function(panel, preservePanel){
50712         panel = this.getPanel(panel);
50713         if(!panel){
50714             return null;
50715         }
50716         var e = {};
50717         this.fireEvent("beforeremove", this, panel, e);
50718         if(e.cancel === true){
50719             return null;
50720         }
50721         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50722         var panelId = panel.getId();
50723         this.panels.removeKey(panelId);
50724         if(preservePanel){
50725             document.body.appendChild(panel.getEl().dom);
50726         }
50727         if(this.tabs){
50728             this.tabs.removeTab(panel.getEl().id);
50729         }else if (!preservePanel){
50730             this.bodyEl.dom.removeChild(panel.getEl().dom);
50731         }
50732         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50733             var p = this.panels.first();
50734             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50735             tempEl.appendChild(p.getEl().dom);
50736             this.bodyEl.update("");
50737             this.bodyEl.dom.appendChild(p.getEl().dom);
50738             tempEl = null;
50739             this.updateTitle(p.getTitle());
50740             this.tabs = null;
50741             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50742             this.setActivePanel(p);
50743         }
50744         panel.setRegion(null);
50745         if(this.activePanel == panel){
50746             this.activePanel = null;
50747         }
50748         if(this.config.autoDestroy !== false && preservePanel !== true){
50749             try{panel.destroy();}catch(e){}
50750         }
50751         this.fireEvent("panelremoved", this, panel);
50752         return panel;
50753     },
50754
50755     /**
50756      * Returns the TabPanel component used by this region
50757      * @return {Roo.TabPanel}
50758      */
50759     getTabs : function(){
50760         return this.tabs;
50761     },
50762
50763     createTool : function(parentEl, className){
50764         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50765             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50766         btn.addClassOnOver("x-layout-tools-button-over");
50767         return btn;
50768     }
50769 });/*
50770  * Based on:
50771  * Ext JS Library 1.1.1
50772  * Copyright(c) 2006-2007, Ext JS, LLC.
50773  *
50774  * Originally Released Under LGPL - original licence link has changed is not relivant.
50775  *
50776  * Fork - LGPL
50777  * <script type="text/javascript">
50778  */
50779  
50780
50781
50782 /**
50783  * @class Roo.SplitLayoutRegion
50784  * @extends Roo.LayoutRegion
50785  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50786  */
50787 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50788     this.cursor = cursor;
50789     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50790 };
50791
50792 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50793     splitTip : "Drag to resize.",
50794     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50795     useSplitTips : false,
50796
50797     applyConfig : function(config){
50798         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50799         if(config.split){
50800             if(!this.split){
50801                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50802                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50803                 /** The SplitBar for this region 
50804                 * @type Roo.SplitBar */
50805                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50806                 this.split.on("moved", this.onSplitMove, this);
50807                 this.split.useShim = config.useShim === true;
50808                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50809                 if(this.useSplitTips){
50810                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50811                 }
50812                 if(config.collapsible){
50813                     this.split.el.on("dblclick", this.collapse,  this);
50814                 }
50815             }
50816             if(typeof config.minSize != "undefined"){
50817                 this.split.minSize = config.minSize;
50818             }
50819             if(typeof config.maxSize != "undefined"){
50820                 this.split.maxSize = config.maxSize;
50821             }
50822             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50823                 this.hideSplitter();
50824             }
50825         }
50826     },
50827
50828     getHMaxSize : function(){
50829          var cmax = this.config.maxSize || 10000;
50830          var center = this.mgr.getRegion("center");
50831          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50832     },
50833
50834     getVMaxSize : function(){
50835          var cmax = this.config.maxSize || 10000;
50836          var center = this.mgr.getRegion("center");
50837          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50838     },
50839
50840     onSplitMove : function(split, newSize){
50841         this.fireEvent("resized", this, newSize);
50842     },
50843     
50844     /** 
50845      * Returns the {@link Roo.SplitBar} for this region.
50846      * @return {Roo.SplitBar}
50847      */
50848     getSplitBar : function(){
50849         return this.split;
50850     },
50851     
50852     hide : function(){
50853         this.hideSplitter();
50854         Roo.SplitLayoutRegion.superclass.hide.call(this);
50855     },
50856
50857     hideSplitter : function(){
50858         if(this.split){
50859             this.split.el.setLocation(-2000,-2000);
50860             this.split.el.hide();
50861         }
50862     },
50863
50864     show : function(){
50865         if(this.split){
50866             this.split.el.show();
50867         }
50868         Roo.SplitLayoutRegion.superclass.show.call(this);
50869     },
50870     
50871     beforeSlide: function(){
50872         if(Roo.isGecko){// firefox overflow auto bug workaround
50873             this.bodyEl.clip();
50874             if(this.tabs) this.tabs.bodyEl.clip();
50875             if(this.activePanel){
50876                 this.activePanel.getEl().clip();
50877                 
50878                 if(this.activePanel.beforeSlide){
50879                     this.activePanel.beforeSlide();
50880                 }
50881             }
50882         }
50883     },
50884     
50885     afterSlide : function(){
50886         if(Roo.isGecko){// firefox overflow auto bug workaround
50887             this.bodyEl.unclip();
50888             if(this.tabs) this.tabs.bodyEl.unclip();
50889             if(this.activePanel){
50890                 this.activePanel.getEl().unclip();
50891                 if(this.activePanel.afterSlide){
50892                     this.activePanel.afterSlide();
50893                 }
50894             }
50895         }
50896     },
50897
50898     initAutoHide : function(){
50899         if(this.autoHide !== false){
50900             if(!this.autoHideHd){
50901                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50902                 this.autoHideHd = {
50903                     "mouseout": function(e){
50904                         if(!e.within(this.el, true)){
50905                             st.delay(500);
50906                         }
50907                     },
50908                     "mouseover" : function(e){
50909                         st.cancel();
50910                     },
50911                     scope : this
50912                 };
50913             }
50914             this.el.on(this.autoHideHd);
50915         }
50916     },
50917
50918     clearAutoHide : function(){
50919         if(this.autoHide !== false){
50920             this.el.un("mouseout", this.autoHideHd.mouseout);
50921             this.el.un("mouseover", this.autoHideHd.mouseover);
50922         }
50923     },
50924
50925     clearMonitor : function(){
50926         Roo.get(document).un("click", this.slideInIf, this);
50927     },
50928
50929     // these names are backwards but not changed for compat
50930     slideOut : function(){
50931         if(this.isSlid || this.el.hasActiveFx()){
50932             return;
50933         }
50934         this.isSlid = true;
50935         if(this.collapseBtn){
50936             this.collapseBtn.hide();
50937         }
50938         this.closeBtnState = this.closeBtn.getStyle('display');
50939         this.closeBtn.hide();
50940         if(this.stickBtn){
50941             this.stickBtn.show();
50942         }
50943         this.el.show();
50944         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50945         this.beforeSlide();
50946         this.el.setStyle("z-index", 10001);
50947         this.el.slideIn(this.getSlideAnchor(), {
50948             callback: function(){
50949                 this.afterSlide();
50950                 this.initAutoHide();
50951                 Roo.get(document).on("click", this.slideInIf, this);
50952                 this.fireEvent("slideshow", this);
50953             },
50954             scope: this,
50955             block: true
50956         });
50957     },
50958
50959     afterSlideIn : function(){
50960         this.clearAutoHide();
50961         this.isSlid = false;
50962         this.clearMonitor();
50963         this.el.setStyle("z-index", "");
50964         if(this.collapseBtn){
50965             this.collapseBtn.show();
50966         }
50967         this.closeBtn.setStyle('display', this.closeBtnState);
50968         if(this.stickBtn){
50969             this.stickBtn.hide();
50970         }
50971         this.fireEvent("slidehide", this);
50972     },
50973
50974     slideIn : function(cb){
50975         if(!this.isSlid || this.el.hasActiveFx()){
50976             Roo.callback(cb);
50977             return;
50978         }
50979         this.isSlid = false;
50980         this.beforeSlide();
50981         this.el.slideOut(this.getSlideAnchor(), {
50982             callback: function(){
50983                 this.el.setLeftTop(-10000, -10000);
50984                 this.afterSlide();
50985                 this.afterSlideIn();
50986                 Roo.callback(cb);
50987             },
50988             scope: this,
50989             block: true
50990         });
50991     },
50992     
50993     slideInIf : function(e){
50994         if(!e.within(this.el)){
50995             this.slideIn();
50996         }
50997     },
50998
50999     animateCollapse : function(){
51000         this.beforeSlide();
51001         this.el.setStyle("z-index", 20000);
51002         var anchor = this.getSlideAnchor();
51003         this.el.slideOut(anchor, {
51004             callback : function(){
51005                 this.el.setStyle("z-index", "");
51006                 this.collapsedEl.slideIn(anchor, {duration:.3});
51007                 this.afterSlide();
51008                 this.el.setLocation(-10000,-10000);
51009                 this.el.hide();
51010                 this.fireEvent("collapsed", this);
51011             },
51012             scope: this,
51013             block: true
51014         });
51015     },
51016
51017     animateExpand : function(){
51018         this.beforeSlide();
51019         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51020         this.el.setStyle("z-index", 20000);
51021         this.collapsedEl.hide({
51022             duration:.1
51023         });
51024         this.el.slideIn(this.getSlideAnchor(), {
51025             callback : function(){
51026                 this.el.setStyle("z-index", "");
51027                 this.afterSlide();
51028                 if(this.split){
51029                     this.split.el.show();
51030                 }
51031                 this.fireEvent("invalidated", this);
51032                 this.fireEvent("expanded", this);
51033             },
51034             scope: this,
51035             block: true
51036         });
51037     },
51038
51039     anchors : {
51040         "west" : "left",
51041         "east" : "right",
51042         "north" : "top",
51043         "south" : "bottom"
51044     },
51045
51046     sanchors : {
51047         "west" : "l",
51048         "east" : "r",
51049         "north" : "t",
51050         "south" : "b"
51051     },
51052
51053     canchors : {
51054         "west" : "tl-tr",
51055         "east" : "tr-tl",
51056         "north" : "tl-bl",
51057         "south" : "bl-tl"
51058     },
51059
51060     getAnchor : function(){
51061         return this.anchors[this.position];
51062     },
51063
51064     getCollapseAnchor : function(){
51065         return this.canchors[this.position];
51066     },
51067
51068     getSlideAnchor : function(){
51069         return this.sanchors[this.position];
51070     },
51071
51072     getAlignAdj : function(){
51073         var cm = this.cmargins;
51074         switch(this.position){
51075             case "west":
51076                 return [0, 0];
51077             break;
51078             case "east":
51079                 return [0, 0];
51080             break;
51081             case "north":
51082                 return [0, 0];
51083             break;
51084             case "south":
51085                 return [0, 0];
51086             break;
51087         }
51088     },
51089
51090     getExpandAdj : function(){
51091         var c = this.collapsedEl, cm = this.cmargins;
51092         switch(this.position){
51093             case "west":
51094                 return [-(cm.right+c.getWidth()+cm.left), 0];
51095             break;
51096             case "east":
51097                 return [cm.right+c.getWidth()+cm.left, 0];
51098             break;
51099             case "north":
51100                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51101             break;
51102             case "south":
51103                 return [0, cm.top+cm.bottom+c.getHeight()];
51104             break;
51105         }
51106     }
51107 });/*
51108  * Based on:
51109  * Ext JS Library 1.1.1
51110  * Copyright(c) 2006-2007, Ext JS, LLC.
51111  *
51112  * Originally Released Under LGPL - original licence link has changed is not relivant.
51113  *
51114  * Fork - LGPL
51115  * <script type="text/javascript">
51116  */
51117 /*
51118  * These classes are private internal classes
51119  */
51120 Roo.CenterLayoutRegion = function(mgr, config){
51121     Roo.LayoutRegion.call(this, mgr, config, "center");
51122     this.visible = true;
51123     this.minWidth = config.minWidth || 20;
51124     this.minHeight = config.minHeight || 20;
51125 };
51126
51127 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51128     hide : function(){
51129         // center panel can't be hidden
51130     },
51131     
51132     show : function(){
51133         // center panel can't be hidden
51134     },
51135     
51136     getMinWidth: function(){
51137         return this.minWidth;
51138     },
51139     
51140     getMinHeight: function(){
51141         return this.minHeight;
51142     }
51143 });
51144
51145
51146 Roo.NorthLayoutRegion = function(mgr, config){
51147     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51148     if(this.split){
51149         this.split.placement = Roo.SplitBar.TOP;
51150         this.split.orientation = Roo.SplitBar.VERTICAL;
51151         this.split.el.addClass("x-layout-split-v");
51152     }
51153     var size = config.initialSize || config.height;
51154     if(typeof size != "undefined"){
51155         this.el.setHeight(size);
51156     }
51157 };
51158 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51159     orientation: Roo.SplitBar.VERTICAL,
51160     getBox : function(){
51161         if(this.collapsed){
51162             return this.collapsedEl.getBox();
51163         }
51164         var box = this.el.getBox();
51165         if(this.split){
51166             box.height += this.split.el.getHeight();
51167         }
51168         return box;
51169     },
51170     
51171     updateBox : function(box){
51172         if(this.split && !this.collapsed){
51173             box.height -= this.split.el.getHeight();
51174             this.split.el.setLeft(box.x);
51175             this.split.el.setTop(box.y+box.height);
51176             this.split.el.setWidth(box.width);
51177         }
51178         if(this.collapsed){
51179             this.updateBody(box.width, null);
51180         }
51181         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51182     }
51183 });
51184
51185 Roo.SouthLayoutRegion = function(mgr, config){
51186     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51187     if(this.split){
51188         this.split.placement = Roo.SplitBar.BOTTOM;
51189         this.split.orientation = Roo.SplitBar.VERTICAL;
51190         this.split.el.addClass("x-layout-split-v");
51191     }
51192     var size = config.initialSize || config.height;
51193     if(typeof size != "undefined"){
51194         this.el.setHeight(size);
51195     }
51196 };
51197 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51198     orientation: Roo.SplitBar.VERTICAL,
51199     getBox : function(){
51200         if(this.collapsed){
51201             return this.collapsedEl.getBox();
51202         }
51203         var box = this.el.getBox();
51204         if(this.split){
51205             var sh = this.split.el.getHeight();
51206             box.height += sh;
51207             box.y -= sh;
51208         }
51209         return box;
51210     },
51211     
51212     updateBox : function(box){
51213         if(this.split && !this.collapsed){
51214             var sh = this.split.el.getHeight();
51215             box.height -= sh;
51216             box.y += sh;
51217             this.split.el.setLeft(box.x);
51218             this.split.el.setTop(box.y-sh);
51219             this.split.el.setWidth(box.width);
51220         }
51221         if(this.collapsed){
51222             this.updateBody(box.width, null);
51223         }
51224         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51225     }
51226 });
51227
51228 Roo.EastLayoutRegion = function(mgr, config){
51229     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51230     if(this.split){
51231         this.split.placement = Roo.SplitBar.RIGHT;
51232         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51233         this.split.el.addClass("x-layout-split-h");
51234     }
51235     var size = config.initialSize || config.width;
51236     if(typeof size != "undefined"){
51237         this.el.setWidth(size);
51238     }
51239 };
51240 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51241     orientation: Roo.SplitBar.HORIZONTAL,
51242     getBox : function(){
51243         if(this.collapsed){
51244             return this.collapsedEl.getBox();
51245         }
51246         var box = this.el.getBox();
51247         if(this.split){
51248             var sw = this.split.el.getWidth();
51249             box.width += sw;
51250             box.x -= sw;
51251         }
51252         return box;
51253     },
51254
51255     updateBox : function(box){
51256         if(this.split && !this.collapsed){
51257             var sw = this.split.el.getWidth();
51258             box.width -= sw;
51259             this.split.el.setLeft(box.x);
51260             this.split.el.setTop(box.y);
51261             this.split.el.setHeight(box.height);
51262             box.x += sw;
51263         }
51264         if(this.collapsed){
51265             this.updateBody(null, box.height);
51266         }
51267         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51268     }
51269 });
51270
51271 Roo.WestLayoutRegion = function(mgr, config){
51272     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51273     if(this.split){
51274         this.split.placement = Roo.SplitBar.LEFT;
51275         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51276         this.split.el.addClass("x-layout-split-h");
51277     }
51278     var size = config.initialSize || config.width;
51279     if(typeof size != "undefined"){
51280         this.el.setWidth(size);
51281     }
51282 };
51283 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51284     orientation: Roo.SplitBar.HORIZONTAL,
51285     getBox : function(){
51286         if(this.collapsed){
51287             return this.collapsedEl.getBox();
51288         }
51289         var box = this.el.getBox();
51290         if(this.split){
51291             box.width += this.split.el.getWidth();
51292         }
51293         return box;
51294     },
51295     
51296     updateBox : function(box){
51297         if(this.split && !this.collapsed){
51298             var sw = this.split.el.getWidth();
51299             box.width -= sw;
51300             this.split.el.setLeft(box.x+box.width);
51301             this.split.el.setTop(box.y);
51302             this.split.el.setHeight(box.height);
51303         }
51304         if(this.collapsed){
51305             this.updateBody(null, box.height);
51306         }
51307         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51308     }
51309 });
51310 /*
51311  * Based on:
51312  * Ext JS Library 1.1.1
51313  * Copyright(c) 2006-2007, Ext JS, LLC.
51314  *
51315  * Originally Released Under LGPL - original licence link has changed is not relivant.
51316  *
51317  * Fork - LGPL
51318  * <script type="text/javascript">
51319  */
51320  
51321  
51322 /*
51323  * Private internal class for reading and applying state
51324  */
51325 Roo.LayoutStateManager = function(layout){
51326      // default empty state
51327      this.state = {
51328         north: {},
51329         south: {},
51330         east: {},
51331         west: {}       
51332     };
51333 };
51334
51335 Roo.LayoutStateManager.prototype = {
51336     init : function(layout, provider){
51337         this.provider = provider;
51338         var state = provider.get(layout.id+"-layout-state");
51339         if(state){
51340             var wasUpdating = layout.isUpdating();
51341             if(!wasUpdating){
51342                 layout.beginUpdate();
51343             }
51344             for(var key in state){
51345                 if(typeof state[key] != "function"){
51346                     var rstate = state[key];
51347                     var r = layout.getRegion(key);
51348                     if(r && rstate){
51349                         if(rstate.size){
51350                             r.resizeTo(rstate.size);
51351                         }
51352                         if(rstate.collapsed == true){
51353                             r.collapse(true);
51354                         }else{
51355                             r.expand(null, true);
51356                         }
51357                     }
51358                 }
51359             }
51360             if(!wasUpdating){
51361                 layout.endUpdate();
51362             }
51363             this.state = state; 
51364         }
51365         this.layout = layout;
51366         layout.on("regionresized", this.onRegionResized, this);
51367         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51368         layout.on("regionexpanded", this.onRegionExpanded, this);
51369     },
51370     
51371     storeState : function(){
51372         this.provider.set(this.layout.id+"-layout-state", this.state);
51373     },
51374     
51375     onRegionResized : function(region, newSize){
51376         this.state[region.getPosition()].size = newSize;
51377         this.storeState();
51378     },
51379     
51380     onRegionCollapsed : function(region){
51381         this.state[region.getPosition()].collapsed = true;
51382         this.storeState();
51383     },
51384     
51385     onRegionExpanded : function(region){
51386         this.state[region.getPosition()].collapsed = false;
51387         this.storeState();
51388     }
51389 };/*
51390  * Based on:
51391  * Ext JS Library 1.1.1
51392  * Copyright(c) 2006-2007, Ext JS, LLC.
51393  *
51394  * Originally Released Under LGPL - original licence link has changed is not relivant.
51395  *
51396  * Fork - LGPL
51397  * <script type="text/javascript">
51398  */
51399 /**
51400  * @class Roo.ContentPanel
51401  * @extends Roo.util.Observable
51402  * A basic ContentPanel element.
51403  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51404  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51405  * @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
51406  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51407  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51408  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51409  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51410  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51411  * @cfg {String} title          The title for this panel
51412  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51413  * @cfg {String} url            Calls {@link #setUrl} with this value
51414  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51415  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51416  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51417  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51418
51419  * @constructor
51420  * Create a new ContentPanel.
51421  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51422  * @param {String/Object} config A string to set only the title or a config object
51423  * @param {String} content (optional) Set the HTML content for this panel
51424  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51425  */
51426 Roo.ContentPanel = function(el, config, content){
51427     
51428      
51429     /*
51430     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51431         config = el;
51432         el = Roo.id();
51433     }
51434     if (config && config.parentLayout) { 
51435         el = config.parentLayout.el.createChild(); 
51436     }
51437     */
51438     if(el.autoCreate){ // xtype is available if this is called from factory
51439         config = el;
51440         el = Roo.id();
51441     }
51442     this.el = Roo.get(el);
51443     if(!this.el && config && config.autoCreate){
51444         if(typeof config.autoCreate == "object"){
51445             if(!config.autoCreate.id){
51446                 config.autoCreate.id = config.id||el;
51447             }
51448             this.el = Roo.DomHelper.append(document.body,
51449                         config.autoCreate, true);
51450         }else{
51451             this.el = Roo.DomHelper.append(document.body,
51452                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51453         }
51454     }
51455     this.closable = false;
51456     this.loaded = false;
51457     this.active = false;
51458     if(typeof config == "string"){
51459         this.title = config;
51460     }else{
51461         Roo.apply(this, config);
51462     }
51463     
51464     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51465         this.wrapEl = this.el.wrap();
51466         this.toolbar.container = this.el.insertSibling(false, 'before');
51467         this.toolbar = new Roo.Toolbar(this.toolbar);
51468     }
51469     
51470     // xtype created footer. - not sure if will work as we normally have to render first..
51471     if (this.footer && !this.footer.el && this.footer.xtype) {
51472         if (!this.wrapEl) {
51473             this.wrapEl = this.el.wrap();
51474         }
51475     
51476         this.footer.container = this.wrapEl.createChild();
51477          
51478         this.footer = Roo.factory(this.footer, Roo);
51479         
51480     }
51481     
51482     if(this.resizeEl){
51483         this.resizeEl = Roo.get(this.resizeEl, true);
51484     }else{
51485         this.resizeEl = this.el;
51486     }
51487     // handle view.xtype
51488     
51489  
51490     
51491     
51492     this.addEvents({
51493         /**
51494          * @event activate
51495          * Fires when this panel is activated. 
51496          * @param {Roo.ContentPanel} this
51497          */
51498         "activate" : true,
51499         /**
51500          * @event deactivate
51501          * Fires when this panel is activated. 
51502          * @param {Roo.ContentPanel} this
51503          */
51504         "deactivate" : true,
51505
51506         /**
51507          * @event resize
51508          * Fires when this panel is resized if fitToFrame is true.
51509          * @param {Roo.ContentPanel} this
51510          * @param {Number} width The width after any component adjustments
51511          * @param {Number} height The height after any component adjustments
51512          */
51513         "resize" : true,
51514         
51515          /**
51516          * @event render
51517          * Fires when this tab is created
51518          * @param {Roo.ContentPanel} this
51519          */
51520         "render" : true
51521         
51522         
51523         
51524     });
51525     
51526
51527     
51528     
51529     if(this.autoScroll){
51530         this.resizeEl.setStyle("overflow", "auto");
51531     } else {
51532         // fix randome scrolling
51533         this.el.on('scroll', function() {
51534             Roo.log('fix random scolling');
51535             this.scrollTo('top',0); 
51536         });
51537     }
51538     content = content || this.content;
51539     if(content){
51540         this.setContent(content);
51541     }
51542     if(config && config.url){
51543         this.setUrl(this.url, this.params, this.loadOnce);
51544     }
51545     
51546     
51547     
51548     Roo.ContentPanel.superclass.constructor.call(this);
51549     
51550     if (this.view && typeof(this.view.xtype) != 'undefined') {
51551         this.view.el = this.el.appendChild(document.createElement("div"));
51552         this.view = Roo.factory(this.view); 
51553         this.view.render  &&  this.view.render(false, '');  
51554     }
51555     
51556     
51557     this.fireEvent('render', this);
51558 };
51559
51560 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51561     tabTip:'',
51562     setRegion : function(region){
51563         this.region = region;
51564         if(region){
51565            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51566         }else{
51567            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51568         } 
51569     },
51570     
51571     /**
51572      * Returns the toolbar for this Panel if one was configured. 
51573      * @return {Roo.Toolbar} 
51574      */
51575     getToolbar : function(){
51576         return this.toolbar;
51577     },
51578     
51579     setActiveState : function(active){
51580         this.active = active;
51581         if(!active){
51582             this.fireEvent("deactivate", this);
51583         }else{
51584             this.fireEvent("activate", this);
51585         }
51586     },
51587     /**
51588      * Updates this panel's element
51589      * @param {String} content The new content
51590      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51591     */
51592     setContent : function(content, loadScripts){
51593         this.el.update(content, loadScripts);
51594     },
51595
51596     ignoreResize : function(w, h){
51597         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51598             return true;
51599         }else{
51600             this.lastSize = {width: w, height: h};
51601             return false;
51602         }
51603     },
51604     /**
51605      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51606      * @return {Roo.UpdateManager} The UpdateManager
51607      */
51608     getUpdateManager : function(){
51609         return this.el.getUpdateManager();
51610     },
51611      /**
51612      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51613      * @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:
51614 <pre><code>
51615 panel.load({
51616     url: "your-url.php",
51617     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51618     callback: yourFunction,
51619     scope: yourObject, //(optional scope)
51620     discardUrl: false,
51621     nocache: false,
51622     text: "Loading...",
51623     timeout: 30,
51624     scripts: false
51625 });
51626 </code></pre>
51627      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51628      * 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.
51629      * @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}
51630      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51631      * @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.
51632      * @return {Roo.ContentPanel} this
51633      */
51634     load : function(){
51635         var um = this.el.getUpdateManager();
51636         um.update.apply(um, arguments);
51637         return this;
51638     },
51639
51640
51641     /**
51642      * 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.
51643      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51644      * @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)
51645      * @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)
51646      * @return {Roo.UpdateManager} The UpdateManager
51647      */
51648     setUrl : function(url, params, loadOnce){
51649         if(this.refreshDelegate){
51650             this.removeListener("activate", this.refreshDelegate);
51651         }
51652         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51653         this.on("activate", this.refreshDelegate);
51654         return this.el.getUpdateManager();
51655     },
51656     
51657     _handleRefresh : function(url, params, loadOnce){
51658         if(!loadOnce || !this.loaded){
51659             var updater = this.el.getUpdateManager();
51660             updater.update(url, params, this._setLoaded.createDelegate(this));
51661         }
51662     },
51663     
51664     _setLoaded : function(){
51665         this.loaded = true;
51666     }, 
51667     
51668     /**
51669      * Returns this panel's id
51670      * @return {String} 
51671      */
51672     getId : function(){
51673         return this.el.id;
51674     },
51675     
51676     /** 
51677      * Returns this panel's element - used by regiosn to add.
51678      * @return {Roo.Element} 
51679      */
51680     getEl : function(){
51681         return this.wrapEl || this.el;
51682     },
51683     
51684     adjustForComponents : function(width, height)
51685     {
51686         //Roo.log('adjustForComponents ');
51687         if(this.resizeEl != this.el){
51688             width -= this.el.getFrameWidth('lr');
51689             height -= this.el.getFrameWidth('tb');
51690         }
51691         if(this.toolbar){
51692             var te = this.toolbar.getEl();
51693             height -= te.getHeight();
51694             te.setWidth(width);
51695         }
51696         if(this.footer){
51697             var te = this.footer.getEl();
51698             Roo.log("footer:" + te.getHeight());
51699             
51700             height -= te.getHeight();
51701             te.setWidth(width);
51702         }
51703         
51704         
51705         if(this.adjustments){
51706             width += this.adjustments[0];
51707             height += this.adjustments[1];
51708         }
51709         return {"width": width, "height": height};
51710     },
51711     
51712     setSize : function(width, height){
51713         if(this.fitToFrame && !this.ignoreResize(width, height)){
51714             if(this.fitContainer && this.resizeEl != this.el){
51715                 this.el.setSize(width, height);
51716             }
51717             var size = this.adjustForComponents(width, height);
51718             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51719             this.fireEvent('resize', this, size.width, size.height);
51720         }
51721     },
51722     
51723     /**
51724      * Returns this panel's title
51725      * @return {String} 
51726      */
51727     getTitle : function(){
51728         return this.title;
51729     },
51730     
51731     /**
51732      * Set this panel's title
51733      * @param {String} title
51734      */
51735     setTitle : function(title){
51736         this.title = title;
51737         if(this.region){
51738             this.region.updatePanelTitle(this, title);
51739         }
51740     },
51741     
51742     /**
51743      * Returns true is this panel was configured to be closable
51744      * @return {Boolean} 
51745      */
51746     isClosable : function(){
51747         return this.closable;
51748     },
51749     
51750     beforeSlide : function(){
51751         this.el.clip();
51752         this.resizeEl.clip();
51753     },
51754     
51755     afterSlide : function(){
51756         this.el.unclip();
51757         this.resizeEl.unclip();
51758     },
51759     
51760     /**
51761      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51762      *   Will fail silently if the {@link #setUrl} method has not been called.
51763      *   This does not activate the panel, just updates its content.
51764      */
51765     refresh : function(){
51766         if(this.refreshDelegate){
51767            this.loaded = false;
51768            this.refreshDelegate();
51769         }
51770     },
51771     
51772     /**
51773      * Destroys this panel
51774      */
51775     destroy : function(){
51776         this.el.removeAllListeners();
51777         var tempEl = document.createElement("span");
51778         tempEl.appendChild(this.el.dom);
51779         tempEl.innerHTML = "";
51780         this.el.remove();
51781         this.el = null;
51782     },
51783     
51784     /**
51785      * form - if the content panel contains a form - this is a reference to it.
51786      * @type {Roo.form.Form}
51787      */
51788     form : false,
51789     /**
51790      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51791      *    This contains a reference to it.
51792      * @type {Roo.View}
51793      */
51794     view : false,
51795     
51796       /**
51797      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51798      * <pre><code>
51799
51800 layout.addxtype({
51801        xtype : 'Form',
51802        items: [ .... ]
51803    }
51804 );
51805
51806 </code></pre>
51807      * @param {Object} cfg Xtype definition of item to add.
51808      */
51809     
51810     addxtype : function(cfg) {
51811         // add form..
51812         if (cfg.xtype.match(/^Form$/)) {
51813             
51814             var el;
51815             //if (this.footer) {
51816             //    el = this.footer.container.insertSibling(false, 'before');
51817             //} else {
51818                 el = this.el.createChild();
51819             //}
51820
51821             this.form = new  Roo.form.Form(cfg);
51822             
51823             
51824             if ( this.form.allItems.length) this.form.render(el.dom);
51825             return this.form;
51826         }
51827         // should only have one of theses..
51828         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51829             // views.. should not be just added - used named prop 'view''
51830             
51831             cfg.el = this.el.appendChild(document.createElement("div"));
51832             // factory?
51833             
51834             var ret = new Roo.factory(cfg);
51835              
51836              ret.render && ret.render(false, ''); // render blank..
51837             this.view = ret;
51838             return ret;
51839         }
51840         return false;
51841     }
51842 });
51843
51844 /**
51845  * @class Roo.GridPanel
51846  * @extends Roo.ContentPanel
51847  * @constructor
51848  * Create a new GridPanel.
51849  * @param {Roo.grid.Grid} grid The grid for this panel
51850  * @param {String/Object} config A string to set only the panel's title, or a config object
51851  */
51852 Roo.GridPanel = function(grid, config){
51853     
51854   
51855     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51856         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51857         
51858     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51859     
51860     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51861     
51862     if(this.toolbar){
51863         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51864     }
51865     // xtype created footer. - not sure if will work as we normally have to render first..
51866     if (this.footer && !this.footer.el && this.footer.xtype) {
51867         
51868         this.footer.container = this.grid.getView().getFooterPanel(true);
51869         this.footer.dataSource = this.grid.dataSource;
51870         this.footer = Roo.factory(this.footer, Roo);
51871         
51872     }
51873     
51874     grid.monitorWindowResize = false; // turn off autosizing
51875     grid.autoHeight = false;
51876     grid.autoWidth = false;
51877     this.grid = grid;
51878     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51879 };
51880
51881 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51882     getId : function(){
51883         return this.grid.id;
51884     },
51885     
51886     /**
51887      * Returns the grid for this panel
51888      * @return {Roo.grid.Grid} 
51889      */
51890     getGrid : function(){
51891         return this.grid;    
51892     },
51893     
51894     setSize : function(width, height){
51895         if(!this.ignoreResize(width, height)){
51896             var grid = this.grid;
51897             var size = this.adjustForComponents(width, height);
51898             grid.getGridEl().setSize(size.width, size.height);
51899             grid.autoSize();
51900         }
51901     },
51902     
51903     beforeSlide : function(){
51904         this.grid.getView().scroller.clip();
51905     },
51906     
51907     afterSlide : function(){
51908         this.grid.getView().scroller.unclip();
51909     },
51910     
51911     destroy : function(){
51912         this.grid.destroy();
51913         delete this.grid;
51914         Roo.GridPanel.superclass.destroy.call(this); 
51915     }
51916 });
51917
51918
51919 /**
51920  * @class Roo.NestedLayoutPanel
51921  * @extends Roo.ContentPanel
51922  * @constructor
51923  * Create a new NestedLayoutPanel.
51924  * 
51925  * 
51926  * @param {Roo.BorderLayout} layout The layout for this panel
51927  * @param {String/Object} config A string to set only the title or a config object
51928  */
51929 Roo.NestedLayoutPanel = function(layout, config)
51930 {
51931     // construct with only one argument..
51932     /* FIXME - implement nicer consturctors
51933     if (layout.layout) {
51934         config = layout;
51935         layout = config.layout;
51936         delete config.layout;
51937     }
51938     if (layout.xtype && !layout.getEl) {
51939         // then layout needs constructing..
51940         layout = Roo.factory(layout, Roo);
51941     }
51942     */
51943     
51944     
51945     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51946     
51947     layout.monitorWindowResize = false; // turn off autosizing
51948     this.layout = layout;
51949     this.layout.getEl().addClass("x-layout-nested-layout");
51950     
51951     
51952     
51953     
51954 };
51955
51956 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51957
51958     setSize : function(width, height){
51959         if(!this.ignoreResize(width, height)){
51960             var size = this.adjustForComponents(width, height);
51961             var el = this.layout.getEl();
51962             el.setSize(size.width, size.height);
51963             var touch = el.dom.offsetWidth;
51964             this.layout.layout();
51965             // ie requires a double layout on the first pass
51966             if(Roo.isIE && !this.initialized){
51967                 this.initialized = true;
51968                 this.layout.layout();
51969             }
51970         }
51971     },
51972     
51973     // activate all subpanels if not currently active..
51974     
51975     setActiveState : function(active){
51976         this.active = active;
51977         if(!active){
51978             this.fireEvent("deactivate", this);
51979             return;
51980         }
51981         
51982         this.fireEvent("activate", this);
51983         // not sure if this should happen before or after..
51984         if (!this.layout) {
51985             return; // should not happen..
51986         }
51987         var reg = false;
51988         for (var r in this.layout.regions) {
51989             reg = this.layout.getRegion(r);
51990             if (reg.getActivePanel()) {
51991                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51992                 reg.setActivePanel(reg.getActivePanel());
51993                 continue;
51994             }
51995             if (!reg.panels.length) {
51996                 continue;
51997             }
51998             reg.showPanel(reg.getPanel(0));
51999         }
52000         
52001         
52002         
52003         
52004     },
52005     
52006     /**
52007      * Returns the nested BorderLayout for this panel
52008      * @return {Roo.BorderLayout} 
52009      */
52010     getLayout : function(){
52011         return this.layout;
52012     },
52013     
52014      /**
52015      * Adds a xtype elements to the layout of the nested panel
52016      * <pre><code>
52017
52018 panel.addxtype({
52019        xtype : 'ContentPanel',
52020        region: 'west',
52021        items: [ .... ]
52022    }
52023 );
52024
52025 panel.addxtype({
52026         xtype : 'NestedLayoutPanel',
52027         region: 'west',
52028         layout: {
52029            center: { },
52030            west: { }   
52031         },
52032         items : [ ... list of content panels or nested layout panels.. ]
52033    }
52034 );
52035 </code></pre>
52036      * @param {Object} cfg Xtype definition of item to add.
52037      */
52038     addxtype : function(cfg) {
52039         return this.layout.addxtype(cfg);
52040     
52041     }
52042 });
52043
52044 Roo.ScrollPanel = function(el, config, content){
52045     config = config || {};
52046     config.fitToFrame = true;
52047     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52048     
52049     this.el.dom.style.overflow = "hidden";
52050     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52051     this.el.removeClass("x-layout-inactive-content");
52052     this.el.on("mousewheel", this.onWheel, this);
52053
52054     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52055     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52056     up.unselectable(); down.unselectable();
52057     up.on("click", this.scrollUp, this);
52058     down.on("click", this.scrollDown, this);
52059     up.addClassOnOver("x-scroller-btn-over");
52060     down.addClassOnOver("x-scroller-btn-over");
52061     up.addClassOnClick("x-scroller-btn-click");
52062     down.addClassOnClick("x-scroller-btn-click");
52063     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52064
52065     this.resizeEl = this.el;
52066     this.el = wrap; this.up = up; this.down = down;
52067 };
52068
52069 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52070     increment : 100,
52071     wheelIncrement : 5,
52072     scrollUp : function(){
52073         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52074     },
52075
52076     scrollDown : function(){
52077         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52078     },
52079
52080     afterScroll : function(){
52081         var el = this.resizeEl;
52082         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52083         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52084         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52085     },
52086
52087     setSize : function(){
52088         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52089         this.afterScroll();
52090     },
52091
52092     onWheel : function(e){
52093         var d = e.getWheelDelta();
52094         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52095         this.afterScroll();
52096         e.stopEvent();
52097     },
52098
52099     setContent : function(content, loadScripts){
52100         this.resizeEl.update(content, loadScripts);
52101     }
52102
52103 });
52104
52105
52106
52107
52108
52109
52110
52111
52112
52113 /**
52114  * @class Roo.TreePanel
52115  * @extends Roo.ContentPanel
52116  * @constructor
52117  * Create a new TreePanel. - defaults to fit/scoll contents.
52118  * @param {String/Object} config A string to set only the panel's title, or a config object
52119  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52120  */
52121 Roo.TreePanel = function(config){
52122     var el = config.el;
52123     var tree = config.tree;
52124     delete config.tree; 
52125     delete config.el; // hopefull!
52126     
52127     // wrapper for IE7 strict & safari scroll issue
52128     
52129     var treeEl = el.createChild();
52130     config.resizeEl = treeEl;
52131     
52132     
52133     
52134     Roo.TreePanel.superclass.constructor.call(this, el, config);
52135  
52136  
52137     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52138     //console.log(tree);
52139     this.on('activate', function()
52140     {
52141         if (this.tree.rendered) {
52142             return;
52143         }
52144         //console.log('render tree');
52145         this.tree.render();
52146     });
52147     // this should not be needed.. - it's actually the 'el' that resizes?
52148     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52149     
52150     //this.on('resize',  function (cp, w, h) {
52151     //        this.tree.innerCt.setWidth(w);
52152     //        this.tree.innerCt.setHeight(h);
52153     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52154     //});
52155
52156         
52157     
52158 };
52159
52160 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52161     fitToFrame : true,
52162     autoScroll : true
52163 });
52164
52165
52166
52167
52168
52169
52170
52171
52172
52173
52174
52175 /*
52176  * Based on:
52177  * Ext JS Library 1.1.1
52178  * Copyright(c) 2006-2007, Ext JS, LLC.
52179  *
52180  * Originally Released Under LGPL - original licence link has changed is not relivant.
52181  *
52182  * Fork - LGPL
52183  * <script type="text/javascript">
52184  */
52185  
52186
52187 /**
52188  * @class Roo.ReaderLayout
52189  * @extends Roo.BorderLayout
52190  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52191  * center region containing two nested regions (a top one for a list view and one for item preview below),
52192  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52193  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52194  * expedites the setup of the overall layout and regions for this common application style.
52195  * Example:
52196  <pre><code>
52197 var reader = new Roo.ReaderLayout();
52198 var CP = Roo.ContentPanel;  // shortcut for adding
52199
52200 reader.beginUpdate();
52201 reader.add("north", new CP("north", "North"));
52202 reader.add("west", new CP("west", {title: "West"}));
52203 reader.add("east", new CP("east", {title: "East"}));
52204
52205 reader.regions.listView.add(new CP("listView", "List"));
52206 reader.regions.preview.add(new CP("preview", "Preview"));
52207 reader.endUpdate();
52208 </code></pre>
52209 * @constructor
52210 * Create a new ReaderLayout
52211 * @param {Object} config Configuration options
52212 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52213 * document.body if omitted)
52214 */
52215 Roo.ReaderLayout = function(config, renderTo){
52216     var c = config || {size:{}};
52217     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52218         north: c.north !== false ? Roo.apply({
52219             split:false,
52220             initialSize: 32,
52221             titlebar: false
52222         }, c.north) : false,
52223         west: c.west !== false ? Roo.apply({
52224             split:true,
52225             initialSize: 200,
52226             minSize: 175,
52227             maxSize: 400,
52228             titlebar: true,
52229             collapsible: true,
52230             animate: true,
52231             margins:{left:5,right:0,bottom:5,top:5},
52232             cmargins:{left:5,right:5,bottom:5,top:5}
52233         }, c.west) : false,
52234         east: c.east !== false ? Roo.apply({
52235             split:true,
52236             initialSize: 200,
52237             minSize: 175,
52238             maxSize: 400,
52239             titlebar: true,
52240             collapsible: true,
52241             animate: true,
52242             margins:{left:0,right:5,bottom:5,top:5},
52243             cmargins:{left:5,right:5,bottom:5,top:5}
52244         }, c.east) : false,
52245         center: Roo.apply({
52246             tabPosition: 'top',
52247             autoScroll:false,
52248             closeOnTab: true,
52249             titlebar:false,
52250             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52251         }, c.center)
52252     });
52253
52254     this.el.addClass('x-reader');
52255
52256     this.beginUpdate();
52257
52258     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52259         south: c.preview !== false ? Roo.apply({
52260             split:true,
52261             initialSize: 200,
52262             minSize: 100,
52263             autoScroll:true,
52264             collapsible:true,
52265             titlebar: true,
52266             cmargins:{top:5,left:0, right:0, bottom:0}
52267         }, c.preview) : false,
52268         center: Roo.apply({
52269             autoScroll:false,
52270             titlebar:false,
52271             minHeight:200
52272         }, c.listView)
52273     });
52274     this.add('center', new Roo.NestedLayoutPanel(inner,
52275             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52276
52277     this.endUpdate();
52278
52279     this.regions.preview = inner.getRegion('south');
52280     this.regions.listView = inner.getRegion('center');
52281 };
52282
52283 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52284  * Based on:
52285  * Ext JS Library 1.1.1
52286  * Copyright(c) 2006-2007, Ext JS, LLC.
52287  *
52288  * Originally Released Under LGPL - original licence link has changed is not relivant.
52289  *
52290  * Fork - LGPL
52291  * <script type="text/javascript">
52292  */
52293  
52294 /**
52295  * @class Roo.grid.Grid
52296  * @extends Roo.util.Observable
52297  * This class represents the primary interface of a component based grid control.
52298  * <br><br>Usage:<pre><code>
52299  var grid = new Roo.grid.Grid("my-container-id", {
52300      ds: myDataStore,
52301      cm: myColModel,
52302      selModel: mySelectionModel,
52303      autoSizeColumns: true,
52304      monitorWindowResize: false,
52305      trackMouseOver: true
52306  });
52307  // set any options
52308  grid.render();
52309  * </code></pre>
52310  * <b>Common Problems:</b><br/>
52311  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52312  * element will correct this<br/>
52313  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52314  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52315  * are unpredictable.<br/>
52316  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52317  * grid to calculate dimensions/offsets.<br/>
52318   * @constructor
52319  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52320  * The container MUST have some type of size defined for the grid to fill. The container will be
52321  * automatically set to position relative if it isn't already.
52322  * @param {Object} config A config object that sets properties on this grid.
52323  */
52324 Roo.grid.Grid = function(container, config){
52325         // initialize the container
52326         this.container = Roo.get(container);
52327         this.container.update("");
52328         this.container.setStyle("overflow", "hidden");
52329     this.container.addClass('x-grid-container');
52330
52331     this.id = this.container.id;
52332
52333     Roo.apply(this, config);
52334     // check and correct shorthanded configs
52335     if(this.ds){
52336         this.dataSource = this.ds;
52337         delete this.ds;
52338     }
52339     if(this.cm){
52340         this.colModel = this.cm;
52341         delete this.cm;
52342     }
52343     if(this.sm){
52344         this.selModel = this.sm;
52345         delete this.sm;
52346     }
52347
52348     if (this.selModel) {
52349         this.selModel = Roo.factory(this.selModel, Roo.grid);
52350         this.sm = this.selModel;
52351         this.sm.xmodule = this.xmodule || false;
52352     }
52353     if (typeof(this.colModel.config) == 'undefined') {
52354         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52355         this.cm = this.colModel;
52356         this.cm.xmodule = this.xmodule || false;
52357     }
52358     if (this.dataSource) {
52359         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52360         this.ds = this.dataSource;
52361         this.ds.xmodule = this.xmodule || false;
52362          
52363     }
52364     
52365     
52366     
52367     if(this.width){
52368         this.container.setWidth(this.width);
52369     }
52370
52371     if(this.height){
52372         this.container.setHeight(this.height);
52373     }
52374     /** @private */
52375         this.addEvents({
52376         // raw events
52377         /**
52378          * @event click
52379          * The raw click event for the entire grid.
52380          * @param {Roo.EventObject} e
52381          */
52382         "click" : true,
52383         /**
52384          * @event dblclick
52385          * The raw dblclick event for the entire grid.
52386          * @param {Roo.EventObject} e
52387          */
52388         "dblclick" : true,
52389         /**
52390          * @event contextmenu
52391          * The raw contextmenu event for the entire grid.
52392          * @param {Roo.EventObject} e
52393          */
52394         "contextmenu" : true,
52395         /**
52396          * @event mousedown
52397          * The raw mousedown event for the entire grid.
52398          * @param {Roo.EventObject} e
52399          */
52400         "mousedown" : true,
52401         /**
52402          * @event mouseup
52403          * The raw mouseup event for the entire grid.
52404          * @param {Roo.EventObject} e
52405          */
52406         "mouseup" : true,
52407         /**
52408          * @event mouseover
52409          * The raw mouseover event for the entire grid.
52410          * @param {Roo.EventObject} e
52411          */
52412         "mouseover" : true,
52413         /**
52414          * @event mouseout
52415          * The raw mouseout event for the entire grid.
52416          * @param {Roo.EventObject} e
52417          */
52418         "mouseout" : true,
52419         /**
52420          * @event keypress
52421          * The raw keypress event for the entire grid.
52422          * @param {Roo.EventObject} e
52423          */
52424         "keypress" : true,
52425         /**
52426          * @event keydown
52427          * The raw keydown event for the entire grid.
52428          * @param {Roo.EventObject} e
52429          */
52430         "keydown" : true,
52431
52432         // custom events
52433
52434         /**
52435          * @event cellclick
52436          * Fires when a cell is clicked
52437          * @param {Grid} this
52438          * @param {Number} rowIndex
52439          * @param {Number} columnIndex
52440          * @param {Roo.EventObject} e
52441          */
52442         "cellclick" : true,
52443         /**
52444          * @event celldblclick
52445          * Fires when a cell is double clicked
52446          * @param {Grid} this
52447          * @param {Number} rowIndex
52448          * @param {Number} columnIndex
52449          * @param {Roo.EventObject} e
52450          */
52451         "celldblclick" : true,
52452         /**
52453          * @event rowclick
52454          * Fires when a row is clicked
52455          * @param {Grid} this
52456          * @param {Number} rowIndex
52457          * @param {Roo.EventObject} e
52458          */
52459         "rowclick" : true,
52460         /**
52461          * @event rowdblclick
52462          * Fires when a row is double clicked
52463          * @param {Grid} this
52464          * @param {Number} rowIndex
52465          * @param {Roo.EventObject} e
52466          */
52467         "rowdblclick" : true,
52468         /**
52469          * @event headerclick
52470          * Fires when a header is clicked
52471          * @param {Grid} this
52472          * @param {Number} columnIndex
52473          * @param {Roo.EventObject} e
52474          */
52475         "headerclick" : true,
52476         /**
52477          * @event headerdblclick
52478          * Fires when a header cell is double clicked
52479          * @param {Grid} this
52480          * @param {Number} columnIndex
52481          * @param {Roo.EventObject} e
52482          */
52483         "headerdblclick" : true,
52484         /**
52485          * @event rowcontextmenu
52486          * Fires when a row is right clicked
52487          * @param {Grid} this
52488          * @param {Number} rowIndex
52489          * @param {Roo.EventObject} e
52490          */
52491         "rowcontextmenu" : true,
52492         /**
52493          * @event cellcontextmenu
52494          * Fires when a cell is right clicked
52495          * @param {Grid} this
52496          * @param {Number} rowIndex
52497          * @param {Number} cellIndex
52498          * @param {Roo.EventObject} e
52499          */
52500          "cellcontextmenu" : true,
52501         /**
52502          * @event headercontextmenu
52503          * Fires when a header is right clicked
52504          * @param {Grid} this
52505          * @param {Number} columnIndex
52506          * @param {Roo.EventObject} e
52507          */
52508         "headercontextmenu" : true,
52509         /**
52510          * @event bodyscroll
52511          * Fires when the body element is scrolled
52512          * @param {Number} scrollLeft
52513          * @param {Number} scrollTop
52514          */
52515         "bodyscroll" : true,
52516         /**
52517          * @event columnresize
52518          * Fires when the user resizes a column
52519          * @param {Number} columnIndex
52520          * @param {Number} newSize
52521          */
52522         "columnresize" : true,
52523         /**
52524          * @event columnmove
52525          * Fires when the user moves a column
52526          * @param {Number} oldIndex
52527          * @param {Number} newIndex
52528          */
52529         "columnmove" : true,
52530         /**
52531          * @event startdrag
52532          * Fires when row(s) start being dragged
52533          * @param {Grid} this
52534          * @param {Roo.GridDD} dd The drag drop object
52535          * @param {event} e The raw browser event
52536          */
52537         "startdrag" : true,
52538         /**
52539          * @event enddrag
52540          * Fires when a drag operation is complete
52541          * @param {Grid} this
52542          * @param {Roo.GridDD} dd The drag drop object
52543          * @param {event} e The raw browser event
52544          */
52545         "enddrag" : true,
52546         /**
52547          * @event dragdrop
52548          * Fires when dragged row(s) are dropped on a valid DD target
52549          * @param {Grid} this
52550          * @param {Roo.GridDD} dd The drag drop object
52551          * @param {String} targetId The target drag drop object
52552          * @param {event} e The raw browser event
52553          */
52554         "dragdrop" : true,
52555         /**
52556          * @event dragover
52557          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52558          * @param {Grid} this
52559          * @param {Roo.GridDD} dd The drag drop object
52560          * @param {String} targetId The target drag drop object
52561          * @param {event} e The raw browser event
52562          */
52563         "dragover" : true,
52564         /**
52565          * @event dragenter
52566          *  Fires when the dragged row(s) first cross another DD target while being dragged
52567          * @param {Grid} this
52568          * @param {Roo.GridDD} dd The drag drop object
52569          * @param {String} targetId The target drag drop object
52570          * @param {event} e The raw browser event
52571          */
52572         "dragenter" : true,
52573         /**
52574          * @event dragout
52575          * Fires when the dragged row(s) leave another DD target while being dragged
52576          * @param {Grid} this
52577          * @param {Roo.GridDD} dd The drag drop object
52578          * @param {String} targetId The target drag drop object
52579          * @param {event} e The raw browser event
52580          */
52581         "dragout" : true,
52582         /**
52583          * @event rowclass
52584          * Fires when a row is rendered, so you can change add a style to it.
52585          * @param {GridView} gridview   The grid view
52586          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52587          */
52588         'rowclass' : true,
52589
52590         /**
52591          * @event render
52592          * Fires when the grid is rendered
52593          * @param {Grid} grid
52594          */
52595         'render' : true
52596     });
52597
52598     Roo.grid.Grid.superclass.constructor.call(this);
52599 };
52600 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52601     
52602     /**
52603      * @cfg {String} ddGroup - drag drop group.
52604      */
52605
52606     /**
52607      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52608      */
52609     minColumnWidth : 25,
52610
52611     /**
52612      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52613      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52614      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52615      */
52616     autoSizeColumns : false,
52617
52618     /**
52619      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52620      */
52621     autoSizeHeaders : true,
52622
52623     /**
52624      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52625      */
52626     monitorWindowResize : true,
52627
52628     /**
52629      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52630      * rows measured to get a columns size. Default is 0 (all rows).
52631      */
52632     maxRowsToMeasure : 0,
52633
52634     /**
52635      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52636      */
52637     trackMouseOver : true,
52638
52639     /**
52640     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52641     */
52642     
52643     /**
52644     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52645     */
52646     enableDragDrop : false,
52647     
52648     /**
52649     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52650     */
52651     enableColumnMove : true,
52652     
52653     /**
52654     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52655     */
52656     enableColumnHide : true,
52657     
52658     /**
52659     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52660     */
52661     enableRowHeightSync : false,
52662     
52663     /**
52664     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52665     */
52666     stripeRows : true,
52667     
52668     /**
52669     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52670     */
52671     autoHeight : false,
52672
52673     /**
52674      * @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.
52675      */
52676     autoExpandColumn : false,
52677
52678     /**
52679     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52680     * Default is 50.
52681     */
52682     autoExpandMin : 50,
52683
52684     /**
52685     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52686     */
52687     autoExpandMax : 1000,
52688
52689     /**
52690     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52691     */
52692     view : null,
52693
52694     /**
52695     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52696     */
52697     loadMask : false,
52698     /**
52699     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52700     */
52701     dropTarget: false,
52702     
52703    
52704     
52705     // private
52706     rendered : false,
52707
52708     /**
52709     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52710     * of a fixed width. Default is false.
52711     */
52712     /**
52713     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52714     */
52715     /**
52716      * Called once after all setup has been completed and the grid is ready to be rendered.
52717      * @return {Roo.grid.Grid} this
52718      */
52719     render : function()
52720     {
52721         var c = this.container;
52722         // try to detect autoHeight/width mode
52723         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52724             this.autoHeight = true;
52725         }
52726         var view = this.getView();
52727         view.init(this);
52728
52729         c.on("click", this.onClick, this);
52730         c.on("dblclick", this.onDblClick, this);
52731         c.on("contextmenu", this.onContextMenu, this);
52732         c.on("keydown", this.onKeyDown, this);
52733         if (Roo.isTouch) {
52734             c.on("touchstart", this.onTouchStart, this);
52735         }
52736
52737         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52738
52739         this.getSelectionModel().init(this);
52740
52741         view.render();
52742
52743         if(this.loadMask){
52744             this.loadMask = new Roo.LoadMask(this.container,
52745                     Roo.apply({store:this.dataSource}, this.loadMask));
52746         }
52747         
52748         
52749         if (this.toolbar && this.toolbar.xtype) {
52750             this.toolbar.container = this.getView().getHeaderPanel(true);
52751             this.toolbar = new Roo.Toolbar(this.toolbar);
52752         }
52753         if (this.footer && this.footer.xtype) {
52754             this.footer.dataSource = this.getDataSource();
52755             this.footer.container = this.getView().getFooterPanel(true);
52756             this.footer = Roo.factory(this.footer, Roo);
52757         }
52758         if (this.dropTarget && this.dropTarget.xtype) {
52759             delete this.dropTarget.xtype;
52760             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52761         }
52762         
52763         
52764         this.rendered = true;
52765         this.fireEvent('render', this);
52766         return this;
52767     },
52768
52769         /**
52770          * Reconfigures the grid to use a different Store and Column Model.
52771          * The View will be bound to the new objects and refreshed.
52772          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52773          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52774          */
52775     reconfigure : function(dataSource, colModel){
52776         if(this.loadMask){
52777             this.loadMask.destroy();
52778             this.loadMask = new Roo.LoadMask(this.container,
52779                     Roo.apply({store:dataSource}, this.loadMask));
52780         }
52781         this.view.bind(dataSource, colModel);
52782         this.dataSource = dataSource;
52783         this.colModel = colModel;
52784         this.view.refresh(true);
52785     },
52786
52787     // private
52788     onKeyDown : function(e){
52789         this.fireEvent("keydown", e);
52790     },
52791
52792     /**
52793      * Destroy this grid.
52794      * @param {Boolean} removeEl True to remove the element
52795      */
52796     destroy : function(removeEl, keepListeners){
52797         if(this.loadMask){
52798             this.loadMask.destroy();
52799         }
52800         var c = this.container;
52801         c.removeAllListeners();
52802         this.view.destroy();
52803         this.colModel.purgeListeners();
52804         if(!keepListeners){
52805             this.purgeListeners();
52806         }
52807         c.update("");
52808         if(removeEl === true){
52809             c.remove();
52810         }
52811     },
52812
52813     // private
52814     processEvent : function(name, e){
52815         // does this fire select???
52816         //Roo.log('grid:processEvent '  + name);
52817         
52818         if (name != 'touchstart' ) {
52819             this.fireEvent(name, e);    
52820         }
52821         
52822         var t = e.getTarget();
52823         var v = this.view;
52824         var header = v.findHeaderIndex(t);
52825         if(header !== false){
52826             var ename = name == 'touchstart' ? 'click' : name;
52827              
52828             this.fireEvent("header" + ename, this, header, e);
52829         }else{
52830             var row = v.findRowIndex(t);
52831             var cell = v.findCellIndex(t);
52832             if (name == 'touchstart') {
52833                 // first touch is always a click.
52834                 // hopefull this happens after selection is updated.?
52835                 name = false;
52836                 
52837                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52838                     var cs = this.selModel.getSelectedCell();
52839                     if (row == cs[0] && cell == cs[1]){
52840                         name = 'dblclick';
52841                     }
52842                 }
52843                 if (typeof(this.selModel.getSelections) != 'undefined') {
52844                     var cs = this.selModel.getSelections();
52845                     var ds = this.dataSource;
52846                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52847                         name = 'dblclick';
52848                     }
52849                 }
52850                 if (!name) {
52851                     return;
52852                 }
52853             }
52854             
52855             
52856             if(row !== false){
52857                 this.fireEvent("row" + name, this, row, e);
52858                 if(cell !== false){
52859                     this.fireEvent("cell" + name, this, row, cell, e);
52860                 }
52861             }
52862         }
52863     },
52864
52865     // private
52866     onClick : function(e){
52867         this.processEvent("click", e);
52868     },
52869    // private
52870     onTouchStart : function(e){
52871         this.processEvent("touchstart", e);
52872     },
52873
52874     // private
52875     onContextMenu : function(e, t){
52876         this.processEvent("contextmenu", e);
52877     },
52878
52879     // private
52880     onDblClick : function(e){
52881         this.processEvent("dblclick", e);
52882     },
52883
52884     // private
52885     walkCells : function(row, col, step, fn, scope){
52886         var cm = this.colModel, clen = cm.getColumnCount();
52887         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52888         if(step < 0){
52889             if(col < 0){
52890                 row--;
52891                 first = false;
52892             }
52893             while(row >= 0){
52894                 if(!first){
52895                     col = clen-1;
52896                 }
52897                 first = false;
52898                 while(col >= 0){
52899                     if(fn.call(scope || this, row, col, cm) === true){
52900                         return [row, col];
52901                     }
52902                     col--;
52903                 }
52904                 row--;
52905             }
52906         } else {
52907             if(col >= clen){
52908                 row++;
52909                 first = false;
52910             }
52911             while(row < rlen){
52912                 if(!first){
52913                     col = 0;
52914                 }
52915                 first = false;
52916                 while(col < clen){
52917                     if(fn.call(scope || this, row, col, cm) === true){
52918                         return [row, col];
52919                     }
52920                     col++;
52921                 }
52922                 row++;
52923             }
52924         }
52925         return null;
52926     },
52927
52928     // private
52929     getSelections : function(){
52930         return this.selModel.getSelections();
52931     },
52932
52933     /**
52934      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52935      * but if manual update is required this method will initiate it.
52936      */
52937     autoSize : function(){
52938         if(this.rendered){
52939             this.view.layout();
52940             if(this.view.adjustForScroll){
52941                 this.view.adjustForScroll();
52942             }
52943         }
52944     },
52945
52946     /**
52947      * Returns the grid's underlying element.
52948      * @return {Element} The element
52949      */
52950     getGridEl : function(){
52951         return this.container;
52952     },
52953
52954     // private for compatibility, overridden by editor grid
52955     stopEditing : function(){},
52956
52957     /**
52958      * Returns the grid's SelectionModel.
52959      * @return {SelectionModel}
52960      */
52961     getSelectionModel : function(){
52962         if(!this.selModel){
52963             this.selModel = new Roo.grid.RowSelectionModel();
52964         }
52965         return this.selModel;
52966     },
52967
52968     /**
52969      * Returns the grid's DataSource.
52970      * @return {DataSource}
52971      */
52972     getDataSource : function(){
52973         return this.dataSource;
52974     },
52975
52976     /**
52977      * Returns the grid's ColumnModel.
52978      * @return {ColumnModel}
52979      */
52980     getColumnModel : function(){
52981         return this.colModel;
52982     },
52983
52984     /**
52985      * Returns the grid's GridView object.
52986      * @return {GridView}
52987      */
52988     getView : function(){
52989         if(!this.view){
52990             this.view = new Roo.grid.GridView(this.viewConfig);
52991         }
52992         return this.view;
52993     },
52994     /**
52995      * Called to get grid's drag proxy text, by default returns this.ddText.
52996      * @return {String}
52997      */
52998     getDragDropText : function(){
52999         var count = this.selModel.getCount();
53000         return String.format(this.ddText, count, count == 1 ? '' : 's');
53001     }
53002 });
53003 /**
53004  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53005  * %0 is replaced with the number of selected rows.
53006  * @type String
53007  */
53008 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53009  * Based on:
53010  * Ext JS Library 1.1.1
53011  * Copyright(c) 2006-2007, Ext JS, LLC.
53012  *
53013  * Originally Released Under LGPL - original licence link has changed is not relivant.
53014  *
53015  * Fork - LGPL
53016  * <script type="text/javascript">
53017  */
53018  
53019 Roo.grid.AbstractGridView = function(){
53020         this.grid = null;
53021         
53022         this.events = {
53023             "beforerowremoved" : true,
53024             "beforerowsinserted" : true,
53025             "beforerefresh" : true,
53026             "rowremoved" : true,
53027             "rowsinserted" : true,
53028             "rowupdated" : true,
53029             "refresh" : true
53030         };
53031     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53032 };
53033
53034 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53035     rowClass : "x-grid-row",
53036     cellClass : "x-grid-cell",
53037     tdClass : "x-grid-td",
53038     hdClass : "x-grid-hd",
53039     splitClass : "x-grid-hd-split",
53040     
53041     init: function(grid){
53042         this.grid = grid;
53043                 var cid = this.grid.getGridEl().id;
53044         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53045         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53046         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53047         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53048         },
53049         
53050     getColumnRenderers : function(){
53051         var renderers = [];
53052         var cm = this.grid.colModel;
53053         var colCount = cm.getColumnCount();
53054         for(var i = 0; i < colCount; i++){
53055             renderers[i] = cm.getRenderer(i);
53056         }
53057         return renderers;
53058     },
53059     
53060     getColumnIds : function(){
53061         var ids = [];
53062         var cm = this.grid.colModel;
53063         var colCount = cm.getColumnCount();
53064         for(var i = 0; i < colCount; i++){
53065             ids[i] = cm.getColumnId(i);
53066         }
53067         return ids;
53068     },
53069     
53070     getDataIndexes : function(){
53071         if(!this.indexMap){
53072             this.indexMap = this.buildIndexMap();
53073         }
53074         return this.indexMap.colToData;
53075     },
53076     
53077     getColumnIndexByDataIndex : function(dataIndex){
53078         if(!this.indexMap){
53079             this.indexMap = this.buildIndexMap();
53080         }
53081         return this.indexMap.dataToCol[dataIndex];
53082     },
53083     
53084     /**
53085      * Set a css style for a column dynamically. 
53086      * @param {Number} colIndex The index of the column
53087      * @param {String} name The css property name
53088      * @param {String} value The css value
53089      */
53090     setCSSStyle : function(colIndex, name, value){
53091         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53092         Roo.util.CSS.updateRule(selector, name, value);
53093     },
53094     
53095     generateRules : function(cm){
53096         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53097         Roo.util.CSS.removeStyleSheet(rulesId);
53098         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53099             var cid = cm.getColumnId(i);
53100             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53101                          this.tdSelector, cid, " {\n}\n",
53102                          this.hdSelector, cid, " {\n}\n",
53103                          this.splitSelector, cid, " {\n}\n");
53104         }
53105         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53106     }
53107 });/*
53108  * Based on:
53109  * Ext JS Library 1.1.1
53110  * Copyright(c) 2006-2007, Ext JS, LLC.
53111  *
53112  * Originally Released Under LGPL - original licence link has changed is not relivant.
53113  *
53114  * Fork - LGPL
53115  * <script type="text/javascript">
53116  */
53117
53118 // private
53119 // This is a support class used internally by the Grid components
53120 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53121     this.grid = grid;
53122     this.view = grid.getView();
53123     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53124     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53125     if(hd2){
53126         this.setHandleElId(Roo.id(hd));
53127         this.setOuterHandleElId(Roo.id(hd2));
53128     }
53129     this.scroll = false;
53130 };
53131 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53132     maxDragWidth: 120,
53133     getDragData : function(e){
53134         var t = Roo.lib.Event.getTarget(e);
53135         var h = this.view.findHeaderCell(t);
53136         if(h){
53137             return {ddel: h.firstChild, header:h};
53138         }
53139         return false;
53140     },
53141
53142     onInitDrag : function(e){
53143         this.view.headersDisabled = true;
53144         var clone = this.dragData.ddel.cloneNode(true);
53145         clone.id = Roo.id();
53146         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53147         this.proxy.update(clone);
53148         return true;
53149     },
53150
53151     afterValidDrop : function(){
53152         var v = this.view;
53153         setTimeout(function(){
53154             v.headersDisabled = false;
53155         }, 50);
53156     },
53157
53158     afterInvalidDrop : function(){
53159         var v = this.view;
53160         setTimeout(function(){
53161             v.headersDisabled = false;
53162         }, 50);
53163     }
53164 });
53165 /*
53166  * Based on:
53167  * Ext JS Library 1.1.1
53168  * Copyright(c) 2006-2007, Ext JS, LLC.
53169  *
53170  * Originally Released Under LGPL - original licence link has changed is not relivant.
53171  *
53172  * Fork - LGPL
53173  * <script type="text/javascript">
53174  */
53175 // private
53176 // This is a support class used internally by the Grid components
53177 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53178     this.grid = grid;
53179     this.view = grid.getView();
53180     // split the proxies so they don't interfere with mouse events
53181     this.proxyTop = Roo.DomHelper.append(document.body, {
53182         cls:"col-move-top", html:"&#160;"
53183     }, true);
53184     this.proxyBottom = Roo.DomHelper.append(document.body, {
53185         cls:"col-move-bottom", html:"&#160;"
53186     }, true);
53187     this.proxyTop.hide = this.proxyBottom.hide = function(){
53188         this.setLeftTop(-100,-100);
53189         this.setStyle("visibility", "hidden");
53190     };
53191     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53192     // temporarily disabled
53193     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53194     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53195 };
53196 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53197     proxyOffsets : [-4, -9],
53198     fly: Roo.Element.fly,
53199
53200     getTargetFromEvent : function(e){
53201         var t = Roo.lib.Event.getTarget(e);
53202         var cindex = this.view.findCellIndex(t);
53203         if(cindex !== false){
53204             return this.view.getHeaderCell(cindex);
53205         }
53206         return null;
53207     },
53208
53209     nextVisible : function(h){
53210         var v = this.view, cm = this.grid.colModel;
53211         h = h.nextSibling;
53212         while(h){
53213             if(!cm.isHidden(v.getCellIndex(h))){
53214                 return h;
53215             }
53216             h = h.nextSibling;
53217         }
53218         return null;
53219     },
53220
53221     prevVisible : function(h){
53222         var v = this.view, cm = this.grid.colModel;
53223         h = h.prevSibling;
53224         while(h){
53225             if(!cm.isHidden(v.getCellIndex(h))){
53226                 return h;
53227             }
53228             h = h.prevSibling;
53229         }
53230         return null;
53231     },
53232
53233     positionIndicator : function(h, n, e){
53234         var x = Roo.lib.Event.getPageX(e);
53235         var r = Roo.lib.Dom.getRegion(n.firstChild);
53236         var px, pt, py = r.top + this.proxyOffsets[1];
53237         if((r.right - x) <= (r.right-r.left)/2){
53238             px = r.right+this.view.borderWidth;
53239             pt = "after";
53240         }else{
53241             px = r.left;
53242             pt = "before";
53243         }
53244         var oldIndex = this.view.getCellIndex(h);
53245         var newIndex = this.view.getCellIndex(n);
53246
53247         if(this.grid.colModel.isFixed(newIndex)){
53248             return false;
53249         }
53250
53251         var locked = this.grid.colModel.isLocked(newIndex);
53252
53253         if(pt == "after"){
53254             newIndex++;
53255         }
53256         if(oldIndex < newIndex){
53257             newIndex--;
53258         }
53259         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53260             return false;
53261         }
53262         px +=  this.proxyOffsets[0];
53263         this.proxyTop.setLeftTop(px, py);
53264         this.proxyTop.show();
53265         if(!this.bottomOffset){
53266             this.bottomOffset = this.view.mainHd.getHeight();
53267         }
53268         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53269         this.proxyBottom.show();
53270         return pt;
53271     },
53272
53273     onNodeEnter : function(n, dd, e, data){
53274         if(data.header != n){
53275             this.positionIndicator(data.header, n, e);
53276         }
53277     },
53278
53279     onNodeOver : function(n, dd, e, data){
53280         var result = false;
53281         if(data.header != n){
53282             result = this.positionIndicator(data.header, n, e);
53283         }
53284         if(!result){
53285             this.proxyTop.hide();
53286             this.proxyBottom.hide();
53287         }
53288         return result ? this.dropAllowed : this.dropNotAllowed;
53289     },
53290
53291     onNodeOut : function(n, dd, e, data){
53292         this.proxyTop.hide();
53293         this.proxyBottom.hide();
53294     },
53295
53296     onNodeDrop : function(n, dd, e, data){
53297         var h = data.header;
53298         if(h != n){
53299             var cm = this.grid.colModel;
53300             var x = Roo.lib.Event.getPageX(e);
53301             var r = Roo.lib.Dom.getRegion(n.firstChild);
53302             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53303             var oldIndex = this.view.getCellIndex(h);
53304             var newIndex = this.view.getCellIndex(n);
53305             var locked = cm.isLocked(newIndex);
53306             if(pt == "after"){
53307                 newIndex++;
53308             }
53309             if(oldIndex < newIndex){
53310                 newIndex--;
53311             }
53312             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53313                 return false;
53314             }
53315             cm.setLocked(oldIndex, locked, true);
53316             cm.moveColumn(oldIndex, newIndex);
53317             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53318             return true;
53319         }
53320         return false;
53321     }
53322 });
53323 /*
53324  * Based on:
53325  * Ext JS Library 1.1.1
53326  * Copyright(c) 2006-2007, Ext JS, LLC.
53327  *
53328  * Originally Released Under LGPL - original licence link has changed is not relivant.
53329  *
53330  * Fork - LGPL
53331  * <script type="text/javascript">
53332  */
53333   
53334 /**
53335  * @class Roo.grid.GridView
53336  * @extends Roo.util.Observable
53337  *
53338  * @constructor
53339  * @param {Object} config
53340  */
53341 Roo.grid.GridView = function(config){
53342     Roo.grid.GridView.superclass.constructor.call(this);
53343     this.el = null;
53344
53345     Roo.apply(this, config);
53346 };
53347
53348 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53349
53350     unselectable :  'unselectable="on"',
53351     unselectableCls :  'x-unselectable',
53352     
53353     
53354     rowClass : "x-grid-row",
53355
53356     cellClass : "x-grid-col",
53357
53358     tdClass : "x-grid-td",
53359
53360     hdClass : "x-grid-hd",
53361
53362     splitClass : "x-grid-split",
53363
53364     sortClasses : ["sort-asc", "sort-desc"],
53365
53366     enableMoveAnim : false,
53367
53368     hlColor: "C3DAF9",
53369
53370     dh : Roo.DomHelper,
53371
53372     fly : Roo.Element.fly,
53373
53374     css : Roo.util.CSS,
53375
53376     borderWidth: 1,
53377
53378     splitOffset: 3,
53379
53380     scrollIncrement : 22,
53381
53382     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53383
53384     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53385
53386     bind : function(ds, cm){
53387         if(this.ds){
53388             this.ds.un("load", this.onLoad, this);
53389             this.ds.un("datachanged", this.onDataChange, this);
53390             this.ds.un("add", this.onAdd, this);
53391             this.ds.un("remove", this.onRemove, this);
53392             this.ds.un("update", this.onUpdate, this);
53393             this.ds.un("clear", this.onClear, this);
53394         }
53395         if(ds){
53396             ds.on("load", this.onLoad, this);
53397             ds.on("datachanged", this.onDataChange, this);
53398             ds.on("add", this.onAdd, this);
53399             ds.on("remove", this.onRemove, this);
53400             ds.on("update", this.onUpdate, this);
53401             ds.on("clear", this.onClear, this);
53402         }
53403         this.ds = ds;
53404
53405         if(this.cm){
53406             this.cm.un("widthchange", this.onColWidthChange, this);
53407             this.cm.un("headerchange", this.onHeaderChange, this);
53408             this.cm.un("hiddenchange", this.onHiddenChange, this);
53409             this.cm.un("columnmoved", this.onColumnMove, this);
53410             this.cm.un("columnlockchange", this.onColumnLock, this);
53411         }
53412         if(cm){
53413             this.generateRules(cm);
53414             cm.on("widthchange", this.onColWidthChange, this);
53415             cm.on("headerchange", this.onHeaderChange, this);
53416             cm.on("hiddenchange", this.onHiddenChange, this);
53417             cm.on("columnmoved", this.onColumnMove, this);
53418             cm.on("columnlockchange", this.onColumnLock, this);
53419         }
53420         this.cm = cm;
53421     },
53422
53423     init: function(grid){
53424         Roo.grid.GridView.superclass.init.call(this, grid);
53425
53426         this.bind(grid.dataSource, grid.colModel);
53427
53428         grid.on("headerclick", this.handleHeaderClick, this);
53429
53430         if(grid.trackMouseOver){
53431             grid.on("mouseover", this.onRowOver, this);
53432             grid.on("mouseout", this.onRowOut, this);
53433         }
53434         grid.cancelTextSelection = function(){};
53435         this.gridId = grid.id;
53436
53437         var tpls = this.templates || {};
53438
53439         if(!tpls.master){
53440             tpls.master = new Roo.Template(
53441                '<div class="x-grid" hidefocus="true">',
53442                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53443                   '<div class="x-grid-topbar"></div>',
53444                   '<div class="x-grid-scroller"><div></div></div>',
53445                   '<div class="x-grid-locked">',
53446                       '<div class="x-grid-header">{lockedHeader}</div>',
53447                       '<div class="x-grid-body">{lockedBody}</div>',
53448                   "</div>",
53449                   '<div class="x-grid-viewport">',
53450                       '<div class="x-grid-header">{header}</div>',
53451                       '<div class="x-grid-body">{body}</div>',
53452                   "</div>",
53453                   '<div class="x-grid-bottombar"></div>',
53454                  
53455                   '<div class="x-grid-resize-proxy">&#160;</div>',
53456                "</div>"
53457             );
53458             tpls.master.disableformats = true;
53459         }
53460
53461         if(!tpls.header){
53462             tpls.header = new Roo.Template(
53463                '<table border="0" cellspacing="0" cellpadding="0">',
53464                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53465                "</table>{splits}"
53466             );
53467             tpls.header.disableformats = true;
53468         }
53469         tpls.header.compile();
53470
53471         if(!tpls.hcell){
53472             tpls.hcell = new Roo.Template(
53473                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53474                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53475                 "</div></td>"
53476              );
53477              tpls.hcell.disableFormats = true;
53478         }
53479         tpls.hcell.compile();
53480
53481         if(!tpls.hsplit){
53482             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53483                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53484             tpls.hsplit.disableFormats = true;
53485         }
53486         tpls.hsplit.compile();
53487
53488         if(!tpls.body){
53489             tpls.body = new Roo.Template(
53490                '<table border="0" cellspacing="0" cellpadding="0">',
53491                "<tbody>{rows}</tbody>",
53492                "</table>"
53493             );
53494             tpls.body.disableFormats = true;
53495         }
53496         tpls.body.compile();
53497
53498         if(!tpls.row){
53499             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53500             tpls.row.disableFormats = true;
53501         }
53502         tpls.row.compile();
53503
53504         if(!tpls.cell){
53505             tpls.cell = new Roo.Template(
53506                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53507                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53508                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53509                 "</td>"
53510             );
53511             tpls.cell.disableFormats = true;
53512         }
53513         tpls.cell.compile();
53514
53515         this.templates = tpls;
53516     },
53517
53518     // remap these for backwards compat
53519     onColWidthChange : function(){
53520         this.updateColumns.apply(this, arguments);
53521     },
53522     onHeaderChange : function(){
53523         this.updateHeaders.apply(this, arguments);
53524     }, 
53525     onHiddenChange : function(){
53526         this.handleHiddenChange.apply(this, arguments);
53527     },
53528     onColumnMove : function(){
53529         this.handleColumnMove.apply(this, arguments);
53530     },
53531     onColumnLock : function(){
53532         this.handleLockChange.apply(this, arguments);
53533     },
53534
53535     onDataChange : function(){
53536         this.refresh();
53537         this.updateHeaderSortState();
53538     },
53539
53540     onClear : function(){
53541         this.refresh();
53542     },
53543
53544     onUpdate : function(ds, record){
53545         this.refreshRow(record);
53546     },
53547
53548     refreshRow : function(record){
53549         var ds = this.ds, index;
53550         if(typeof record == 'number'){
53551             index = record;
53552             record = ds.getAt(index);
53553         }else{
53554             index = ds.indexOf(record);
53555         }
53556         this.insertRows(ds, index, index, true);
53557         this.onRemove(ds, record, index+1, true);
53558         this.syncRowHeights(index, index);
53559         this.layout();
53560         this.fireEvent("rowupdated", this, index, record);
53561     },
53562
53563     onAdd : function(ds, records, index){
53564         this.insertRows(ds, index, index + (records.length-1));
53565     },
53566
53567     onRemove : function(ds, record, index, isUpdate){
53568         if(isUpdate !== true){
53569             this.fireEvent("beforerowremoved", this, index, record);
53570         }
53571         var bt = this.getBodyTable(), lt = this.getLockedTable();
53572         if(bt.rows[index]){
53573             bt.firstChild.removeChild(bt.rows[index]);
53574         }
53575         if(lt.rows[index]){
53576             lt.firstChild.removeChild(lt.rows[index]);
53577         }
53578         if(isUpdate !== true){
53579             this.stripeRows(index);
53580             this.syncRowHeights(index, index);
53581             this.layout();
53582             this.fireEvent("rowremoved", this, index, record);
53583         }
53584     },
53585
53586     onLoad : function(){
53587         this.scrollToTop();
53588     },
53589
53590     /**
53591      * Scrolls the grid to the top
53592      */
53593     scrollToTop : function(){
53594         if(this.scroller){
53595             this.scroller.dom.scrollTop = 0;
53596             this.syncScroll();
53597         }
53598     },
53599
53600     /**
53601      * Gets a panel in the header of the grid that can be used for toolbars etc.
53602      * After modifying the contents of this panel a call to grid.autoSize() may be
53603      * required to register any changes in size.
53604      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53605      * @return Roo.Element
53606      */
53607     getHeaderPanel : function(doShow){
53608         if(doShow){
53609             this.headerPanel.show();
53610         }
53611         return this.headerPanel;
53612     },
53613
53614     /**
53615      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53616      * After modifying the contents of this panel a call to grid.autoSize() may be
53617      * required to register any changes in size.
53618      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53619      * @return Roo.Element
53620      */
53621     getFooterPanel : function(doShow){
53622         if(doShow){
53623             this.footerPanel.show();
53624         }
53625         return this.footerPanel;
53626     },
53627
53628     initElements : function(){
53629         var E = Roo.Element;
53630         var el = this.grid.getGridEl().dom.firstChild;
53631         var cs = el.childNodes;
53632
53633         this.el = new E(el);
53634         
53635          this.focusEl = new E(el.firstChild);
53636         this.focusEl.swallowEvent("click", true);
53637         
53638         this.headerPanel = new E(cs[1]);
53639         this.headerPanel.enableDisplayMode("block");
53640
53641         this.scroller = new E(cs[2]);
53642         this.scrollSizer = new E(this.scroller.dom.firstChild);
53643
53644         this.lockedWrap = new E(cs[3]);
53645         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53646         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53647
53648         this.mainWrap = new E(cs[4]);
53649         this.mainHd = new E(this.mainWrap.dom.firstChild);
53650         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53651
53652         this.footerPanel = new E(cs[5]);
53653         this.footerPanel.enableDisplayMode("block");
53654
53655         this.resizeProxy = new E(cs[6]);
53656
53657         this.headerSelector = String.format(
53658            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53659            this.lockedHd.id, this.mainHd.id
53660         );
53661
53662         this.splitterSelector = String.format(
53663            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53664            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53665         );
53666     },
53667     idToCssName : function(s)
53668     {
53669         return s.replace(/[^a-z0-9]+/ig, '-');
53670     },
53671
53672     getHeaderCell : function(index){
53673         return Roo.DomQuery.select(this.headerSelector)[index];
53674     },
53675
53676     getHeaderCellMeasure : function(index){
53677         return this.getHeaderCell(index).firstChild;
53678     },
53679
53680     getHeaderCellText : function(index){
53681         return this.getHeaderCell(index).firstChild.firstChild;
53682     },
53683
53684     getLockedTable : function(){
53685         return this.lockedBody.dom.firstChild;
53686     },
53687
53688     getBodyTable : function(){
53689         return this.mainBody.dom.firstChild;
53690     },
53691
53692     getLockedRow : function(index){
53693         return this.getLockedTable().rows[index];
53694     },
53695
53696     getRow : function(index){
53697         return this.getBodyTable().rows[index];
53698     },
53699
53700     getRowComposite : function(index){
53701         if(!this.rowEl){
53702             this.rowEl = new Roo.CompositeElementLite();
53703         }
53704         var els = [], lrow, mrow;
53705         if(lrow = this.getLockedRow(index)){
53706             els.push(lrow);
53707         }
53708         if(mrow = this.getRow(index)){
53709             els.push(mrow);
53710         }
53711         this.rowEl.elements = els;
53712         return this.rowEl;
53713     },
53714     /**
53715      * Gets the 'td' of the cell
53716      * 
53717      * @param {Integer} rowIndex row to select
53718      * @param {Integer} colIndex column to select
53719      * 
53720      * @return {Object} 
53721      */
53722     getCell : function(rowIndex, colIndex){
53723         var locked = this.cm.getLockedCount();
53724         var source;
53725         if(colIndex < locked){
53726             source = this.lockedBody.dom.firstChild;
53727         }else{
53728             source = this.mainBody.dom.firstChild;
53729             colIndex -= locked;
53730         }
53731         return source.rows[rowIndex].childNodes[colIndex];
53732     },
53733
53734     getCellText : function(rowIndex, colIndex){
53735         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53736     },
53737
53738     getCellBox : function(cell){
53739         var b = this.fly(cell).getBox();
53740         if(Roo.isOpera){ // opera fails to report the Y
53741             b.y = cell.offsetTop + this.mainBody.getY();
53742         }
53743         return b;
53744     },
53745
53746     getCellIndex : function(cell){
53747         var id = String(cell.className).match(this.cellRE);
53748         if(id){
53749             return parseInt(id[1], 10);
53750         }
53751         return 0;
53752     },
53753
53754     findHeaderIndex : function(n){
53755         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53756         return r ? this.getCellIndex(r) : false;
53757     },
53758
53759     findHeaderCell : function(n){
53760         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53761         return r ? r : false;
53762     },
53763
53764     findRowIndex : function(n){
53765         if(!n){
53766             return false;
53767         }
53768         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53769         return r ? r.rowIndex : false;
53770     },
53771
53772     findCellIndex : function(node){
53773         var stop = this.el.dom;
53774         while(node && node != stop){
53775             if(this.findRE.test(node.className)){
53776                 return this.getCellIndex(node);
53777             }
53778             node = node.parentNode;
53779         }
53780         return false;
53781     },
53782
53783     getColumnId : function(index){
53784         return this.cm.getColumnId(index);
53785     },
53786
53787     getSplitters : function()
53788     {
53789         if(this.splitterSelector){
53790            return Roo.DomQuery.select(this.splitterSelector);
53791         }else{
53792             return null;
53793       }
53794     },
53795
53796     getSplitter : function(index){
53797         return this.getSplitters()[index];
53798     },
53799
53800     onRowOver : function(e, t){
53801         var row;
53802         if((row = this.findRowIndex(t)) !== false){
53803             this.getRowComposite(row).addClass("x-grid-row-over");
53804         }
53805     },
53806
53807     onRowOut : function(e, t){
53808         var row;
53809         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53810             this.getRowComposite(row).removeClass("x-grid-row-over");
53811         }
53812     },
53813
53814     renderHeaders : function(){
53815         var cm = this.cm;
53816         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53817         var cb = [], lb = [], sb = [], lsb = [], p = {};
53818         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53819             p.cellId = "x-grid-hd-0-" + i;
53820             p.splitId = "x-grid-csplit-0-" + i;
53821             p.id = cm.getColumnId(i);
53822             p.title = cm.getColumnTooltip(i) || "";
53823             p.value = cm.getColumnHeader(i) || "";
53824             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53825             if(!cm.isLocked(i)){
53826                 cb[cb.length] = ct.apply(p);
53827                 sb[sb.length] = st.apply(p);
53828             }else{
53829                 lb[lb.length] = ct.apply(p);
53830                 lsb[lsb.length] = st.apply(p);
53831             }
53832         }
53833         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53834                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53835     },
53836
53837     updateHeaders : function(){
53838         var html = this.renderHeaders();
53839         this.lockedHd.update(html[0]);
53840         this.mainHd.update(html[1]);
53841     },
53842
53843     /**
53844      * Focuses the specified row.
53845      * @param {Number} row The row index
53846      */
53847     focusRow : function(row)
53848     {
53849         //Roo.log('GridView.focusRow');
53850         var x = this.scroller.dom.scrollLeft;
53851         this.focusCell(row, 0, false);
53852         this.scroller.dom.scrollLeft = x;
53853     },
53854
53855     /**
53856      * Focuses the specified cell.
53857      * @param {Number} row The row index
53858      * @param {Number} col The column index
53859      * @param {Boolean} hscroll false to disable horizontal scrolling
53860      */
53861     focusCell : function(row, col, hscroll)
53862     {
53863         //Roo.log('GridView.focusCell');
53864         var el = this.ensureVisible(row, col, hscroll);
53865         this.focusEl.alignTo(el, "tl-tl");
53866         if(Roo.isGecko){
53867             this.focusEl.focus();
53868         }else{
53869             this.focusEl.focus.defer(1, this.focusEl);
53870         }
53871     },
53872
53873     /**
53874      * Scrolls the specified cell into view
53875      * @param {Number} row The row index
53876      * @param {Number} col The column index
53877      * @param {Boolean} hscroll false to disable horizontal scrolling
53878      */
53879     ensureVisible : function(row, col, hscroll)
53880     {
53881         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53882         //return null; //disable for testing.
53883         if(typeof row != "number"){
53884             row = row.rowIndex;
53885         }
53886         if(row < 0 && row >= this.ds.getCount()){
53887             return  null;
53888         }
53889         col = (col !== undefined ? col : 0);
53890         var cm = this.grid.colModel;
53891         while(cm.isHidden(col)){
53892             col++;
53893         }
53894
53895         var el = this.getCell(row, col);
53896         if(!el){
53897             return null;
53898         }
53899         var c = this.scroller.dom;
53900
53901         var ctop = parseInt(el.offsetTop, 10);
53902         var cleft = parseInt(el.offsetLeft, 10);
53903         var cbot = ctop + el.offsetHeight;
53904         var cright = cleft + el.offsetWidth;
53905         
53906         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53907         var stop = parseInt(c.scrollTop, 10);
53908         var sleft = parseInt(c.scrollLeft, 10);
53909         var sbot = stop + ch;
53910         var sright = sleft + c.clientWidth;
53911         /*
53912         Roo.log('GridView.ensureVisible:' +
53913                 ' ctop:' + ctop +
53914                 ' c.clientHeight:' + c.clientHeight +
53915                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53916                 ' stop:' + stop +
53917                 ' cbot:' + cbot +
53918                 ' sbot:' + sbot +
53919                 ' ch:' + ch  
53920                 );
53921         */
53922         if(ctop < stop){
53923              c.scrollTop = ctop;
53924             //Roo.log("set scrolltop to ctop DISABLE?");
53925         }else if(cbot > sbot){
53926             //Roo.log("set scrolltop to cbot-ch");
53927             c.scrollTop = cbot-ch;
53928         }
53929         
53930         if(hscroll !== false){
53931             if(cleft < sleft){
53932                 c.scrollLeft = cleft;
53933             }else if(cright > sright){
53934                 c.scrollLeft = cright-c.clientWidth;
53935             }
53936         }
53937          
53938         return el;
53939     },
53940
53941     updateColumns : function(){
53942         this.grid.stopEditing();
53943         var cm = this.grid.colModel, colIds = this.getColumnIds();
53944         //var totalWidth = cm.getTotalWidth();
53945         var pos = 0;
53946         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53947             //if(cm.isHidden(i)) continue;
53948             var w = cm.getColumnWidth(i);
53949             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53950             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53951         }
53952         this.updateSplitters();
53953     },
53954
53955     generateRules : function(cm){
53956         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53957         Roo.util.CSS.removeStyleSheet(rulesId);
53958         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53959             var cid = cm.getColumnId(i);
53960             var align = '';
53961             if(cm.config[i].align){
53962                 align = 'text-align:'+cm.config[i].align+';';
53963             }
53964             var hidden = '';
53965             if(cm.isHidden(i)){
53966                 hidden = 'display:none;';
53967             }
53968             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53969             ruleBuf.push(
53970                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53971                     this.hdSelector, cid, " {\n", align, width, "}\n",
53972                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53973                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53974         }
53975         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53976     },
53977
53978     updateSplitters : function(){
53979         var cm = this.cm, s = this.getSplitters();
53980         if(s){ // splitters not created yet
53981             var pos = 0, locked = true;
53982             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53983                 if(cm.isHidden(i)) continue;
53984                 var w = cm.getColumnWidth(i); // make sure it's a number
53985                 if(!cm.isLocked(i) && locked){
53986                     pos = 0;
53987                     locked = false;
53988                 }
53989                 pos += w;
53990                 s[i].style.left = (pos-this.splitOffset) + "px";
53991             }
53992         }
53993     },
53994
53995     handleHiddenChange : function(colModel, colIndex, hidden){
53996         if(hidden){
53997             this.hideColumn(colIndex);
53998         }else{
53999             this.unhideColumn(colIndex);
54000         }
54001     },
54002
54003     hideColumn : function(colIndex){
54004         var cid = this.getColumnId(colIndex);
54005         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54006         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54007         if(Roo.isSafari){
54008             this.updateHeaders();
54009         }
54010         this.updateSplitters();
54011         this.layout();
54012     },
54013
54014     unhideColumn : function(colIndex){
54015         var cid = this.getColumnId(colIndex);
54016         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54017         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54018
54019         if(Roo.isSafari){
54020             this.updateHeaders();
54021         }
54022         this.updateSplitters();
54023         this.layout();
54024     },
54025
54026     insertRows : function(dm, firstRow, lastRow, isUpdate){
54027         if(firstRow == 0 && lastRow == dm.getCount()-1){
54028             this.refresh();
54029         }else{
54030             if(!isUpdate){
54031                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54032             }
54033             var s = this.getScrollState();
54034             var markup = this.renderRows(firstRow, lastRow);
54035             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54036             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54037             this.restoreScroll(s);
54038             if(!isUpdate){
54039                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54040                 this.syncRowHeights(firstRow, lastRow);
54041                 this.stripeRows(firstRow);
54042                 this.layout();
54043             }
54044         }
54045     },
54046
54047     bufferRows : function(markup, target, index){
54048         var before = null, trows = target.rows, tbody = target.tBodies[0];
54049         if(index < trows.length){
54050             before = trows[index];
54051         }
54052         var b = document.createElement("div");
54053         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54054         var rows = b.firstChild.rows;
54055         for(var i = 0, len = rows.length; i < len; i++){
54056             if(before){
54057                 tbody.insertBefore(rows[0], before);
54058             }else{
54059                 tbody.appendChild(rows[0]);
54060             }
54061         }
54062         b.innerHTML = "";
54063         b = null;
54064     },
54065
54066     deleteRows : function(dm, firstRow, lastRow){
54067         if(dm.getRowCount()<1){
54068             this.fireEvent("beforerefresh", this);
54069             this.mainBody.update("");
54070             this.lockedBody.update("");
54071             this.fireEvent("refresh", this);
54072         }else{
54073             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54074             var bt = this.getBodyTable();
54075             var tbody = bt.firstChild;
54076             var rows = bt.rows;
54077             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54078                 tbody.removeChild(rows[firstRow]);
54079             }
54080             this.stripeRows(firstRow);
54081             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54082         }
54083     },
54084
54085     updateRows : function(dataSource, firstRow, lastRow){
54086         var s = this.getScrollState();
54087         this.refresh();
54088         this.restoreScroll(s);
54089     },
54090
54091     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54092         if(!noRefresh){
54093            this.refresh();
54094         }
54095         this.updateHeaderSortState();
54096     },
54097
54098     getScrollState : function(){
54099         
54100         var sb = this.scroller.dom;
54101         return {left: sb.scrollLeft, top: sb.scrollTop};
54102     },
54103
54104     stripeRows : function(startRow){
54105         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54106             return;
54107         }
54108         startRow = startRow || 0;
54109         var rows = this.getBodyTable().rows;
54110         var lrows = this.getLockedTable().rows;
54111         var cls = ' x-grid-row-alt ';
54112         for(var i = startRow, len = rows.length; i < len; i++){
54113             var row = rows[i], lrow = lrows[i];
54114             var isAlt = ((i+1) % 2 == 0);
54115             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54116             if(isAlt == hasAlt){
54117                 continue;
54118             }
54119             if(isAlt){
54120                 row.className += " x-grid-row-alt";
54121             }else{
54122                 row.className = row.className.replace("x-grid-row-alt", "");
54123             }
54124             if(lrow){
54125                 lrow.className = row.className;
54126             }
54127         }
54128     },
54129
54130     restoreScroll : function(state){
54131         //Roo.log('GridView.restoreScroll');
54132         var sb = this.scroller.dom;
54133         sb.scrollLeft = state.left;
54134         sb.scrollTop = state.top;
54135         this.syncScroll();
54136     },
54137
54138     syncScroll : function(){
54139         //Roo.log('GridView.syncScroll');
54140         var sb = this.scroller.dom;
54141         var sh = this.mainHd.dom;
54142         var bs = this.mainBody.dom;
54143         var lv = this.lockedBody.dom;
54144         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54145         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54146     },
54147
54148     handleScroll : function(e){
54149         this.syncScroll();
54150         var sb = this.scroller.dom;
54151         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54152         e.stopEvent();
54153     },
54154
54155     handleWheel : function(e){
54156         var d = e.getWheelDelta();
54157         this.scroller.dom.scrollTop -= d*22;
54158         // set this here to prevent jumpy scrolling on large tables
54159         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54160         e.stopEvent();
54161     },
54162
54163     renderRows : function(startRow, endRow){
54164         // pull in all the crap needed to render rows
54165         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54166         var colCount = cm.getColumnCount();
54167
54168         if(ds.getCount() < 1){
54169             return ["", ""];
54170         }
54171
54172         // build a map for all the columns
54173         var cs = [];
54174         for(var i = 0; i < colCount; i++){
54175             var name = cm.getDataIndex(i);
54176             cs[i] = {
54177                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54178                 renderer : cm.getRenderer(i),
54179                 id : cm.getColumnId(i),
54180                 locked : cm.isLocked(i)
54181             };
54182         }
54183
54184         startRow = startRow || 0;
54185         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54186
54187         // records to render
54188         var rs = ds.getRange(startRow, endRow);
54189
54190         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54191     },
54192
54193     // As much as I hate to duplicate code, this was branched because FireFox really hates
54194     // [].join("") on strings. The performance difference was substantial enough to
54195     // branch this function
54196     doRender : Roo.isGecko ?
54197             function(cs, rs, ds, startRow, colCount, stripe){
54198                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54199                 // buffers
54200                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54201                 
54202                 var hasListener = this.grid.hasListener('rowclass');
54203                 var rowcfg = {};
54204                 for(var j = 0, len = rs.length; j < len; j++){
54205                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54206                     for(var i = 0; i < colCount; i++){
54207                         c = cs[i];
54208                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54209                         p.id = c.id;
54210                         p.css = p.attr = "";
54211                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54212                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54213                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54214                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54215                         }
54216                         var markup = ct.apply(p);
54217                         if(!c.locked){
54218                             cb+= markup;
54219                         }else{
54220                             lcb+= markup;
54221                         }
54222                     }
54223                     var alt = [];
54224                     if(stripe && ((rowIndex+1) % 2 == 0)){
54225                         alt.push("x-grid-row-alt")
54226                     }
54227                     if(r.dirty){
54228                         alt.push(  " x-grid-dirty-row");
54229                     }
54230                     rp.cells = lcb;
54231                     if(this.getRowClass){
54232                         alt.push(this.getRowClass(r, rowIndex));
54233                     }
54234                     if (hasListener) {
54235                         rowcfg = {
54236                              
54237                             record: r,
54238                             rowIndex : rowIndex,
54239                             rowClass : ''
54240                         }
54241                         this.grid.fireEvent('rowclass', this, rowcfg);
54242                         alt.push(rowcfg.rowClass);
54243                     }
54244                     rp.alt = alt.join(" ");
54245                     lbuf+= rt.apply(rp);
54246                     rp.cells = cb;
54247                     buf+=  rt.apply(rp);
54248                 }
54249                 return [lbuf, buf];
54250             } :
54251             function(cs, rs, ds, startRow, colCount, stripe){
54252                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54253                 // buffers
54254                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54255                 var hasListener = this.grid.hasListener('rowclass');
54256  
54257                 var rowcfg = {};
54258                 for(var j = 0, len = rs.length; j < len; j++){
54259                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54260                     for(var i = 0; i < colCount; i++){
54261                         c = cs[i];
54262                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54263                         p.id = c.id;
54264                         p.css = p.attr = "";
54265                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54266                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54267                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54268                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54269                         }
54270                         
54271                         var markup = ct.apply(p);
54272                         if(!c.locked){
54273                             cb[cb.length] = markup;
54274                         }else{
54275                             lcb[lcb.length] = markup;
54276                         }
54277                     }
54278                     var alt = [];
54279                     if(stripe && ((rowIndex+1) % 2 == 0)){
54280                         alt.push( "x-grid-row-alt");
54281                     }
54282                     if(r.dirty){
54283                         alt.push(" x-grid-dirty-row");
54284                     }
54285                     rp.cells = lcb;
54286                     if(this.getRowClass){
54287                         alt.push( this.getRowClass(r, rowIndex));
54288                     }
54289                     if (hasListener) {
54290                         rowcfg = {
54291                              
54292                             record: r,
54293                             rowIndex : rowIndex,
54294                             rowClass : ''
54295                         }
54296                         this.grid.fireEvent('rowclass', this, rowcfg);
54297                         alt.push(rowcfg.rowClass);
54298                     }
54299                     rp.alt = alt.join(" ");
54300                     rp.cells = lcb.join("");
54301                     lbuf[lbuf.length] = rt.apply(rp);
54302                     rp.cells = cb.join("");
54303                     buf[buf.length] =  rt.apply(rp);
54304                 }
54305                 return [lbuf.join(""), buf.join("")];
54306             },
54307
54308     renderBody : function(){
54309         var markup = this.renderRows();
54310         var bt = this.templates.body;
54311         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54312     },
54313
54314     /**
54315      * Refreshes the grid
54316      * @param {Boolean} headersToo
54317      */
54318     refresh : function(headersToo){
54319         this.fireEvent("beforerefresh", this);
54320         this.grid.stopEditing();
54321         var result = this.renderBody();
54322         this.lockedBody.update(result[0]);
54323         this.mainBody.update(result[1]);
54324         if(headersToo === true){
54325             this.updateHeaders();
54326             this.updateColumns();
54327             this.updateSplitters();
54328             this.updateHeaderSortState();
54329         }
54330         this.syncRowHeights();
54331         this.layout();
54332         this.fireEvent("refresh", this);
54333     },
54334
54335     handleColumnMove : function(cm, oldIndex, newIndex){
54336         this.indexMap = null;
54337         var s = this.getScrollState();
54338         this.refresh(true);
54339         this.restoreScroll(s);
54340         this.afterMove(newIndex);
54341     },
54342
54343     afterMove : function(colIndex){
54344         if(this.enableMoveAnim && Roo.enableFx){
54345             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54346         }
54347         // if multisort - fix sortOrder, and reload..
54348         if (this.grid.dataSource.multiSort) {
54349             // the we can call sort again..
54350             var dm = this.grid.dataSource;
54351             var cm = this.grid.colModel;
54352             var so = [];
54353             for(var i = 0; i < cm.config.length; i++ ) {
54354                 
54355                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54356                     continue; // dont' bother, it's not in sort list or being set.
54357                 }
54358                 
54359                 so.push(cm.config[i].dataIndex);
54360             };
54361             dm.sortOrder = so;
54362             dm.load(dm.lastOptions);
54363             
54364             
54365         }
54366         
54367     },
54368
54369     updateCell : function(dm, rowIndex, dataIndex){
54370         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54371         if(typeof colIndex == "undefined"){ // not present in grid
54372             return;
54373         }
54374         var cm = this.grid.colModel;
54375         var cell = this.getCell(rowIndex, colIndex);
54376         var cellText = this.getCellText(rowIndex, colIndex);
54377
54378         var p = {
54379             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54380             id : cm.getColumnId(colIndex),
54381             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54382         };
54383         var renderer = cm.getRenderer(colIndex);
54384         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54385         if(typeof val == "undefined" || val === "") val = "&#160;";
54386         cellText.innerHTML = val;
54387         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54388         this.syncRowHeights(rowIndex, rowIndex);
54389     },
54390
54391     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54392         var maxWidth = 0;
54393         if(this.grid.autoSizeHeaders){
54394             var h = this.getHeaderCellMeasure(colIndex);
54395             maxWidth = Math.max(maxWidth, h.scrollWidth);
54396         }
54397         var tb, index;
54398         if(this.cm.isLocked(colIndex)){
54399             tb = this.getLockedTable();
54400             index = colIndex;
54401         }else{
54402             tb = this.getBodyTable();
54403             index = colIndex - this.cm.getLockedCount();
54404         }
54405         if(tb && tb.rows){
54406             var rows = tb.rows;
54407             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54408             for(var i = 0; i < stopIndex; i++){
54409                 var cell = rows[i].childNodes[index].firstChild;
54410                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54411             }
54412         }
54413         return maxWidth + /*margin for error in IE*/ 5;
54414     },
54415     /**
54416      * Autofit a column to its content.
54417      * @param {Number} colIndex
54418      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54419      */
54420      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54421          if(this.cm.isHidden(colIndex)){
54422              return; // can't calc a hidden column
54423          }
54424         if(forceMinSize){
54425             var cid = this.cm.getColumnId(colIndex);
54426             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54427            if(this.grid.autoSizeHeaders){
54428                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54429            }
54430         }
54431         var newWidth = this.calcColumnWidth(colIndex);
54432         this.cm.setColumnWidth(colIndex,
54433             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54434         if(!suppressEvent){
54435             this.grid.fireEvent("columnresize", colIndex, newWidth);
54436         }
54437     },
54438
54439     /**
54440      * Autofits all columns to their content and then expands to fit any extra space in the grid
54441      */
54442      autoSizeColumns : function(){
54443         var cm = this.grid.colModel;
54444         var colCount = cm.getColumnCount();
54445         for(var i = 0; i < colCount; i++){
54446             this.autoSizeColumn(i, true, true);
54447         }
54448         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54449             this.fitColumns();
54450         }else{
54451             this.updateColumns();
54452             this.layout();
54453         }
54454     },
54455
54456     /**
54457      * Autofits all columns to the grid's width proportionate with their current size
54458      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54459      */
54460     fitColumns : function(reserveScrollSpace){
54461         var cm = this.grid.colModel;
54462         var colCount = cm.getColumnCount();
54463         var cols = [];
54464         var width = 0;
54465         var i, w;
54466         for (i = 0; i < colCount; i++){
54467             if(!cm.isHidden(i) && !cm.isFixed(i)){
54468                 w = cm.getColumnWidth(i);
54469                 cols.push(i);
54470                 cols.push(w);
54471                 width += w;
54472             }
54473         }
54474         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54475         if(reserveScrollSpace){
54476             avail -= 17;
54477         }
54478         var frac = (avail - cm.getTotalWidth())/width;
54479         while (cols.length){
54480             w = cols.pop();
54481             i = cols.pop();
54482             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54483         }
54484         this.updateColumns();
54485         this.layout();
54486     },
54487
54488     onRowSelect : function(rowIndex){
54489         var row = this.getRowComposite(rowIndex);
54490         row.addClass("x-grid-row-selected");
54491     },
54492
54493     onRowDeselect : function(rowIndex){
54494         var row = this.getRowComposite(rowIndex);
54495         row.removeClass("x-grid-row-selected");
54496     },
54497
54498     onCellSelect : function(row, col){
54499         var cell = this.getCell(row, col);
54500         if(cell){
54501             Roo.fly(cell).addClass("x-grid-cell-selected");
54502         }
54503     },
54504
54505     onCellDeselect : function(row, col){
54506         var cell = this.getCell(row, col);
54507         if(cell){
54508             Roo.fly(cell).removeClass("x-grid-cell-selected");
54509         }
54510     },
54511
54512     updateHeaderSortState : function(){
54513         
54514         // sort state can be single { field: xxx, direction : yyy}
54515         // or   { xxx=>ASC , yyy : DESC ..... }
54516         
54517         var mstate = {};
54518         if (!this.ds.multiSort) { 
54519             var state = this.ds.getSortState();
54520             if(!state){
54521                 return;
54522             }
54523             mstate[state.field] = state.direction;
54524             // FIXME... - this is not used here.. but might be elsewhere..
54525             this.sortState = state;
54526             
54527         } else {
54528             mstate = this.ds.sortToggle;
54529         }
54530         //remove existing sort classes..
54531         
54532         var sc = this.sortClasses;
54533         var hds = this.el.select(this.headerSelector).removeClass(sc);
54534         
54535         for(var f in mstate) {
54536         
54537             var sortColumn = this.cm.findColumnIndex(f);
54538             
54539             if(sortColumn != -1){
54540                 var sortDir = mstate[f];        
54541                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54542             }
54543         }
54544         
54545          
54546         
54547     },
54548
54549
54550     handleHeaderClick : function(g, index,e){
54551         
54552         Roo.log("header click");
54553         
54554         if (Roo.isTouch) {
54555             // touch events on header are handled by context
54556             this.handleHdCtx(g,index,e);
54557             return;
54558         }
54559         
54560         
54561         if(this.headersDisabled){
54562             return;
54563         }
54564         var dm = g.dataSource, cm = g.colModel;
54565         if(!cm.isSortable(index)){
54566             return;
54567         }
54568         g.stopEditing();
54569         
54570         if (dm.multiSort) {
54571             // update the sortOrder
54572             var so = [];
54573             for(var i = 0; i < cm.config.length; i++ ) {
54574                 
54575                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54576                     continue; // dont' bother, it's not in sort list or being set.
54577                 }
54578                 
54579                 so.push(cm.config[i].dataIndex);
54580             };
54581             dm.sortOrder = so;
54582         }
54583         
54584         
54585         dm.sort(cm.getDataIndex(index));
54586     },
54587
54588
54589     destroy : function(){
54590         if(this.colMenu){
54591             this.colMenu.removeAll();
54592             Roo.menu.MenuMgr.unregister(this.colMenu);
54593             this.colMenu.getEl().remove();
54594             delete this.colMenu;
54595         }
54596         if(this.hmenu){
54597             this.hmenu.removeAll();
54598             Roo.menu.MenuMgr.unregister(this.hmenu);
54599             this.hmenu.getEl().remove();
54600             delete this.hmenu;
54601         }
54602         if(this.grid.enableColumnMove){
54603             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54604             if(dds){
54605                 for(var dd in dds){
54606                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54607                         var elid = dds[dd].dragElId;
54608                         dds[dd].unreg();
54609                         Roo.get(elid).remove();
54610                     } else if(dds[dd].config.isTarget){
54611                         dds[dd].proxyTop.remove();
54612                         dds[dd].proxyBottom.remove();
54613                         dds[dd].unreg();
54614                     }
54615                     if(Roo.dd.DDM.locationCache[dd]){
54616                         delete Roo.dd.DDM.locationCache[dd];
54617                     }
54618                 }
54619                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54620             }
54621         }
54622         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54623         this.bind(null, null);
54624         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54625     },
54626
54627     handleLockChange : function(){
54628         this.refresh(true);
54629     },
54630
54631     onDenyColumnLock : function(){
54632
54633     },
54634
54635     onDenyColumnHide : function(){
54636
54637     },
54638
54639     handleHdMenuClick : function(item){
54640         var index = this.hdCtxIndex;
54641         var cm = this.cm, ds = this.ds;
54642         switch(item.id){
54643             case "asc":
54644                 ds.sort(cm.getDataIndex(index), "ASC");
54645                 break;
54646             case "desc":
54647                 ds.sort(cm.getDataIndex(index), "DESC");
54648                 break;
54649             case "lock":
54650                 var lc = cm.getLockedCount();
54651                 if(cm.getColumnCount(true) <= lc+1){
54652                     this.onDenyColumnLock();
54653                     return;
54654                 }
54655                 if(lc != index){
54656                     cm.setLocked(index, true, true);
54657                     cm.moveColumn(index, lc);
54658                     this.grid.fireEvent("columnmove", index, lc);
54659                 }else{
54660                     cm.setLocked(index, true);
54661                 }
54662             break;
54663             case "unlock":
54664                 var lc = cm.getLockedCount();
54665                 if((lc-1) != index){
54666                     cm.setLocked(index, false, true);
54667                     cm.moveColumn(index, lc-1);
54668                     this.grid.fireEvent("columnmove", index, lc-1);
54669                 }else{
54670                     cm.setLocked(index, false);
54671                 }
54672             break;
54673             case 'wider': // used to expand cols on touch..
54674             case 'narrow':
54675                 var cw = cm.getColumnWidth(index);
54676                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54677                 cw = Math.max(0, cw);
54678                 cw = Math.min(cw,4000);
54679                 cm.setColumnWidth(index, cw);
54680                 break;
54681                 
54682             default:
54683                 index = cm.getIndexById(item.id.substr(4));
54684                 if(index != -1){
54685                     if(item.checked && cm.getColumnCount(true) <= 1){
54686                         this.onDenyColumnHide();
54687                         return false;
54688                     }
54689                     cm.setHidden(index, item.checked);
54690                 }
54691         }
54692         return true;
54693     },
54694
54695     beforeColMenuShow : function(){
54696         var cm = this.cm,  colCount = cm.getColumnCount();
54697         this.colMenu.removeAll();
54698         for(var i = 0; i < colCount; i++){
54699             this.colMenu.add(new Roo.menu.CheckItem({
54700                 id: "col-"+cm.getColumnId(i),
54701                 text: cm.getColumnHeader(i),
54702                 checked: !cm.isHidden(i),
54703                 hideOnClick:false
54704             }));
54705         }
54706     },
54707
54708     handleHdCtx : function(g, index, e){
54709         e.stopEvent();
54710         var hd = this.getHeaderCell(index);
54711         this.hdCtxIndex = index;
54712         var ms = this.hmenu.items, cm = this.cm;
54713         ms.get("asc").setDisabled(!cm.isSortable(index));
54714         ms.get("desc").setDisabled(!cm.isSortable(index));
54715         if(this.grid.enableColLock !== false){
54716             ms.get("lock").setDisabled(cm.isLocked(index));
54717             ms.get("unlock").setDisabled(!cm.isLocked(index));
54718         }
54719         this.hmenu.show(hd, "tl-bl");
54720     },
54721
54722     handleHdOver : function(e){
54723         var hd = this.findHeaderCell(e.getTarget());
54724         if(hd && !this.headersDisabled){
54725             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54726                this.fly(hd).addClass("x-grid-hd-over");
54727             }
54728         }
54729     },
54730
54731     handleHdOut : function(e){
54732         var hd = this.findHeaderCell(e.getTarget());
54733         if(hd){
54734             this.fly(hd).removeClass("x-grid-hd-over");
54735         }
54736     },
54737
54738     handleSplitDblClick : function(e, t){
54739         var i = this.getCellIndex(t);
54740         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54741             this.autoSizeColumn(i, true);
54742             this.layout();
54743         }
54744     },
54745
54746     render : function(){
54747
54748         var cm = this.cm;
54749         var colCount = cm.getColumnCount();
54750
54751         if(this.grid.monitorWindowResize === true){
54752             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54753         }
54754         var header = this.renderHeaders();
54755         var body = this.templates.body.apply({rows:""});
54756         var html = this.templates.master.apply({
54757             lockedBody: body,
54758             body: body,
54759             lockedHeader: header[0],
54760             header: header[1]
54761         });
54762
54763         //this.updateColumns();
54764
54765         this.grid.getGridEl().dom.innerHTML = html;
54766
54767         this.initElements();
54768         
54769         // a kludge to fix the random scolling effect in webkit
54770         this.el.on("scroll", function() {
54771             this.el.dom.scrollTop=0; // hopefully not recursive..
54772         },this);
54773
54774         this.scroller.on("scroll", this.handleScroll, this);
54775         this.lockedBody.on("mousewheel", this.handleWheel, this);
54776         this.mainBody.on("mousewheel", this.handleWheel, this);
54777
54778         this.mainHd.on("mouseover", this.handleHdOver, this);
54779         this.mainHd.on("mouseout", this.handleHdOut, this);
54780         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54781                 {delegate: "."+this.splitClass});
54782
54783         this.lockedHd.on("mouseover", this.handleHdOver, this);
54784         this.lockedHd.on("mouseout", this.handleHdOut, this);
54785         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54786                 {delegate: "."+this.splitClass});
54787
54788         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54789             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54790         }
54791
54792         this.updateSplitters();
54793
54794         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54795             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54796             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54797         }
54798
54799         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54800             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54801             this.hmenu.add(
54802                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54803                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54804             );
54805             if(this.grid.enableColLock !== false){
54806                 this.hmenu.add('-',
54807                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54808                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54809                 );
54810             }
54811             if (Roo.isTouch) {
54812                  this.hmenu.add('-',
54813                     {id:"wider", text: this.columnsWiderText},
54814                     {id:"narrow", text: this.columnsNarrowText }
54815                 );
54816                 
54817                  
54818             }
54819             
54820             if(this.grid.enableColumnHide !== false){
54821
54822                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54823                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54824                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54825
54826                 this.hmenu.add('-',
54827                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54828                 );
54829             }
54830             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54831
54832             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54833         }
54834
54835         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54836             this.dd = new Roo.grid.GridDragZone(this.grid, {
54837                 ddGroup : this.grid.ddGroup || 'GridDD'
54838             });
54839             
54840         }
54841
54842         /*
54843         for(var i = 0; i < colCount; i++){
54844             if(cm.isHidden(i)){
54845                 this.hideColumn(i);
54846             }
54847             if(cm.config[i].align){
54848                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54849                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54850             }
54851         }*/
54852         
54853         this.updateHeaderSortState();
54854
54855         this.beforeInitialResize();
54856         this.layout(true);
54857
54858         // two part rendering gives faster view to the user
54859         this.renderPhase2.defer(1, this);
54860     },
54861
54862     renderPhase2 : function(){
54863         // render the rows now
54864         this.refresh();
54865         if(this.grid.autoSizeColumns){
54866             this.autoSizeColumns();
54867         }
54868     },
54869
54870     beforeInitialResize : function(){
54871
54872     },
54873
54874     onColumnSplitterMoved : function(i, w){
54875         this.userResized = true;
54876         var cm = this.grid.colModel;
54877         cm.setColumnWidth(i, w, true);
54878         var cid = cm.getColumnId(i);
54879         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54880         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54881         this.updateSplitters();
54882         this.layout();
54883         this.grid.fireEvent("columnresize", i, w);
54884     },
54885
54886     syncRowHeights : function(startIndex, endIndex){
54887         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54888             startIndex = startIndex || 0;
54889             var mrows = this.getBodyTable().rows;
54890             var lrows = this.getLockedTable().rows;
54891             var len = mrows.length-1;
54892             endIndex = Math.min(endIndex || len, len);
54893             for(var i = startIndex; i <= endIndex; i++){
54894                 var m = mrows[i], l = lrows[i];
54895                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54896                 m.style.height = l.style.height = h + "px";
54897             }
54898         }
54899     },
54900
54901     layout : function(initialRender, is2ndPass){
54902         var g = this.grid;
54903         var auto = g.autoHeight;
54904         var scrollOffset = 16;
54905         var c = g.getGridEl(), cm = this.cm,
54906                 expandCol = g.autoExpandColumn,
54907                 gv = this;
54908         //c.beginMeasure();
54909
54910         if(!c.dom.offsetWidth){ // display:none?
54911             if(initialRender){
54912                 this.lockedWrap.show();
54913                 this.mainWrap.show();
54914             }
54915             return;
54916         }
54917
54918         var hasLock = this.cm.isLocked(0);
54919
54920         var tbh = this.headerPanel.getHeight();
54921         var bbh = this.footerPanel.getHeight();
54922
54923         if(auto){
54924             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54925             var newHeight = ch + c.getBorderWidth("tb");
54926             if(g.maxHeight){
54927                 newHeight = Math.min(g.maxHeight, newHeight);
54928             }
54929             c.setHeight(newHeight);
54930         }
54931
54932         if(g.autoWidth){
54933             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54934         }
54935
54936         var s = this.scroller;
54937
54938         var csize = c.getSize(true);
54939
54940         this.el.setSize(csize.width, csize.height);
54941
54942         this.headerPanel.setWidth(csize.width);
54943         this.footerPanel.setWidth(csize.width);
54944
54945         var hdHeight = this.mainHd.getHeight();
54946         var vw = csize.width;
54947         var vh = csize.height - (tbh + bbh);
54948
54949         s.setSize(vw, vh);
54950
54951         var bt = this.getBodyTable();
54952         var ltWidth = hasLock ?
54953                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54954
54955         var scrollHeight = bt.offsetHeight;
54956         var scrollWidth = ltWidth + bt.offsetWidth;
54957         var vscroll = false, hscroll = false;
54958
54959         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54960
54961         var lw = this.lockedWrap, mw = this.mainWrap;
54962         var lb = this.lockedBody, mb = this.mainBody;
54963
54964         setTimeout(function(){
54965             var t = s.dom.offsetTop;
54966             var w = s.dom.clientWidth,
54967                 h = s.dom.clientHeight;
54968
54969             lw.setTop(t);
54970             lw.setSize(ltWidth, h);
54971
54972             mw.setLeftTop(ltWidth, t);
54973             mw.setSize(w-ltWidth, h);
54974
54975             lb.setHeight(h-hdHeight);
54976             mb.setHeight(h-hdHeight);
54977
54978             if(is2ndPass !== true && !gv.userResized && expandCol){
54979                 // high speed resize without full column calculation
54980                 
54981                 var ci = cm.getIndexById(expandCol);
54982                 if (ci < 0) {
54983                     ci = cm.findColumnIndex(expandCol);
54984                 }
54985                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54986                 var expandId = cm.getColumnId(ci);
54987                 var  tw = cm.getTotalWidth(false);
54988                 var currentWidth = cm.getColumnWidth(ci);
54989                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54990                 if(currentWidth != cw){
54991                     cm.setColumnWidth(ci, cw, true);
54992                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54993                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54994                     gv.updateSplitters();
54995                     gv.layout(false, true);
54996                 }
54997             }
54998
54999             if(initialRender){
55000                 lw.show();
55001                 mw.show();
55002             }
55003             //c.endMeasure();
55004         }, 10);
55005     },
55006
55007     onWindowResize : function(){
55008         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55009             return;
55010         }
55011         this.layout();
55012     },
55013
55014     appendFooter : function(parentEl){
55015         return null;
55016     },
55017
55018     sortAscText : "Sort Ascending",
55019     sortDescText : "Sort Descending",
55020     lockText : "Lock Column",
55021     unlockText : "Unlock Column",
55022     columnsText : "Columns",
55023  
55024     columnsWiderText : "Wider",
55025     columnsNarrowText : "Thinner"
55026 });
55027
55028
55029 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55030     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55031     this.proxy.el.addClass('x-grid3-col-dd');
55032 };
55033
55034 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55035     handleMouseDown : function(e){
55036
55037     },
55038
55039     callHandleMouseDown : function(e){
55040         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55041     }
55042 });
55043 /*
55044  * Based on:
55045  * Ext JS Library 1.1.1
55046  * Copyright(c) 2006-2007, Ext JS, LLC.
55047  *
55048  * Originally Released Under LGPL - original licence link has changed is not relivant.
55049  *
55050  * Fork - LGPL
55051  * <script type="text/javascript">
55052  */
55053  
55054 // private
55055 // This is a support class used internally by the Grid components
55056 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55057     this.grid = grid;
55058     this.view = grid.getView();
55059     this.proxy = this.view.resizeProxy;
55060     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55061         "gridSplitters" + this.grid.getGridEl().id, {
55062         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55063     });
55064     this.setHandleElId(Roo.id(hd));
55065     this.setOuterHandleElId(Roo.id(hd2));
55066     this.scroll = false;
55067 };
55068 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55069     fly: Roo.Element.fly,
55070
55071     b4StartDrag : function(x, y){
55072         this.view.headersDisabled = true;
55073         this.proxy.setHeight(this.view.mainWrap.getHeight());
55074         var w = this.cm.getColumnWidth(this.cellIndex);
55075         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55076         this.resetConstraints();
55077         this.setXConstraint(minw, 1000);
55078         this.setYConstraint(0, 0);
55079         this.minX = x - minw;
55080         this.maxX = x + 1000;
55081         this.startPos = x;
55082         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55083     },
55084
55085
55086     handleMouseDown : function(e){
55087         ev = Roo.EventObject.setEvent(e);
55088         var t = this.fly(ev.getTarget());
55089         if(t.hasClass("x-grid-split")){
55090             this.cellIndex = this.view.getCellIndex(t.dom);
55091             this.split = t.dom;
55092             this.cm = this.grid.colModel;
55093             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55094                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55095             }
55096         }
55097     },
55098
55099     endDrag : function(e){
55100         this.view.headersDisabled = false;
55101         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55102         var diff = endX - this.startPos;
55103         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55104     },
55105
55106     autoOffset : function(){
55107         this.setDelta(0,0);
55108     }
55109 });/*
55110  * Based on:
55111  * Ext JS Library 1.1.1
55112  * Copyright(c) 2006-2007, Ext JS, LLC.
55113  *
55114  * Originally Released Under LGPL - original licence link has changed is not relivant.
55115  *
55116  * Fork - LGPL
55117  * <script type="text/javascript">
55118  */
55119  
55120 // private
55121 // This is a support class used internally by the Grid components
55122 Roo.grid.GridDragZone = function(grid, config){
55123     this.view = grid.getView();
55124     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55125     if(this.view.lockedBody){
55126         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55127         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55128     }
55129     this.scroll = false;
55130     this.grid = grid;
55131     this.ddel = document.createElement('div');
55132     this.ddel.className = 'x-grid-dd-wrap';
55133 };
55134
55135 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55136     ddGroup : "GridDD",
55137
55138     getDragData : function(e){
55139         var t = Roo.lib.Event.getTarget(e);
55140         var rowIndex = this.view.findRowIndex(t);
55141         var sm = this.grid.selModel;
55142             
55143         //Roo.log(rowIndex);
55144         
55145         if (sm.getSelectedCell) {
55146             // cell selection..
55147             if (!sm.getSelectedCell()) {
55148                 return false;
55149             }
55150             if (rowIndex != sm.getSelectedCell()[0]) {
55151                 return false;
55152             }
55153         
55154         }
55155         
55156         if(rowIndex !== false){
55157             
55158             // if editorgrid.. 
55159             
55160             
55161             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55162                
55163             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55164               //  
55165             //}
55166             if (e.hasModifier()){
55167                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55168             }
55169             
55170             Roo.log("getDragData ????");
55171             
55172             return {
55173                 grid: this.grid,
55174                 ddel: this.ddel,
55175                 rowIndex: rowIndex,
55176                 selections:sm.getSelections ? sm.getSelections() : (
55177                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55178                 )
55179             };
55180         }
55181         return false;
55182     },
55183
55184     onInitDrag : function(e){
55185         var data = this.dragData;
55186         this.ddel.innerHTML = this.grid.getDragDropText();
55187         this.proxy.update(this.ddel);
55188         // fire start drag?
55189     },
55190
55191     afterRepair : function(){
55192         this.dragging = false;
55193     },
55194
55195     getRepairXY : function(e, data){
55196         return false;
55197     },
55198
55199     onEndDrag : function(data, e){
55200         // fire end drag?
55201     },
55202
55203     onValidDrop : function(dd, e, id){
55204         // fire drag drop?
55205         this.hideProxy();
55206     },
55207
55208     beforeInvalidDrop : function(e, id){
55209
55210     }
55211 });/*
55212  * Based on:
55213  * Ext JS Library 1.1.1
55214  * Copyright(c) 2006-2007, Ext JS, LLC.
55215  *
55216  * Originally Released Under LGPL - original licence link has changed is not relivant.
55217  *
55218  * Fork - LGPL
55219  * <script type="text/javascript">
55220  */
55221  
55222
55223 /**
55224  * @class Roo.grid.ColumnModel
55225  * @extends Roo.util.Observable
55226  * This is the default implementation of a ColumnModel used by the Grid. It defines
55227  * the columns in the grid.
55228  * <br>Usage:<br>
55229  <pre><code>
55230  var colModel = new Roo.grid.ColumnModel([
55231         {header: "Ticker", width: 60, sortable: true, locked: true},
55232         {header: "Company Name", width: 150, sortable: true},
55233         {header: "Market Cap.", width: 100, sortable: true},
55234         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55235         {header: "Employees", width: 100, sortable: true, resizable: false}
55236  ]);
55237  </code></pre>
55238  * <p>
55239  
55240  * The config options listed for this class are options which may appear in each
55241  * individual column definition.
55242  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55243  * @constructor
55244  * @param {Object} config An Array of column config objects. See this class's
55245  * config objects for details.
55246 */
55247 Roo.grid.ColumnModel = function(config){
55248         /**
55249      * The config passed into the constructor
55250      */
55251     this.config = config;
55252     this.lookup = {};
55253
55254     // if no id, create one
55255     // if the column does not have a dataIndex mapping,
55256     // map it to the order it is in the config
55257     for(var i = 0, len = config.length; i < len; i++){
55258         var c = config[i];
55259         if(typeof c.dataIndex == "undefined"){
55260             c.dataIndex = i;
55261         }
55262         if(typeof c.renderer == "string"){
55263             c.renderer = Roo.util.Format[c.renderer];
55264         }
55265         if(typeof c.id == "undefined"){
55266             c.id = Roo.id();
55267         }
55268         if(c.editor && c.editor.xtype){
55269             c.editor  = Roo.factory(c.editor, Roo.grid);
55270         }
55271         if(c.editor && c.editor.isFormField){
55272             c.editor = new Roo.grid.GridEditor(c.editor);
55273         }
55274         this.lookup[c.id] = c;
55275     }
55276
55277     /**
55278      * The width of columns which have no width specified (defaults to 100)
55279      * @type Number
55280      */
55281     this.defaultWidth = 100;
55282
55283     /**
55284      * Default sortable of columns which have no sortable specified (defaults to false)
55285      * @type Boolean
55286      */
55287     this.defaultSortable = false;
55288
55289     this.addEvents({
55290         /**
55291              * @event widthchange
55292              * Fires when the width of a column changes.
55293              * @param {ColumnModel} this
55294              * @param {Number} columnIndex The column index
55295              * @param {Number} newWidth The new width
55296              */
55297             "widthchange": true,
55298         /**
55299              * @event headerchange
55300              * Fires when the text of a header changes.
55301              * @param {ColumnModel} this
55302              * @param {Number} columnIndex The column index
55303              * @param {Number} newText The new header text
55304              */
55305             "headerchange": true,
55306         /**
55307              * @event hiddenchange
55308              * Fires when a column is hidden or "unhidden".
55309              * @param {ColumnModel} this
55310              * @param {Number} columnIndex The column index
55311              * @param {Boolean} hidden true if hidden, false otherwise
55312              */
55313             "hiddenchange": true,
55314             /**
55315          * @event columnmoved
55316          * Fires when a column is moved.
55317          * @param {ColumnModel} this
55318          * @param {Number} oldIndex
55319          * @param {Number} newIndex
55320          */
55321         "columnmoved" : true,
55322         /**
55323          * @event columlockchange
55324          * Fires when a column's locked state is changed
55325          * @param {ColumnModel} this
55326          * @param {Number} colIndex
55327          * @param {Boolean} locked true if locked
55328          */
55329         "columnlockchange" : true
55330     });
55331     Roo.grid.ColumnModel.superclass.constructor.call(this);
55332 };
55333 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55334     /**
55335      * @cfg {String} header The header text to display in the Grid view.
55336      */
55337     /**
55338      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55339      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55340      * specified, the column's index is used as an index into the Record's data Array.
55341      */
55342     /**
55343      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55344      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55345      */
55346     /**
55347      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55348      * Defaults to the value of the {@link #defaultSortable} property.
55349      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55350      */
55351     /**
55352      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55353      */
55354     /**
55355      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55356      */
55357     /**
55358      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55359      */
55360     /**
55361      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55362      */
55363     /**
55364      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55365      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55366      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55367      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55368      */
55369        /**
55370      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55371      */
55372     /**
55373      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55374      */
55375     /**
55376      * @cfg {String} cursor (Optional)
55377      */
55378     /**
55379      * @cfg {String} tooltip (Optional)
55380      */
55381     /**
55382      * Returns the id of the column at the specified index.
55383      * @param {Number} index The column index
55384      * @return {String} the id
55385      */
55386     getColumnId : function(index){
55387         return this.config[index].id;
55388     },
55389
55390     /**
55391      * Returns the column for a specified id.
55392      * @param {String} id The column id
55393      * @return {Object} the column
55394      */
55395     getColumnById : function(id){
55396         return this.lookup[id];
55397     },
55398
55399     
55400     /**
55401      * Returns the column for a specified dataIndex.
55402      * @param {String} dataIndex The column dataIndex
55403      * @return {Object|Boolean} the column or false if not found
55404      */
55405     getColumnByDataIndex: function(dataIndex){
55406         var index = this.findColumnIndex(dataIndex);
55407         return index > -1 ? this.config[index] : false;
55408     },
55409     
55410     /**
55411      * Returns the index for a specified column id.
55412      * @param {String} id The column id
55413      * @return {Number} the index, or -1 if not found
55414      */
55415     getIndexById : function(id){
55416         for(var i = 0, len = this.config.length; i < len; i++){
55417             if(this.config[i].id == id){
55418                 return i;
55419             }
55420         }
55421         return -1;
55422     },
55423     
55424     /**
55425      * Returns the index for a specified column dataIndex.
55426      * @param {String} dataIndex The column dataIndex
55427      * @return {Number} the index, or -1 if not found
55428      */
55429     
55430     findColumnIndex : function(dataIndex){
55431         for(var i = 0, len = this.config.length; i < len; i++){
55432             if(this.config[i].dataIndex == dataIndex){
55433                 return i;
55434             }
55435         }
55436         return -1;
55437     },
55438     
55439     
55440     moveColumn : function(oldIndex, newIndex){
55441         var c = this.config[oldIndex];
55442         this.config.splice(oldIndex, 1);
55443         this.config.splice(newIndex, 0, c);
55444         this.dataMap = null;
55445         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55446     },
55447
55448     isLocked : function(colIndex){
55449         return this.config[colIndex].locked === true;
55450     },
55451
55452     setLocked : function(colIndex, value, suppressEvent){
55453         if(this.isLocked(colIndex) == value){
55454             return;
55455         }
55456         this.config[colIndex].locked = value;
55457         if(!suppressEvent){
55458             this.fireEvent("columnlockchange", this, colIndex, value);
55459         }
55460     },
55461
55462     getTotalLockedWidth : function(){
55463         var totalWidth = 0;
55464         for(var i = 0; i < this.config.length; i++){
55465             if(this.isLocked(i) && !this.isHidden(i)){
55466                 this.totalWidth += this.getColumnWidth(i);
55467             }
55468         }
55469         return totalWidth;
55470     },
55471
55472     getLockedCount : function(){
55473         for(var i = 0, len = this.config.length; i < len; i++){
55474             if(!this.isLocked(i)){
55475                 return i;
55476             }
55477         }
55478     },
55479
55480     /**
55481      * Returns the number of columns.
55482      * @return {Number}
55483      */
55484     getColumnCount : function(visibleOnly){
55485         if(visibleOnly === true){
55486             var c = 0;
55487             for(var i = 0, len = this.config.length; i < len; i++){
55488                 if(!this.isHidden(i)){
55489                     c++;
55490                 }
55491             }
55492             return c;
55493         }
55494         return this.config.length;
55495     },
55496
55497     /**
55498      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55499      * @param {Function} fn
55500      * @param {Object} scope (optional)
55501      * @return {Array} result
55502      */
55503     getColumnsBy : function(fn, scope){
55504         var r = [];
55505         for(var i = 0, len = this.config.length; i < len; i++){
55506             var c = this.config[i];
55507             if(fn.call(scope||this, c, i) === true){
55508                 r[r.length] = c;
55509             }
55510         }
55511         return r;
55512     },
55513
55514     /**
55515      * Returns true if the specified column is sortable.
55516      * @param {Number} col The column index
55517      * @return {Boolean}
55518      */
55519     isSortable : function(col){
55520         if(typeof this.config[col].sortable == "undefined"){
55521             return this.defaultSortable;
55522         }
55523         return this.config[col].sortable;
55524     },
55525
55526     /**
55527      * Returns the rendering (formatting) function defined for the column.
55528      * @param {Number} col The column index.
55529      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55530      */
55531     getRenderer : function(col){
55532         if(!this.config[col].renderer){
55533             return Roo.grid.ColumnModel.defaultRenderer;
55534         }
55535         return this.config[col].renderer;
55536     },
55537
55538     /**
55539      * Sets the rendering (formatting) function for a column.
55540      * @param {Number} col The column index
55541      * @param {Function} fn The function to use to process the cell's raw data
55542      * to return HTML markup for the grid view. The render function is called with
55543      * the following parameters:<ul>
55544      * <li>Data value.</li>
55545      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55546      * <li>css A CSS style string to apply to the table cell.</li>
55547      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55548      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55549      * <li>Row index</li>
55550      * <li>Column index</li>
55551      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55552      */
55553     setRenderer : function(col, fn){
55554         this.config[col].renderer = fn;
55555     },
55556
55557     /**
55558      * Returns the width for the specified column.
55559      * @param {Number} col The column index
55560      * @return {Number}
55561      */
55562     getColumnWidth : function(col){
55563         return this.config[col].width * 1 || this.defaultWidth;
55564     },
55565
55566     /**
55567      * Sets the width for a column.
55568      * @param {Number} col The column index
55569      * @param {Number} width The new width
55570      */
55571     setColumnWidth : function(col, width, suppressEvent){
55572         this.config[col].width = width;
55573         this.totalWidth = null;
55574         if(!suppressEvent){
55575              this.fireEvent("widthchange", this, col, width);
55576         }
55577     },
55578
55579     /**
55580      * Returns the total width of all columns.
55581      * @param {Boolean} includeHidden True to include hidden column widths
55582      * @return {Number}
55583      */
55584     getTotalWidth : function(includeHidden){
55585         if(!this.totalWidth){
55586             this.totalWidth = 0;
55587             for(var i = 0, len = this.config.length; i < len; i++){
55588                 if(includeHidden || !this.isHidden(i)){
55589                     this.totalWidth += this.getColumnWidth(i);
55590                 }
55591             }
55592         }
55593         return this.totalWidth;
55594     },
55595
55596     /**
55597      * Returns the header for the specified column.
55598      * @param {Number} col The column index
55599      * @return {String}
55600      */
55601     getColumnHeader : function(col){
55602         return this.config[col].header;
55603     },
55604
55605     /**
55606      * Sets the header for a column.
55607      * @param {Number} col The column index
55608      * @param {String} header The new header
55609      */
55610     setColumnHeader : function(col, header){
55611         this.config[col].header = header;
55612         this.fireEvent("headerchange", this, col, header);
55613     },
55614
55615     /**
55616      * Returns the tooltip for the specified column.
55617      * @param {Number} col The column index
55618      * @return {String}
55619      */
55620     getColumnTooltip : function(col){
55621             return this.config[col].tooltip;
55622     },
55623     /**
55624      * Sets the tooltip for a column.
55625      * @param {Number} col The column index
55626      * @param {String} tooltip The new tooltip
55627      */
55628     setColumnTooltip : function(col, tooltip){
55629             this.config[col].tooltip = tooltip;
55630     },
55631
55632     /**
55633      * Returns the dataIndex for the specified column.
55634      * @param {Number} col The column index
55635      * @return {Number}
55636      */
55637     getDataIndex : function(col){
55638         return this.config[col].dataIndex;
55639     },
55640
55641     /**
55642      * Sets the dataIndex for a column.
55643      * @param {Number} col The column index
55644      * @param {Number} dataIndex The new dataIndex
55645      */
55646     setDataIndex : function(col, dataIndex){
55647         this.config[col].dataIndex = dataIndex;
55648     },
55649
55650     
55651     
55652     /**
55653      * Returns true if the cell is editable.
55654      * @param {Number} colIndex The column index
55655      * @param {Number} rowIndex The row index
55656      * @return {Boolean}
55657      */
55658     isCellEditable : function(colIndex, rowIndex){
55659         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55660     },
55661
55662     /**
55663      * Returns the editor defined for the cell/column.
55664      * return false or null to disable editing.
55665      * @param {Number} colIndex The column index
55666      * @param {Number} rowIndex The row index
55667      * @return {Object}
55668      */
55669     getCellEditor : function(colIndex, rowIndex){
55670         return this.config[colIndex].editor;
55671     },
55672
55673     /**
55674      * Sets if a column is editable.
55675      * @param {Number} col The column index
55676      * @param {Boolean} editable True if the column is editable
55677      */
55678     setEditable : function(col, editable){
55679         this.config[col].editable = editable;
55680     },
55681
55682
55683     /**
55684      * Returns true if the column is hidden.
55685      * @param {Number} colIndex The column index
55686      * @return {Boolean}
55687      */
55688     isHidden : function(colIndex){
55689         return this.config[colIndex].hidden;
55690     },
55691
55692
55693     /**
55694      * Returns true if the column width cannot be changed
55695      */
55696     isFixed : function(colIndex){
55697         return this.config[colIndex].fixed;
55698     },
55699
55700     /**
55701      * Returns true if the column can be resized
55702      * @return {Boolean}
55703      */
55704     isResizable : function(colIndex){
55705         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55706     },
55707     /**
55708      * Sets if a column is hidden.
55709      * @param {Number} colIndex The column index
55710      * @param {Boolean} hidden True if the column is hidden
55711      */
55712     setHidden : function(colIndex, hidden){
55713         this.config[colIndex].hidden = hidden;
55714         this.totalWidth = null;
55715         this.fireEvent("hiddenchange", this, colIndex, hidden);
55716     },
55717
55718     /**
55719      * Sets the editor for a column.
55720      * @param {Number} col The column index
55721      * @param {Object} editor The editor object
55722      */
55723     setEditor : function(col, editor){
55724         this.config[col].editor = editor;
55725     }
55726 });
55727
55728 Roo.grid.ColumnModel.defaultRenderer = function(value){
55729         if(typeof value == "string" && value.length < 1){
55730             return "&#160;";
55731         }
55732         return value;
55733 };
55734
55735 // Alias for backwards compatibility
55736 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55737 /*
55738  * Based on:
55739  * Ext JS Library 1.1.1
55740  * Copyright(c) 2006-2007, Ext JS, LLC.
55741  *
55742  * Originally Released Under LGPL - original licence link has changed is not relivant.
55743  *
55744  * Fork - LGPL
55745  * <script type="text/javascript">
55746  */
55747
55748 /**
55749  * @class Roo.grid.AbstractSelectionModel
55750  * @extends Roo.util.Observable
55751  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55752  * implemented by descendant classes.  This class should not be directly instantiated.
55753  * @constructor
55754  */
55755 Roo.grid.AbstractSelectionModel = function(){
55756     this.locked = false;
55757     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55758 };
55759
55760 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55761     /** @ignore Called by the grid automatically. Do not call directly. */
55762     init : function(grid){
55763         this.grid = grid;
55764         this.initEvents();
55765     },
55766
55767     /**
55768      * Locks the selections.
55769      */
55770     lock : function(){
55771         this.locked = true;
55772     },
55773
55774     /**
55775      * Unlocks the selections.
55776      */
55777     unlock : function(){
55778         this.locked = false;
55779     },
55780
55781     /**
55782      * Returns true if the selections are locked.
55783      * @return {Boolean}
55784      */
55785     isLocked : function(){
55786         return this.locked;
55787     }
55788 });/*
55789  * Based on:
55790  * Ext JS Library 1.1.1
55791  * Copyright(c) 2006-2007, Ext JS, LLC.
55792  *
55793  * Originally Released Under LGPL - original licence link has changed is not relivant.
55794  *
55795  * Fork - LGPL
55796  * <script type="text/javascript">
55797  */
55798 /**
55799  * @extends Roo.grid.AbstractSelectionModel
55800  * @class Roo.grid.RowSelectionModel
55801  * The default SelectionModel used by {@link Roo.grid.Grid}.
55802  * It supports multiple selections and keyboard selection/navigation. 
55803  * @constructor
55804  * @param {Object} config
55805  */
55806 Roo.grid.RowSelectionModel = function(config){
55807     Roo.apply(this, config);
55808     this.selections = new Roo.util.MixedCollection(false, function(o){
55809         return o.id;
55810     });
55811
55812     this.last = false;
55813     this.lastActive = false;
55814
55815     this.addEvents({
55816         /**
55817              * @event selectionchange
55818              * Fires when the selection changes
55819              * @param {SelectionModel} this
55820              */
55821             "selectionchange" : true,
55822         /**
55823              * @event afterselectionchange
55824              * Fires after the selection changes (eg. by key press or clicking)
55825              * @param {SelectionModel} this
55826              */
55827             "afterselectionchange" : true,
55828         /**
55829              * @event beforerowselect
55830              * Fires when a row is selected being selected, return false to cancel.
55831              * @param {SelectionModel} this
55832              * @param {Number} rowIndex The selected index
55833              * @param {Boolean} keepExisting False if other selections will be cleared
55834              */
55835             "beforerowselect" : true,
55836         /**
55837              * @event rowselect
55838              * Fires when a row is selected.
55839              * @param {SelectionModel} this
55840              * @param {Number} rowIndex The selected index
55841              * @param {Roo.data.Record} r The record
55842              */
55843             "rowselect" : true,
55844         /**
55845              * @event rowdeselect
55846              * Fires when a row is deselected.
55847              * @param {SelectionModel} this
55848              * @param {Number} rowIndex The selected index
55849              */
55850         "rowdeselect" : true
55851     });
55852     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55853     this.locked = false;
55854 };
55855
55856 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55857     /**
55858      * @cfg {Boolean} singleSelect
55859      * True to allow selection of only one row at a time (defaults to false)
55860      */
55861     singleSelect : false,
55862
55863     // private
55864     initEvents : function(){
55865
55866         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55867             this.grid.on("mousedown", this.handleMouseDown, this);
55868         }else{ // allow click to work like normal
55869             this.grid.on("rowclick", this.handleDragableRowClick, this);
55870         }
55871
55872         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55873             "up" : function(e){
55874                 if(!e.shiftKey){
55875                     this.selectPrevious(e.shiftKey);
55876                 }else if(this.last !== false && this.lastActive !== false){
55877                     var last = this.last;
55878                     this.selectRange(this.last,  this.lastActive-1);
55879                     this.grid.getView().focusRow(this.lastActive);
55880                     if(last !== false){
55881                         this.last = last;
55882                     }
55883                 }else{
55884                     this.selectFirstRow();
55885                 }
55886                 this.fireEvent("afterselectionchange", this);
55887             },
55888             "down" : function(e){
55889                 if(!e.shiftKey){
55890                     this.selectNext(e.shiftKey);
55891                 }else if(this.last !== false && this.lastActive !== false){
55892                     var last = this.last;
55893                     this.selectRange(this.last,  this.lastActive+1);
55894                     this.grid.getView().focusRow(this.lastActive);
55895                     if(last !== false){
55896                         this.last = last;
55897                     }
55898                 }else{
55899                     this.selectFirstRow();
55900                 }
55901                 this.fireEvent("afterselectionchange", this);
55902             },
55903             scope: this
55904         });
55905
55906         var view = this.grid.view;
55907         view.on("refresh", this.onRefresh, this);
55908         view.on("rowupdated", this.onRowUpdated, this);
55909         view.on("rowremoved", this.onRemove, this);
55910     },
55911
55912     // private
55913     onRefresh : function(){
55914         var ds = this.grid.dataSource, i, v = this.grid.view;
55915         var s = this.selections;
55916         s.each(function(r){
55917             if((i = ds.indexOfId(r.id)) != -1){
55918                 v.onRowSelect(i);
55919             }else{
55920                 s.remove(r);
55921             }
55922         });
55923     },
55924
55925     // private
55926     onRemove : function(v, index, r){
55927         this.selections.remove(r);
55928     },
55929
55930     // private
55931     onRowUpdated : function(v, index, r){
55932         if(this.isSelected(r)){
55933             v.onRowSelect(index);
55934         }
55935     },
55936
55937     /**
55938      * Select records.
55939      * @param {Array} records The records to select
55940      * @param {Boolean} keepExisting (optional) True to keep existing selections
55941      */
55942     selectRecords : function(records, keepExisting){
55943         if(!keepExisting){
55944             this.clearSelections();
55945         }
55946         var ds = this.grid.dataSource;
55947         for(var i = 0, len = records.length; i < len; i++){
55948             this.selectRow(ds.indexOf(records[i]), true);
55949         }
55950     },
55951
55952     /**
55953      * Gets the number of selected rows.
55954      * @return {Number}
55955      */
55956     getCount : function(){
55957         return this.selections.length;
55958     },
55959
55960     /**
55961      * Selects the first row in the grid.
55962      */
55963     selectFirstRow : function(){
55964         this.selectRow(0);
55965     },
55966
55967     /**
55968      * Select the last row.
55969      * @param {Boolean} keepExisting (optional) True to keep existing selections
55970      */
55971     selectLastRow : function(keepExisting){
55972         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55973     },
55974
55975     /**
55976      * Selects the row immediately following the last selected row.
55977      * @param {Boolean} keepExisting (optional) True to keep existing selections
55978      */
55979     selectNext : function(keepExisting){
55980         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55981             this.selectRow(this.last+1, keepExisting);
55982             this.grid.getView().focusRow(this.last);
55983         }
55984     },
55985
55986     /**
55987      * Selects the row that precedes the last selected row.
55988      * @param {Boolean} keepExisting (optional) True to keep existing selections
55989      */
55990     selectPrevious : function(keepExisting){
55991         if(this.last){
55992             this.selectRow(this.last-1, keepExisting);
55993             this.grid.getView().focusRow(this.last);
55994         }
55995     },
55996
55997     /**
55998      * Returns the selected records
55999      * @return {Array} Array of selected records
56000      */
56001     getSelections : function(){
56002         return [].concat(this.selections.items);
56003     },
56004
56005     /**
56006      * Returns the first selected record.
56007      * @return {Record}
56008      */
56009     getSelected : function(){
56010         return this.selections.itemAt(0);
56011     },
56012
56013
56014     /**
56015      * Clears all selections.
56016      */
56017     clearSelections : function(fast){
56018         if(this.locked) return;
56019         if(fast !== true){
56020             var ds = this.grid.dataSource;
56021             var s = this.selections;
56022             s.each(function(r){
56023                 this.deselectRow(ds.indexOfId(r.id));
56024             }, this);
56025             s.clear();
56026         }else{
56027             this.selections.clear();
56028         }
56029         this.last = false;
56030     },
56031
56032
56033     /**
56034      * Selects all rows.
56035      */
56036     selectAll : function(){
56037         if(this.locked) return;
56038         this.selections.clear();
56039         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56040             this.selectRow(i, true);
56041         }
56042     },
56043
56044     /**
56045      * Returns True if there is a selection.
56046      * @return {Boolean}
56047      */
56048     hasSelection : function(){
56049         return this.selections.length > 0;
56050     },
56051
56052     /**
56053      * Returns True if the specified row is selected.
56054      * @param {Number/Record} record The record or index of the record to check
56055      * @return {Boolean}
56056      */
56057     isSelected : function(index){
56058         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56059         return (r && this.selections.key(r.id) ? true : false);
56060     },
56061
56062     /**
56063      * Returns True if the specified record id is selected.
56064      * @param {String} id The id of record to check
56065      * @return {Boolean}
56066      */
56067     isIdSelected : function(id){
56068         return (this.selections.key(id) ? true : false);
56069     },
56070
56071     // private
56072     handleMouseDown : function(e, t){
56073         var view = this.grid.getView(), rowIndex;
56074         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56075             return;
56076         };
56077         if(e.shiftKey && this.last !== false){
56078             var last = this.last;
56079             this.selectRange(last, rowIndex, e.ctrlKey);
56080             this.last = last; // reset the last
56081             view.focusRow(rowIndex);
56082         }else{
56083             var isSelected = this.isSelected(rowIndex);
56084             if(e.button !== 0 && isSelected){
56085                 view.focusRow(rowIndex);
56086             }else if(e.ctrlKey && isSelected){
56087                 this.deselectRow(rowIndex);
56088             }else if(!isSelected){
56089                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56090                 view.focusRow(rowIndex);
56091             }
56092         }
56093         this.fireEvent("afterselectionchange", this);
56094     },
56095     // private
56096     handleDragableRowClick :  function(grid, rowIndex, e) 
56097     {
56098         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56099             this.selectRow(rowIndex, false);
56100             grid.view.focusRow(rowIndex);
56101              this.fireEvent("afterselectionchange", this);
56102         }
56103     },
56104     
56105     /**
56106      * Selects multiple rows.
56107      * @param {Array} rows Array of the indexes of the row to select
56108      * @param {Boolean} keepExisting (optional) True to keep existing selections
56109      */
56110     selectRows : function(rows, keepExisting){
56111         if(!keepExisting){
56112             this.clearSelections();
56113         }
56114         for(var i = 0, len = rows.length; i < len; i++){
56115             this.selectRow(rows[i], true);
56116         }
56117     },
56118
56119     /**
56120      * Selects a range of rows. All rows in between startRow and endRow are also selected.
56121      * @param {Number} startRow The index of the first row in the range
56122      * @param {Number} endRow The index of the last row in the range
56123      * @param {Boolean} keepExisting (optional) True to retain existing selections
56124      */
56125     selectRange : function(startRow, endRow, keepExisting){
56126         if(this.locked) return;
56127         if(!keepExisting){
56128             this.clearSelections();
56129         }
56130         if(startRow <= endRow){
56131             for(var i = startRow; i <= endRow; i++){
56132                 this.selectRow(i, true);
56133             }
56134         }else{
56135             for(var i = startRow; i >= endRow; i--){
56136                 this.selectRow(i, true);
56137             }
56138         }
56139     },
56140
56141     /**
56142      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56143      * @param {Number} startRow The index of the first row in the range
56144      * @param {Number} endRow The index of the last row in the range
56145      */
56146     deselectRange : function(startRow, endRow, preventViewNotify){
56147         if(this.locked) return;
56148         for(var i = startRow; i <= endRow; i++){
56149             this.deselectRow(i, preventViewNotify);
56150         }
56151     },
56152
56153     /**
56154      * Selects a row.
56155      * @param {Number} row The index of the row to select
56156      * @param {Boolean} keepExisting (optional) True to keep existing selections
56157      */
56158     selectRow : function(index, keepExisting, preventViewNotify){
56159         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56160         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56161             if(!keepExisting || this.singleSelect){
56162                 this.clearSelections();
56163             }
56164             var r = this.grid.dataSource.getAt(index);
56165             this.selections.add(r);
56166             this.last = this.lastActive = index;
56167             if(!preventViewNotify){
56168                 this.grid.getView().onRowSelect(index);
56169             }
56170             this.fireEvent("rowselect", this, index, r);
56171             this.fireEvent("selectionchange", this);
56172         }
56173     },
56174
56175     /**
56176      * Deselects a row.
56177      * @param {Number} row The index of the row to deselect
56178      */
56179     deselectRow : function(index, preventViewNotify){
56180         if(this.locked) return;
56181         if(this.last == index){
56182             this.last = false;
56183         }
56184         if(this.lastActive == index){
56185             this.lastActive = false;
56186         }
56187         var r = this.grid.dataSource.getAt(index);
56188         this.selections.remove(r);
56189         if(!preventViewNotify){
56190             this.grid.getView().onRowDeselect(index);
56191         }
56192         this.fireEvent("rowdeselect", this, index);
56193         this.fireEvent("selectionchange", this);
56194     },
56195
56196     // private
56197     restoreLast : function(){
56198         if(this._last){
56199             this.last = this._last;
56200         }
56201     },
56202
56203     // private
56204     acceptsNav : function(row, col, cm){
56205         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56206     },
56207
56208     // private
56209     onEditorKey : function(field, e){
56210         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56211         if(k == e.TAB){
56212             e.stopEvent();
56213             ed.completeEdit();
56214             if(e.shiftKey){
56215                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56216             }else{
56217                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56218             }
56219         }else if(k == e.ENTER && !e.ctrlKey){
56220             e.stopEvent();
56221             ed.completeEdit();
56222             if(e.shiftKey){
56223                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56224             }else{
56225                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56226             }
56227         }else if(k == e.ESC){
56228             ed.cancelEdit();
56229         }
56230         if(newCell){
56231             g.startEditing(newCell[0], newCell[1]);
56232         }
56233     }
56234 });/*
56235  * Based on:
56236  * Ext JS Library 1.1.1
56237  * Copyright(c) 2006-2007, Ext JS, LLC.
56238  *
56239  * Originally Released Under LGPL - original licence link has changed is not relivant.
56240  *
56241  * Fork - LGPL
56242  * <script type="text/javascript">
56243  */
56244 /**
56245  * @class Roo.grid.CellSelectionModel
56246  * @extends Roo.grid.AbstractSelectionModel
56247  * This class provides the basic implementation for cell selection in a grid.
56248  * @constructor
56249  * @param {Object} config The object containing the configuration of this model.
56250  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56251  */
56252 Roo.grid.CellSelectionModel = function(config){
56253     Roo.apply(this, config);
56254
56255     this.selection = null;
56256
56257     this.addEvents({
56258         /**
56259              * @event beforerowselect
56260              * Fires before a cell is selected.
56261              * @param {SelectionModel} this
56262              * @param {Number} rowIndex The selected row index
56263              * @param {Number} colIndex The selected cell index
56264              */
56265             "beforecellselect" : true,
56266         /**
56267              * @event cellselect
56268              * Fires when a cell is selected.
56269              * @param {SelectionModel} this
56270              * @param {Number} rowIndex The selected row index
56271              * @param {Number} colIndex The selected cell index
56272              */
56273             "cellselect" : true,
56274         /**
56275              * @event selectionchange
56276              * Fires when the active selection changes.
56277              * @param {SelectionModel} this
56278              * @param {Object} selection null for no selection or an object (o) with two properties
56279                 <ul>
56280                 <li>o.record: the record object for the row the selection is in</li>
56281                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56282                 </ul>
56283              */
56284             "selectionchange" : true,
56285         /**
56286              * @event tabend
56287              * Fires when the tab (or enter) was pressed on the last editable cell
56288              * You can use this to trigger add new row.
56289              * @param {SelectionModel} this
56290              */
56291             "tabend" : true,
56292          /**
56293              * @event beforeeditnext
56294              * Fires before the next editable sell is made active
56295              * You can use this to skip to another cell or fire the tabend
56296              *    if you set cell to false
56297              * @param {Object} eventdata object : { cell : [ row, col ] } 
56298              */
56299             "beforeeditnext" : true
56300     });
56301     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56302 };
56303
56304 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56305     
56306     enter_is_tab: false,
56307
56308     /** @ignore */
56309     initEvents : function(){
56310         this.grid.on("mousedown", this.handleMouseDown, this);
56311         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56312         var view = this.grid.view;
56313         view.on("refresh", this.onViewChange, this);
56314         view.on("rowupdated", this.onRowUpdated, this);
56315         view.on("beforerowremoved", this.clearSelections, this);
56316         view.on("beforerowsinserted", this.clearSelections, this);
56317         if(this.grid.isEditor){
56318             this.grid.on("beforeedit", this.beforeEdit,  this);
56319         }
56320     },
56321
56322         //private
56323     beforeEdit : function(e){
56324         this.select(e.row, e.column, false, true, e.record);
56325     },
56326
56327         //private
56328     onRowUpdated : function(v, index, r){
56329         if(this.selection && this.selection.record == r){
56330             v.onCellSelect(index, this.selection.cell[1]);
56331         }
56332     },
56333
56334         //private
56335     onViewChange : function(){
56336         this.clearSelections(true);
56337     },
56338
56339         /**
56340          * Returns the currently selected cell,.
56341          * @return {Array} The selected cell (row, column) or null if none selected.
56342          */
56343     getSelectedCell : function(){
56344         return this.selection ? this.selection.cell : null;
56345     },
56346
56347     /**
56348      * Clears all selections.
56349      * @param {Boolean} true to prevent the gridview from being notified about the change.
56350      */
56351     clearSelections : function(preventNotify){
56352         var s = this.selection;
56353         if(s){
56354             if(preventNotify !== true){
56355                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56356             }
56357             this.selection = null;
56358             this.fireEvent("selectionchange", this, null);
56359         }
56360     },
56361
56362     /**
56363      * Returns true if there is a selection.
56364      * @return {Boolean}
56365      */
56366     hasSelection : function(){
56367         return this.selection ? true : false;
56368     },
56369
56370     /** @ignore */
56371     handleMouseDown : function(e, t){
56372         var v = this.grid.getView();
56373         if(this.isLocked()){
56374             return;
56375         };
56376         var row = v.findRowIndex(t);
56377         var cell = v.findCellIndex(t);
56378         if(row !== false && cell !== false){
56379             this.select(row, cell);
56380         }
56381     },
56382
56383     /**
56384      * Selects a cell.
56385      * @param {Number} rowIndex
56386      * @param {Number} collIndex
56387      */
56388     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56389         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56390             this.clearSelections();
56391             r = r || this.grid.dataSource.getAt(rowIndex);
56392             this.selection = {
56393                 record : r,
56394                 cell : [rowIndex, colIndex]
56395             };
56396             if(!preventViewNotify){
56397                 var v = this.grid.getView();
56398                 v.onCellSelect(rowIndex, colIndex);
56399                 if(preventFocus !== true){
56400                     v.focusCell(rowIndex, colIndex);
56401                 }
56402             }
56403             this.fireEvent("cellselect", this, rowIndex, colIndex);
56404             this.fireEvent("selectionchange", this, this.selection);
56405         }
56406     },
56407
56408         //private
56409     isSelectable : function(rowIndex, colIndex, cm){
56410         return !cm.isHidden(colIndex);
56411     },
56412
56413     /** @ignore */
56414     handleKeyDown : function(e){
56415         //Roo.log('Cell Sel Model handleKeyDown');
56416         if(!e.isNavKeyPress()){
56417             return;
56418         }
56419         var g = this.grid, s = this.selection;
56420         if(!s){
56421             e.stopEvent();
56422             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56423             if(cell){
56424                 this.select(cell[0], cell[1]);
56425             }
56426             return;
56427         }
56428         var sm = this;
56429         var walk = function(row, col, step){
56430             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56431         };
56432         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56433         var newCell;
56434
56435       
56436
56437         switch(k){
56438             case e.TAB:
56439                 // handled by onEditorKey
56440                 if (g.isEditor && g.editing) {
56441                     return;
56442                 }
56443                 if(e.shiftKey) {
56444                     newCell = walk(r, c-1, -1);
56445                 } else {
56446                     newCell = walk(r, c+1, 1);
56447                 }
56448                 break;
56449             
56450             case e.DOWN:
56451                newCell = walk(r+1, c, 1);
56452                 break;
56453             
56454             case e.UP:
56455                 newCell = walk(r-1, c, -1);
56456                 break;
56457             
56458             case e.RIGHT:
56459                 newCell = walk(r, c+1, 1);
56460                 break;
56461             
56462             case e.LEFT:
56463                 newCell = walk(r, c-1, -1);
56464                 break;
56465             
56466             case e.ENTER:
56467                 
56468                 if(g.isEditor && !g.editing){
56469                    g.startEditing(r, c);
56470                    e.stopEvent();
56471                    return;
56472                 }
56473                 
56474                 
56475              break;
56476         };
56477         if(newCell){
56478             this.select(newCell[0], newCell[1]);
56479             e.stopEvent();
56480             
56481         }
56482     },
56483
56484     acceptsNav : function(row, col, cm){
56485         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56486     },
56487     /**
56488      * Selects a cell.
56489      * @param {Number} field (not used) - as it's normally used as a listener
56490      * @param {Number} e - event - fake it by using
56491      *
56492      * var e = Roo.EventObjectImpl.prototype;
56493      * e.keyCode = e.TAB
56494      *
56495      * 
56496      */
56497     onEditorKey : function(field, e){
56498         
56499         var k = e.getKey(),
56500             newCell,
56501             g = this.grid,
56502             ed = g.activeEditor,
56503             forward = false;
56504         ///Roo.log('onEditorKey' + k);
56505         
56506         
56507         if (this.enter_is_tab && k == e.ENTER) {
56508             k = e.TAB;
56509         }
56510         
56511         if(k == e.TAB){
56512             if(e.shiftKey){
56513                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56514             }else{
56515                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56516                 forward = true;
56517             }
56518             
56519             e.stopEvent();
56520             
56521         } else if(k == e.ENTER &&  !e.ctrlKey){
56522             ed.completeEdit();
56523             e.stopEvent();
56524             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56525         
56526                 } else if(k == e.ESC){
56527             ed.cancelEdit();
56528         }
56529                 
56530         if (newCell) {
56531             var ecall = { cell : newCell, forward : forward };
56532             this.fireEvent('beforeeditnext', ecall );
56533             newCell = ecall.cell;
56534                         forward = ecall.forward;
56535         }
56536                 
56537         if(newCell){
56538             //Roo.log('next cell after edit');
56539             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56540         } else if (forward) {
56541             // tabbed past last
56542             this.fireEvent.defer(100, this, ['tabend',this]);
56543         }
56544     }
56545 });/*
56546  * Based on:
56547  * Ext JS Library 1.1.1
56548  * Copyright(c) 2006-2007, Ext JS, LLC.
56549  *
56550  * Originally Released Under LGPL - original licence link has changed is not relivant.
56551  *
56552  * Fork - LGPL
56553  * <script type="text/javascript">
56554  */
56555  
56556 /**
56557  * @class Roo.grid.EditorGrid
56558  * @extends Roo.grid.Grid
56559  * Class for creating and editable grid.
56560  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56561  * The container MUST have some type of size defined for the grid to fill. The container will be 
56562  * automatically set to position relative if it isn't already.
56563  * @param {Object} dataSource The data model to bind to
56564  * @param {Object} colModel The column model with info about this grid's columns
56565  */
56566 Roo.grid.EditorGrid = function(container, config){
56567     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56568     this.getGridEl().addClass("xedit-grid");
56569
56570     if(!this.selModel){
56571         this.selModel = new Roo.grid.CellSelectionModel();
56572     }
56573
56574     this.activeEditor = null;
56575
56576         this.addEvents({
56577             /**
56578              * @event beforeedit
56579              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56580              * <ul style="padding:5px;padding-left:16px;">
56581              * <li>grid - This grid</li>
56582              * <li>record - The record being edited</li>
56583              * <li>field - The field name being edited</li>
56584              * <li>value - The value for the field being edited.</li>
56585              * <li>row - The grid row index</li>
56586              * <li>column - The grid column index</li>
56587              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56588              * </ul>
56589              * @param {Object} e An edit event (see above for description)
56590              */
56591             "beforeedit" : true,
56592             /**
56593              * @event afteredit
56594              * Fires after a cell is edited. <br />
56595              * <ul style="padding:5px;padding-left:16px;">
56596              * <li>grid - This grid</li>
56597              * <li>record - The record being edited</li>
56598              * <li>field - The field name being edited</li>
56599              * <li>value - The value being set</li>
56600              * <li>originalValue - The original value for the field, before the edit.</li>
56601              * <li>row - The grid row index</li>
56602              * <li>column - The grid column index</li>
56603              * </ul>
56604              * @param {Object} e An edit event (see above for description)
56605              */
56606             "afteredit" : true,
56607             /**
56608              * @event validateedit
56609              * Fires after a cell is edited, but before the value is set in the record. 
56610          * You can use this to modify the value being set in the field, Return false
56611              * to cancel the change. The edit event object has the following properties <br />
56612              * <ul style="padding:5px;padding-left:16px;">
56613          * <li>editor - This editor</li>
56614              * <li>grid - This grid</li>
56615              * <li>record - The record being edited</li>
56616              * <li>field - The field name being edited</li>
56617              * <li>value - The value being set</li>
56618              * <li>originalValue - The original value for the field, before the edit.</li>
56619              * <li>row - The grid row index</li>
56620              * <li>column - The grid column index</li>
56621              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56622              * </ul>
56623              * @param {Object} e An edit event (see above for description)
56624              */
56625             "validateedit" : true
56626         });
56627     this.on("bodyscroll", this.stopEditing,  this);
56628     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56629 };
56630
56631 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56632     /**
56633      * @cfg {Number} clicksToEdit
56634      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56635      */
56636     clicksToEdit: 2,
56637
56638     // private
56639     isEditor : true,
56640     // private
56641     trackMouseOver: false, // causes very odd FF errors
56642
56643     onCellDblClick : function(g, row, col){
56644         this.startEditing(row, col);
56645     },
56646
56647     onEditComplete : function(ed, value, startValue){
56648         this.editing = false;
56649         this.activeEditor = null;
56650         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56651         var r = ed.record;
56652         var field = this.colModel.getDataIndex(ed.col);
56653         var e = {
56654             grid: this,
56655             record: r,
56656             field: field,
56657             originalValue: startValue,
56658             value: value,
56659             row: ed.row,
56660             column: ed.col,
56661             cancel:false,
56662             editor: ed
56663         };
56664         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56665         cell.show();
56666           
56667         if(String(value) !== String(startValue)){
56668             
56669             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56670                 r.set(field, e.value);
56671                 // if we are dealing with a combo box..
56672                 // then we also set the 'name' colum to be the displayField
56673                 if (ed.field.displayField && ed.field.name) {
56674                     r.set(ed.field.name, ed.field.el.dom.value);
56675                 }
56676                 
56677                 delete e.cancel; //?? why!!!
56678                 this.fireEvent("afteredit", e);
56679             }
56680         } else {
56681             this.fireEvent("afteredit", e); // always fire it!
56682         }
56683         this.view.focusCell(ed.row, ed.col);
56684     },
56685
56686     /**
56687      * Starts editing the specified for the specified row/column
56688      * @param {Number} rowIndex
56689      * @param {Number} colIndex
56690      */
56691     startEditing : function(row, col){
56692         this.stopEditing();
56693         if(this.colModel.isCellEditable(col, row)){
56694             this.view.ensureVisible(row, col, true);
56695           
56696             var r = this.dataSource.getAt(row);
56697             var field = this.colModel.getDataIndex(col);
56698             var cell = Roo.get(this.view.getCell(row,col));
56699             var e = {
56700                 grid: this,
56701                 record: r,
56702                 field: field,
56703                 value: r.data[field],
56704                 row: row,
56705                 column: col,
56706                 cancel:false 
56707             };
56708             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56709                 this.editing = true;
56710                 var ed = this.colModel.getCellEditor(col, row);
56711                 
56712                 if (!ed) {
56713                     return;
56714                 }
56715                 if(!ed.rendered){
56716                     ed.render(ed.parentEl || document.body);
56717                 }
56718                 ed.field.reset();
56719                
56720                 cell.hide();
56721                 
56722                 (function(){ // complex but required for focus issues in safari, ie and opera
56723                     ed.row = row;
56724                     ed.col = col;
56725                     ed.record = r;
56726                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56727                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56728                     this.activeEditor = ed;
56729                     var v = r.data[field];
56730                     ed.startEdit(this.view.getCell(row, col), v);
56731                     // combo's with 'displayField and name set
56732                     if (ed.field.displayField && ed.field.name) {
56733                         ed.field.el.dom.value = r.data[ed.field.name];
56734                     }
56735                     
56736                     
56737                 }).defer(50, this);
56738             }
56739         }
56740     },
56741         
56742     /**
56743      * Stops any active editing
56744      */
56745     stopEditing : function(){
56746         if(this.activeEditor){
56747             this.activeEditor.completeEdit();
56748         }
56749         this.activeEditor = null;
56750     },
56751         
56752          /**
56753      * Called to get grid's drag proxy text, by default returns this.ddText.
56754      * @return {String}
56755      */
56756     getDragDropText : function(){
56757         var count = this.selModel.getSelectedCell() ? 1 : 0;
56758         return String.format(this.ddText, count, count == 1 ? '' : 's');
56759     }
56760         
56761 });/*
56762  * Based on:
56763  * Ext JS Library 1.1.1
56764  * Copyright(c) 2006-2007, Ext JS, LLC.
56765  *
56766  * Originally Released Under LGPL - original licence link has changed is not relivant.
56767  *
56768  * Fork - LGPL
56769  * <script type="text/javascript">
56770  */
56771
56772 // private - not really -- you end up using it !
56773 // This is a support class used internally by the Grid components
56774
56775 /**
56776  * @class Roo.grid.GridEditor
56777  * @extends Roo.Editor
56778  * Class for creating and editable grid elements.
56779  * @param {Object} config any settings (must include field)
56780  */
56781 Roo.grid.GridEditor = function(field, config){
56782     if (!config && field.field) {
56783         config = field;
56784         field = Roo.factory(config.field, Roo.form);
56785     }
56786     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56787     field.monitorTab = false;
56788 };
56789
56790 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56791     
56792     /**
56793      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56794      */
56795     
56796     alignment: "tl-tl",
56797     autoSize: "width",
56798     hideEl : false,
56799     cls: "x-small-editor x-grid-editor",
56800     shim:false,
56801     shadow:"frame"
56802 });/*
56803  * Based on:
56804  * Ext JS Library 1.1.1
56805  * Copyright(c) 2006-2007, Ext JS, LLC.
56806  *
56807  * Originally Released Under LGPL - original licence link has changed is not relivant.
56808  *
56809  * Fork - LGPL
56810  * <script type="text/javascript">
56811  */
56812   
56813
56814   
56815 Roo.grid.PropertyRecord = Roo.data.Record.create([
56816     {name:'name',type:'string'},  'value'
56817 ]);
56818
56819
56820 Roo.grid.PropertyStore = function(grid, source){
56821     this.grid = grid;
56822     this.store = new Roo.data.Store({
56823         recordType : Roo.grid.PropertyRecord
56824     });
56825     this.store.on('update', this.onUpdate,  this);
56826     if(source){
56827         this.setSource(source);
56828     }
56829     Roo.grid.PropertyStore.superclass.constructor.call(this);
56830 };
56831
56832
56833
56834 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56835     setSource : function(o){
56836         this.source = o;
56837         this.store.removeAll();
56838         var data = [];
56839         for(var k in o){
56840             if(this.isEditableValue(o[k])){
56841                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56842             }
56843         }
56844         this.store.loadRecords({records: data}, {}, true);
56845     },
56846
56847     onUpdate : function(ds, record, type){
56848         if(type == Roo.data.Record.EDIT){
56849             var v = record.data['value'];
56850             var oldValue = record.modified['value'];
56851             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56852                 this.source[record.id] = v;
56853                 record.commit();
56854                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56855             }else{
56856                 record.reject();
56857             }
56858         }
56859     },
56860
56861     getProperty : function(row){
56862        return this.store.getAt(row);
56863     },
56864
56865     isEditableValue: function(val){
56866         if(val && val instanceof Date){
56867             return true;
56868         }else if(typeof val == 'object' || typeof val == 'function'){
56869             return false;
56870         }
56871         return true;
56872     },
56873
56874     setValue : function(prop, value){
56875         this.source[prop] = value;
56876         this.store.getById(prop).set('value', value);
56877     },
56878
56879     getSource : function(){
56880         return this.source;
56881     }
56882 });
56883
56884 Roo.grid.PropertyColumnModel = function(grid, store){
56885     this.grid = grid;
56886     var g = Roo.grid;
56887     g.PropertyColumnModel.superclass.constructor.call(this, [
56888         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56889         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56890     ]);
56891     this.store = store;
56892     this.bselect = Roo.DomHelper.append(document.body, {
56893         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56894             {tag: 'option', value: 'true', html: 'true'},
56895             {tag: 'option', value: 'false', html: 'false'}
56896         ]
56897     });
56898     Roo.id(this.bselect);
56899     var f = Roo.form;
56900     this.editors = {
56901         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56902         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56903         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56904         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56905         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56906     };
56907     this.renderCellDelegate = this.renderCell.createDelegate(this);
56908     this.renderPropDelegate = this.renderProp.createDelegate(this);
56909 };
56910
56911 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56912     
56913     
56914     nameText : 'Name',
56915     valueText : 'Value',
56916     
56917     dateFormat : 'm/j/Y',
56918     
56919     
56920     renderDate : function(dateVal){
56921         return dateVal.dateFormat(this.dateFormat);
56922     },
56923
56924     renderBool : function(bVal){
56925         return bVal ? 'true' : 'false';
56926     },
56927
56928     isCellEditable : function(colIndex, rowIndex){
56929         return colIndex == 1;
56930     },
56931
56932     getRenderer : function(col){
56933         return col == 1 ?
56934             this.renderCellDelegate : this.renderPropDelegate;
56935     },
56936
56937     renderProp : function(v){
56938         return this.getPropertyName(v);
56939     },
56940
56941     renderCell : function(val){
56942         var rv = val;
56943         if(val instanceof Date){
56944             rv = this.renderDate(val);
56945         }else if(typeof val == 'boolean'){
56946             rv = this.renderBool(val);
56947         }
56948         return Roo.util.Format.htmlEncode(rv);
56949     },
56950
56951     getPropertyName : function(name){
56952         var pn = this.grid.propertyNames;
56953         return pn && pn[name] ? pn[name] : name;
56954     },
56955
56956     getCellEditor : function(colIndex, rowIndex){
56957         var p = this.store.getProperty(rowIndex);
56958         var n = p.data['name'], val = p.data['value'];
56959         
56960         if(typeof(this.grid.customEditors[n]) == 'string'){
56961             return this.editors[this.grid.customEditors[n]];
56962         }
56963         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56964             return this.grid.customEditors[n];
56965         }
56966         if(val instanceof Date){
56967             return this.editors['date'];
56968         }else if(typeof val == 'number'){
56969             return this.editors['number'];
56970         }else if(typeof val == 'boolean'){
56971             return this.editors['boolean'];
56972         }else{
56973             return this.editors['string'];
56974         }
56975     }
56976 });
56977
56978 /**
56979  * @class Roo.grid.PropertyGrid
56980  * @extends Roo.grid.EditorGrid
56981  * This class represents the  interface of a component based property grid control.
56982  * <br><br>Usage:<pre><code>
56983  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56984       
56985  });
56986  // set any options
56987  grid.render();
56988  * </code></pre>
56989   
56990  * @constructor
56991  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56992  * The container MUST have some type of size defined for the grid to fill. The container will be
56993  * automatically set to position relative if it isn't already.
56994  * @param {Object} config A config object that sets properties on this grid.
56995  */
56996 Roo.grid.PropertyGrid = function(container, config){
56997     config = config || {};
56998     var store = new Roo.grid.PropertyStore(this);
56999     this.store = store;
57000     var cm = new Roo.grid.PropertyColumnModel(this, store);
57001     store.store.sort('name', 'ASC');
57002     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57003         ds: store.store,
57004         cm: cm,
57005         enableColLock:false,
57006         enableColumnMove:false,
57007         stripeRows:false,
57008         trackMouseOver: false,
57009         clicksToEdit:1
57010     }, config));
57011     this.getGridEl().addClass('x-props-grid');
57012     this.lastEditRow = null;
57013     this.on('columnresize', this.onColumnResize, this);
57014     this.addEvents({
57015          /**
57016              * @event beforepropertychange
57017              * Fires before a property changes (return false to stop?)
57018              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57019              * @param {String} id Record Id
57020              * @param {String} newval New Value
57021          * @param {String} oldval Old Value
57022              */
57023         "beforepropertychange": true,
57024         /**
57025              * @event propertychange
57026              * Fires after a property changes
57027              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57028              * @param {String} id Record Id
57029              * @param {String} newval New Value
57030          * @param {String} oldval Old Value
57031              */
57032         "propertychange": true
57033     });
57034     this.customEditors = this.customEditors || {};
57035 };
57036 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57037     
57038      /**
57039      * @cfg {Object} customEditors map of colnames=> custom editors.
57040      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57041      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57042      * false disables editing of the field.
57043          */
57044     
57045       /**
57046      * @cfg {Object} propertyNames map of property Names to their displayed value
57047          */
57048     
57049     render : function(){
57050         Roo.grid.PropertyGrid.superclass.render.call(this);
57051         this.autoSize.defer(100, this);
57052     },
57053
57054     autoSize : function(){
57055         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57056         if(this.view){
57057             this.view.fitColumns();
57058         }
57059     },
57060
57061     onColumnResize : function(){
57062         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57063         this.autoSize();
57064     },
57065     /**
57066      * Sets the data for the Grid
57067      * accepts a Key => Value object of all the elements avaiable.
57068      * @param {Object} data  to appear in grid.
57069      */
57070     setSource : function(source){
57071         this.store.setSource(source);
57072         //this.autoSize();
57073     },
57074     /**
57075      * Gets all the data from the grid.
57076      * @return {Object} data  data stored in grid
57077      */
57078     getSource : function(){
57079         return this.store.getSource();
57080     }
57081 });/*
57082   
57083  * Licence LGPL
57084  
57085  */
57086  
57087 /**
57088  * @class Roo.grid.Calendar
57089  * @extends Roo.util.Grid
57090  * This class extends the Grid to provide a calendar widget
57091  * <br><br>Usage:<pre><code>
57092  var grid = new Roo.grid.Calendar("my-container-id", {
57093      ds: myDataStore,
57094      cm: myColModel,
57095      selModel: mySelectionModel,
57096      autoSizeColumns: true,
57097      monitorWindowResize: false,
57098      trackMouseOver: true
57099      eventstore : real data store..
57100  });
57101  // set any options
57102  grid.render();
57103   
57104   * @constructor
57105  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57106  * The container MUST have some type of size defined for the grid to fill. The container will be
57107  * automatically set to position relative if it isn't already.
57108  * @param {Object} config A config object that sets properties on this grid.
57109  */
57110 Roo.grid.Calendar = function(container, config){
57111         // initialize the container
57112         this.container = Roo.get(container);
57113         this.container.update("");
57114         this.container.setStyle("overflow", "hidden");
57115     this.container.addClass('x-grid-container');
57116
57117     this.id = this.container.id;
57118
57119     Roo.apply(this, config);
57120     // check and correct shorthanded configs
57121     
57122     var rows = [];
57123     var d =1;
57124     for (var r = 0;r < 6;r++) {
57125         
57126         rows[r]=[];
57127         for (var c =0;c < 7;c++) {
57128             rows[r][c]= '';
57129         }
57130     }
57131     if (this.eventStore) {
57132         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57133         this.eventStore.on('load',this.onLoad, this);
57134         this.eventStore.on('beforeload',this.clearEvents, this);
57135          
57136     }
57137     
57138     this.dataSource = new Roo.data.Store({
57139             proxy: new Roo.data.MemoryProxy(rows),
57140             reader: new Roo.data.ArrayReader({}, [
57141                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57142     });
57143
57144     this.dataSource.load();
57145     this.ds = this.dataSource;
57146     this.ds.xmodule = this.xmodule || false;
57147     
57148     
57149     var cellRender = function(v,x,r)
57150     {
57151         return String.format(
57152             '<div class="fc-day  fc-widget-content"><div>' +
57153                 '<div class="fc-event-container"></div>' +
57154                 '<div class="fc-day-number">{0}</div>'+
57155                 
57156                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57157             '</div></div>', v);
57158     
57159     }
57160     
57161     
57162     this.colModel = new Roo.grid.ColumnModel( [
57163         {
57164             xtype: 'ColumnModel',
57165             xns: Roo.grid,
57166             dataIndex : 'weekday0',
57167             header : 'Sunday',
57168             renderer : cellRender
57169         },
57170         {
57171             xtype: 'ColumnModel',
57172             xns: Roo.grid,
57173             dataIndex : 'weekday1',
57174             header : 'Monday',
57175             renderer : cellRender
57176         },
57177         {
57178             xtype: 'ColumnModel',
57179             xns: Roo.grid,
57180             dataIndex : 'weekday2',
57181             header : 'Tuesday',
57182             renderer : cellRender
57183         },
57184         {
57185             xtype: 'ColumnModel',
57186             xns: Roo.grid,
57187             dataIndex : 'weekday3',
57188             header : 'Wednesday',
57189             renderer : cellRender
57190         },
57191         {
57192             xtype: 'ColumnModel',
57193             xns: Roo.grid,
57194             dataIndex : 'weekday4',
57195             header : 'Thursday',
57196             renderer : cellRender
57197         },
57198         {
57199             xtype: 'ColumnModel',
57200             xns: Roo.grid,
57201             dataIndex : 'weekday5',
57202             header : 'Friday',
57203             renderer : cellRender
57204         },
57205         {
57206             xtype: 'ColumnModel',
57207             xns: Roo.grid,
57208             dataIndex : 'weekday6',
57209             header : 'Saturday',
57210             renderer : cellRender
57211         }
57212     ]);
57213     this.cm = this.colModel;
57214     this.cm.xmodule = this.xmodule || false;
57215  
57216         
57217           
57218     //this.selModel = new Roo.grid.CellSelectionModel();
57219     //this.sm = this.selModel;
57220     //this.selModel.init(this);
57221     
57222     
57223     if(this.width){
57224         this.container.setWidth(this.width);
57225     }
57226
57227     if(this.height){
57228         this.container.setHeight(this.height);
57229     }
57230     /** @private */
57231         this.addEvents({
57232         // raw events
57233         /**
57234          * @event click
57235          * The raw click event for the entire grid.
57236          * @param {Roo.EventObject} e
57237          */
57238         "click" : true,
57239         /**
57240          * @event dblclick
57241          * The raw dblclick event for the entire grid.
57242          * @param {Roo.EventObject} e
57243          */
57244         "dblclick" : true,
57245         /**
57246          * @event contextmenu
57247          * The raw contextmenu event for the entire grid.
57248          * @param {Roo.EventObject} e
57249          */
57250         "contextmenu" : true,
57251         /**
57252          * @event mousedown
57253          * The raw mousedown event for the entire grid.
57254          * @param {Roo.EventObject} e
57255          */
57256         "mousedown" : true,
57257         /**
57258          * @event mouseup
57259          * The raw mouseup event for the entire grid.
57260          * @param {Roo.EventObject} e
57261          */
57262         "mouseup" : true,
57263         /**
57264          * @event mouseover
57265          * The raw mouseover event for the entire grid.
57266          * @param {Roo.EventObject} e
57267          */
57268         "mouseover" : true,
57269         /**
57270          * @event mouseout
57271          * The raw mouseout event for the entire grid.
57272          * @param {Roo.EventObject} e
57273          */
57274         "mouseout" : true,
57275         /**
57276          * @event keypress
57277          * The raw keypress event for the entire grid.
57278          * @param {Roo.EventObject} e
57279          */
57280         "keypress" : true,
57281         /**
57282          * @event keydown
57283          * The raw keydown event for the entire grid.
57284          * @param {Roo.EventObject} e
57285          */
57286         "keydown" : true,
57287
57288         // custom events
57289
57290         /**
57291          * @event cellclick
57292          * Fires when a cell is clicked
57293          * @param {Grid} this
57294          * @param {Number} rowIndex
57295          * @param {Number} columnIndex
57296          * @param {Roo.EventObject} e
57297          */
57298         "cellclick" : true,
57299         /**
57300          * @event celldblclick
57301          * Fires when a cell is double clicked
57302          * @param {Grid} this
57303          * @param {Number} rowIndex
57304          * @param {Number} columnIndex
57305          * @param {Roo.EventObject} e
57306          */
57307         "celldblclick" : true,
57308         /**
57309          * @event rowclick
57310          * Fires when a row is clicked
57311          * @param {Grid} this
57312          * @param {Number} rowIndex
57313          * @param {Roo.EventObject} e
57314          */
57315         "rowclick" : true,
57316         /**
57317          * @event rowdblclick
57318          * Fires when a row is double clicked
57319          * @param {Grid} this
57320          * @param {Number} rowIndex
57321          * @param {Roo.EventObject} e
57322          */
57323         "rowdblclick" : true,
57324         /**
57325          * @event headerclick
57326          * Fires when a header is clicked
57327          * @param {Grid} this
57328          * @param {Number} columnIndex
57329          * @param {Roo.EventObject} e
57330          */
57331         "headerclick" : true,
57332         /**
57333          * @event headerdblclick
57334          * Fires when a header cell is double clicked
57335          * @param {Grid} this
57336          * @param {Number} columnIndex
57337          * @param {Roo.EventObject} e
57338          */
57339         "headerdblclick" : true,
57340         /**
57341          * @event rowcontextmenu
57342          * Fires when a row is right clicked
57343          * @param {Grid} this
57344          * @param {Number} rowIndex
57345          * @param {Roo.EventObject} e
57346          */
57347         "rowcontextmenu" : true,
57348         /**
57349          * @event cellcontextmenu
57350          * Fires when a cell is right clicked
57351          * @param {Grid} this
57352          * @param {Number} rowIndex
57353          * @param {Number} cellIndex
57354          * @param {Roo.EventObject} e
57355          */
57356          "cellcontextmenu" : true,
57357         /**
57358          * @event headercontextmenu
57359          * Fires when a header is right clicked
57360          * @param {Grid} this
57361          * @param {Number} columnIndex
57362          * @param {Roo.EventObject} e
57363          */
57364         "headercontextmenu" : true,
57365         /**
57366          * @event bodyscroll
57367          * Fires when the body element is scrolled
57368          * @param {Number} scrollLeft
57369          * @param {Number} scrollTop
57370          */
57371         "bodyscroll" : true,
57372         /**
57373          * @event columnresize
57374          * Fires when the user resizes a column
57375          * @param {Number} columnIndex
57376          * @param {Number} newSize
57377          */
57378         "columnresize" : true,
57379         /**
57380          * @event columnmove
57381          * Fires when the user moves a column
57382          * @param {Number} oldIndex
57383          * @param {Number} newIndex
57384          */
57385         "columnmove" : true,
57386         /**
57387          * @event startdrag
57388          * Fires when row(s) start being dragged
57389          * @param {Grid} this
57390          * @param {Roo.GridDD} dd The drag drop object
57391          * @param {event} e The raw browser event
57392          */
57393         "startdrag" : true,
57394         /**
57395          * @event enddrag
57396          * Fires when a drag operation is complete
57397          * @param {Grid} this
57398          * @param {Roo.GridDD} dd The drag drop object
57399          * @param {event} e The raw browser event
57400          */
57401         "enddrag" : true,
57402         /**
57403          * @event dragdrop
57404          * Fires when dragged row(s) are dropped on a valid DD target
57405          * @param {Grid} this
57406          * @param {Roo.GridDD} dd The drag drop object
57407          * @param {String} targetId The target drag drop object
57408          * @param {event} e The raw browser event
57409          */
57410         "dragdrop" : true,
57411         /**
57412          * @event dragover
57413          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57414          * @param {Grid} this
57415          * @param {Roo.GridDD} dd The drag drop object
57416          * @param {String} targetId The target drag drop object
57417          * @param {event} e The raw browser event
57418          */
57419         "dragover" : true,
57420         /**
57421          * @event dragenter
57422          *  Fires when the dragged row(s) first cross another DD target while being dragged
57423          * @param {Grid} this
57424          * @param {Roo.GridDD} dd The drag drop object
57425          * @param {String} targetId The target drag drop object
57426          * @param {event} e The raw browser event
57427          */
57428         "dragenter" : true,
57429         /**
57430          * @event dragout
57431          * Fires when the dragged row(s) leave another DD target while being dragged
57432          * @param {Grid} this
57433          * @param {Roo.GridDD} dd The drag drop object
57434          * @param {String} targetId The target drag drop object
57435          * @param {event} e The raw browser event
57436          */
57437         "dragout" : true,
57438         /**
57439          * @event rowclass
57440          * Fires when a row is rendered, so you can change add a style to it.
57441          * @param {GridView} gridview   The grid view
57442          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57443          */
57444         'rowclass' : true,
57445
57446         /**
57447          * @event render
57448          * Fires when the grid is rendered
57449          * @param {Grid} grid
57450          */
57451         'render' : true,
57452             /**
57453              * @event select
57454              * Fires when a date is selected
57455              * @param {DatePicker} this
57456              * @param {Date} date The selected date
57457              */
57458         'select': true,
57459         /**
57460              * @event monthchange
57461              * Fires when the displayed month changes 
57462              * @param {DatePicker} this
57463              * @param {Date} date The selected month
57464              */
57465         'monthchange': true,
57466         /**
57467              * @event evententer
57468              * Fires when mouse over an event
57469              * @param {Calendar} this
57470              * @param {event} Event
57471              */
57472         'evententer': true,
57473         /**
57474              * @event eventleave
57475              * Fires when the mouse leaves an
57476              * @param {Calendar} this
57477              * @param {event}
57478              */
57479         'eventleave': true,
57480         /**
57481              * @event eventclick
57482              * Fires when the mouse click an
57483              * @param {Calendar} this
57484              * @param {event}
57485              */
57486         'eventclick': true,
57487         /**
57488              * @event eventrender
57489              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57490              * @param {Calendar} this
57491              * @param {data} data to be modified
57492              */
57493         'eventrender': true
57494         
57495     });
57496
57497     Roo.grid.Grid.superclass.constructor.call(this);
57498     this.on('render', function() {
57499         this.view.el.addClass('x-grid-cal'); 
57500         
57501         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57502
57503     },this);
57504     
57505     if (!Roo.grid.Calendar.style) {
57506         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57507             
57508             
57509             '.x-grid-cal .x-grid-col' :  {
57510                 height: 'auto !important',
57511                 'vertical-align': 'top'
57512             },
57513             '.x-grid-cal  .fc-event-hori' : {
57514                 height: '14px'
57515             }
57516              
57517             
57518         }, Roo.id());
57519     }
57520
57521     
57522     
57523 };
57524 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57525     /**
57526      * @cfg {Store} eventStore The store that loads events.
57527      */
57528     eventStore : 25,
57529
57530      
57531     activeDate : false,
57532     startDay : 0,
57533     autoWidth : true,
57534     monitorWindowResize : false,
57535
57536     
57537     resizeColumns : function() {
57538         var col = (this.view.el.getWidth() / 7) - 3;
57539         // loop through cols, and setWidth
57540         for(var i =0 ; i < 7 ; i++){
57541             this.cm.setColumnWidth(i, col);
57542         }
57543     },
57544      setDate :function(date) {
57545         
57546         Roo.log('setDate?');
57547         
57548         this.resizeColumns();
57549         var vd = this.activeDate;
57550         this.activeDate = date;
57551 //        if(vd && this.el){
57552 //            var t = date.getTime();
57553 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57554 //                Roo.log('using add remove');
57555 //                
57556 //                this.fireEvent('monthchange', this, date);
57557 //                
57558 //                this.cells.removeClass("fc-state-highlight");
57559 //                this.cells.each(function(c){
57560 //                   if(c.dateValue == t){
57561 //                       c.addClass("fc-state-highlight");
57562 //                       setTimeout(function(){
57563 //                            try{c.dom.firstChild.focus();}catch(e){}
57564 //                       }, 50);
57565 //                       return false;
57566 //                   }
57567 //                   return true;
57568 //                });
57569 //                return;
57570 //            }
57571 //        }
57572         
57573         var days = date.getDaysInMonth();
57574         
57575         var firstOfMonth = date.getFirstDateOfMonth();
57576         var startingPos = firstOfMonth.getDay()-this.startDay;
57577         
57578         if(startingPos < this.startDay){
57579             startingPos += 7;
57580         }
57581         
57582         var pm = date.add(Date.MONTH, -1);
57583         var prevStart = pm.getDaysInMonth()-startingPos;
57584 //        
57585         
57586         
57587         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57588         
57589         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57590         //this.cells.addClassOnOver('fc-state-hover');
57591         
57592         var cells = this.cells.elements;
57593         var textEls = this.textNodes;
57594         
57595         //Roo.each(cells, function(cell){
57596         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57597         //});
57598         
57599         days += startingPos;
57600
57601         // convert everything to numbers so it's fast
57602         var day = 86400000;
57603         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57604         //Roo.log(d);
57605         //Roo.log(pm);
57606         //Roo.log(prevStart);
57607         
57608         var today = new Date().clearTime().getTime();
57609         var sel = date.clearTime().getTime();
57610         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57611         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57612         var ddMatch = this.disabledDatesRE;
57613         var ddText = this.disabledDatesText;
57614         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57615         var ddaysText = this.disabledDaysText;
57616         var format = this.format;
57617         
57618         var setCellClass = function(cal, cell){
57619             
57620             //Roo.log('set Cell Class');
57621             cell.title = "";
57622             var t = d.getTime();
57623             
57624             //Roo.log(d);
57625             
57626             
57627             cell.dateValue = t;
57628             if(t == today){
57629                 cell.className += " fc-today";
57630                 cell.className += " fc-state-highlight";
57631                 cell.title = cal.todayText;
57632             }
57633             if(t == sel){
57634                 // disable highlight in other month..
57635                 cell.className += " fc-state-highlight";
57636                 
57637             }
57638             // disabling
57639             if(t < min) {
57640                 //cell.className = " fc-state-disabled";
57641                 cell.title = cal.minText;
57642                 return;
57643             }
57644             if(t > max) {
57645                 //cell.className = " fc-state-disabled";
57646                 cell.title = cal.maxText;
57647                 return;
57648             }
57649             if(ddays){
57650                 if(ddays.indexOf(d.getDay()) != -1){
57651                     // cell.title = ddaysText;
57652                    // cell.className = " fc-state-disabled";
57653                 }
57654             }
57655             if(ddMatch && format){
57656                 var fvalue = d.dateFormat(format);
57657                 if(ddMatch.test(fvalue)){
57658                     cell.title = ddText.replace("%0", fvalue);
57659                    cell.className = " fc-state-disabled";
57660                 }
57661             }
57662             
57663             if (!cell.initialClassName) {
57664                 cell.initialClassName = cell.dom.className;
57665             }
57666             
57667             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57668         };
57669
57670         var i = 0;
57671         
57672         for(; i < startingPos; i++) {
57673             cells[i].dayName =  (++prevStart);
57674             Roo.log(textEls[i]);
57675             d.setDate(d.getDate()+1);
57676             
57677             //cells[i].className = "fc-past fc-other-month";
57678             setCellClass(this, cells[i]);
57679         }
57680         
57681         var intDay = 0;
57682         
57683         for(; i < days; i++){
57684             intDay = i - startingPos + 1;
57685             cells[i].dayName =  (intDay);
57686             d.setDate(d.getDate()+1);
57687             
57688             cells[i].className = ''; // "x-date-active";
57689             setCellClass(this, cells[i]);
57690         }
57691         var extraDays = 0;
57692         
57693         for(; i < 42; i++) {
57694             //textEls[i].innerHTML = (++extraDays);
57695             
57696             d.setDate(d.getDate()+1);
57697             cells[i].dayName = (++extraDays);
57698             cells[i].className = "fc-future fc-other-month";
57699             setCellClass(this, cells[i]);
57700         }
57701         
57702         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57703         
57704         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57705         
57706         // this will cause all the cells to mis
57707         var rows= [];
57708         var i =0;
57709         for (var r = 0;r < 6;r++) {
57710             for (var c =0;c < 7;c++) {
57711                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57712             }    
57713         }
57714         
57715         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57716         for(i=0;i<cells.length;i++) {
57717             
57718             this.cells.elements[i].dayName = cells[i].dayName ;
57719             this.cells.elements[i].className = cells[i].className;
57720             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57721             this.cells.elements[i].title = cells[i].title ;
57722             this.cells.elements[i].dateValue = cells[i].dateValue ;
57723         }
57724         
57725         
57726         
57727         
57728         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57729         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57730         
57731         ////if(totalRows != 6){
57732             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57733            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57734        // }
57735         
57736         this.fireEvent('monthchange', this, date);
57737         
57738         
57739     },
57740  /**
57741      * Returns the grid's SelectionModel.
57742      * @return {SelectionModel}
57743      */
57744     getSelectionModel : function(){
57745         if(!this.selModel){
57746             this.selModel = new Roo.grid.CellSelectionModel();
57747         }
57748         return this.selModel;
57749     },
57750
57751     load: function() {
57752         this.eventStore.load()
57753         
57754         
57755         
57756     },
57757     
57758     findCell : function(dt) {
57759         dt = dt.clearTime().getTime();
57760         var ret = false;
57761         this.cells.each(function(c){
57762             //Roo.log("check " +c.dateValue + '?=' + dt);
57763             if(c.dateValue == dt){
57764                 ret = c;
57765                 return false;
57766             }
57767             return true;
57768         });
57769         
57770         return ret;
57771     },
57772     
57773     findCells : function(rec) {
57774         var s = rec.data.start_dt.clone().clearTime().getTime();
57775        // Roo.log(s);
57776         var e= rec.data.end_dt.clone().clearTime().getTime();
57777        // Roo.log(e);
57778         var ret = [];
57779         this.cells.each(function(c){
57780              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57781             
57782             if(c.dateValue > e){
57783                 return ;
57784             }
57785             if(c.dateValue < s){
57786                 return ;
57787             }
57788             ret.push(c);
57789         });
57790         
57791         return ret;    
57792     },
57793     
57794     findBestRow: function(cells)
57795     {
57796         var ret = 0;
57797         
57798         for (var i =0 ; i < cells.length;i++) {
57799             ret  = Math.max(cells[i].rows || 0,ret);
57800         }
57801         return ret;
57802         
57803     },
57804     
57805     
57806     addItem : function(rec)
57807     {
57808         // look for vertical location slot in
57809         var cells = this.findCells(rec);
57810         
57811         rec.row = this.findBestRow(cells);
57812         
57813         // work out the location.
57814         
57815         var crow = false;
57816         var rows = [];
57817         for(var i =0; i < cells.length; i++) {
57818             if (!crow) {
57819                 crow = {
57820                     start : cells[i],
57821                     end :  cells[i]
57822                 };
57823                 continue;
57824             }
57825             if (crow.start.getY() == cells[i].getY()) {
57826                 // on same row.
57827                 crow.end = cells[i];
57828                 continue;
57829             }
57830             // different row.
57831             rows.push(crow);
57832             crow = {
57833                 start: cells[i],
57834                 end : cells[i]
57835             };
57836             
57837         }
57838         
57839         rows.push(crow);
57840         rec.els = [];
57841         rec.rows = rows;
57842         rec.cells = cells;
57843         for (var i = 0; i < cells.length;i++) {
57844             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57845             
57846         }
57847         
57848         
57849     },
57850     
57851     clearEvents: function() {
57852         
57853         if (!this.eventStore.getCount()) {
57854             return;
57855         }
57856         // reset number of rows in cells.
57857         Roo.each(this.cells.elements, function(c){
57858             c.rows = 0;
57859         });
57860         
57861         this.eventStore.each(function(e) {
57862             this.clearEvent(e);
57863         },this);
57864         
57865     },
57866     
57867     clearEvent : function(ev)
57868     {
57869         if (ev.els) {
57870             Roo.each(ev.els, function(el) {
57871                 el.un('mouseenter' ,this.onEventEnter, this);
57872                 el.un('mouseleave' ,this.onEventLeave, this);
57873                 el.remove();
57874             },this);
57875             ev.els = [];
57876         }
57877     },
57878     
57879     
57880     renderEvent : function(ev,ctr) {
57881         if (!ctr) {
57882              ctr = this.view.el.select('.fc-event-container',true).first();
57883         }
57884         
57885          
57886         this.clearEvent(ev);
57887             //code
57888        
57889         
57890         
57891         ev.els = [];
57892         var cells = ev.cells;
57893         var rows = ev.rows;
57894         this.fireEvent('eventrender', this, ev);
57895         
57896         for(var i =0; i < rows.length; i++) {
57897             
57898             cls = '';
57899             if (i == 0) {
57900                 cls += ' fc-event-start';
57901             }
57902             if ((i+1) == rows.length) {
57903                 cls += ' fc-event-end';
57904             }
57905             
57906             //Roo.log(ev.data);
57907             // how many rows should it span..
57908             var cg = this.eventTmpl.append(ctr,Roo.apply({
57909                 fccls : cls
57910                 
57911             }, ev.data) , true);
57912             
57913             
57914             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57915             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57916             cg.on('click', this.onEventClick, this, ev);
57917             
57918             ev.els.push(cg);
57919             
57920             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57921             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57922             //Roo.log(cg);
57923              
57924             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57925             cg.setWidth(ebox.right - sbox.x -2);
57926         }
57927     },
57928     
57929     renderEvents: function()
57930     {   
57931         // first make sure there is enough space..
57932         
57933         if (!this.eventTmpl) {
57934             this.eventTmpl = new Roo.Template(
57935                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57936                     '<div class="fc-event-inner">' +
57937                         '<span class="fc-event-time">{time}</span>' +
57938                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57939                     '</div>' +
57940                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57941                 '</div>'
57942             );
57943                 
57944         }
57945                
57946         
57947         
57948         this.cells.each(function(c) {
57949             //Roo.log(c.select('.fc-day-content div',true).first());
57950             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57951         });
57952         
57953         var ctr = this.view.el.select('.fc-event-container',true).first();
57954         
57955         var cls;
57956         this.eventStore.each(function(ev){
57957             
57958             this.renderEvent(ev);
57959              
57960              
57961         }, this);
57962         this.view.layout();
57963         
57964     },
57965     
57966     onEventEnter: function (e, el,event,d) {
57967         this.fireEvent('evententer', this, el, event);
57968     },
57969     
57970     onEventLeave: function (e, el,event,d) {
57971         this.fireEvent('eventleave', this, el, event);
57972     },
57973     
57974     onEventClick: function (e, el,event,d) {
57975         this.fireEvent('eventclick', this, el, event);
57976     },
57977     
57978     onMonthChange: function () {
57979         this.store.load();
57980     },
57981     
57982     onLoad: function () {
57983         
57984         //Roo.log('calendar onload');
57985 //         
57986         if(this.eventStore.getCount() > 0){
57987             
57988            
57989             
57990             this.eventStore.each(function(d){
57991                 
57992                 
57993                 // FIXME..
57994                 var add =   d.data;
57995                 if (typeof(add.end_dt) == 'undefined')  {
57996                     Roo.log("Missing End time in calendar data: ");
57997                     Roo.log(d);
57998                     return;
57999                 }
58000                 if (typeof(add.start_dt) == 'undefined')  {
58001                     Roo.log("Missing Start time in calendar data: ");
58002                     Roo.log(d);
58003                     return;
58004                 }
58005                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58006                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58007                 add.id = add.id || d.id;
58008                 add.title = add.title || '??';
58009                 
58010                 this.addItem(d);
58011                 
58012              
58013             },this);
58014         }
58015         
58016         this.renderEvents();
58017     }
58018     
58019
58020 });
58021 /*
58022  grid : {
58023                 xtype: 'Grid',
58024                 xns: Roo.grid,
58025                 listeners : {
58026                     render : function ()
58027                     {
58028                         _this.grid = this;
58029                         
58030                         if (!this.view.el.hasClass('course-timesheet')) {
58031                             this.view.el.addClass('course-timesheet');
58032                         }
58033                         if (this.tsStyle) {
58034                             this.ds.load({});
58035                             return; 
58036                         }
58037                         Roo.log('width');
58038                         Roo.log(_this.grid.view.el.getWidth());
58039                         
58040                         
58041                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58042                             '.course-timesheet .x-grid-row' : {
58043                                 height: '80px'
58044                             },
58045                             '.x-grid-row td' : {
58046                                 'vertical-align' : 0
58047                             },
58048                             '.course-edit-link' : {
58049                                 'color' : 'blue',
58050                                 'text-overflow' : 'ellipsis',
58051                                 'overflow' : 'hidden',
58052                                 'white-space' : 'nowrap',
58053                                 'cursor' : 'pointer'
58054                             },
58055                             '.sub-link' : {
58056                                 'color' : 'green'
58057                             },
58058                             '.de-act-sup-link' : {
58059                                 'color' : 'purple',
58060                                 'text-decoration' : 'line-through'
58061                             },
58062                             '.de-act-link' : {
58063                                 'color' : 'red',
58064                                 'text-decoration' : 'line-through'
58065                             },
58066                             '.course-timesheet .course-highlight' : {
58067                                 'border-top-style': 'dashed !important',
58068                                 'border-bottom-bottom': 'dashed !important'
58069                             },
58070                             '.course-timesheet .course-item' : {
58071                                 'font-family'   : 'tahoma, arial, helvetica',
58072                                 'font-size'     : '11px',
58073                                 'overflow'      : 'hidden',
58074                                 'padding-left'  : '10px',
58075                                 'padding-right' : '10px',
58076                                 'padding-top' : '10px' 
58077                             }
58078                             
58079                         }, Roo.id());
58080                                 this.ds.load({});
58081                     }
58082                 },
58083                 autoWidth : true,
58084                 monitorWindowResize : false,
58085                 cellrenderer : function(v,x,r)
58086                 {
58087                     return v;
58088                 },
58089                 sm : {
58090                     xtype: 'CellSelectionModel',
58091                     xns: Roo.grid
58092                 },
58093                 dataSource : {
58094                     xtype: 'Store',
58095                     xns: Roo.data,
58096                     listeners : {
58097                         beforeload : function (_self, options)
58098                         {
58099                             options.params = options.params || {};
58100                             options.params._month = _this.monthField.getValue();
58101                             options.params.limit = 9999;
58102                             options.params['sort'] = 'when_dt';    
58103                             options.params['dir'] = 'ASC';    
58104                             this.proxy.loadResponse = this.loadResponse;
58105                             Roo.log("load?");
58106                             //this.addColumns();
58107                         },
58108                         load : function (_self, records, options)
58109                         {
58110                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58111                                 // if you click on the translation.. you can edit it...
58112                                 var el = Roo.get(this);
58113                                 var id = el.dom.getAttribute('data-id');
58114                                 var d = el.dom.getAttribute('data-date');
58115                                 var t = el.dom.getAttribute('data-time');
58116                                 //var id = this.child('span').dom.textContent;
58117                                 
58118                                 //Roo.log(this);
58119                                 Pman.Dialog.CourseCalendar.show({
58120                                     id : id,
58121                                     when_d : d,
58122                                     when_t : t,
58123                                     productitem_active : id ? 1 : 0
58124                                 }, function() {
58125                                     _this.grid.ds.load({});
58126                                 });
58127                            
58128                            });
58129                            
58130                            _this.panel.fireEvent('resize', [ '', '' ]);
58131                         }
58132                     },
58133                     loadResponse : function(o, success, response){
58134                             // this is overridden on before load..
58135                             
58136                             Roo.log("our code?");       
58137                             //Roo.log(success);
58138                             //Roo.log(response)
58139                             delete this.activeRequest;
58140                             if(!success){
58141                                 this.fireEvent("loadexception", this, o, response);
58142                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58143                                 return;
58144                             }
58145                             var result;
58146                             try {
58147                                 result = o.reader.read(response);
58148                             }catch(e){
58149                                 Roo.log("load exception?");
58150                                 this.fireEvent("loadexception", this, o, response, e);
58151                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58152                                 return;
58153                             }
58154                             Roo.log("ready...");        
58155                             // loop through result.records;
58156                             // and set this.tdate[date] = [] << array of records..
58157                             _this.tdata  = {};
58158                             Roo.each(result.records, function(r){
58159                                 //Roo.log(r.data);
58160                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58161                                     _this.tdata[r.data.when_dt.format('j')] = [];
58162                                 }
58163                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58164                             });
58165                             
58166                             //Roo.log(_this.tdata);
58167                             
58168                             result.records = [];
58169                             result.totalRecords = 6;
58170                     
58171                             // let's generate some duumy records for the rows.
58172                             //var st = _this.dateField.getValue();
58173                             
58174                             // work out monday..
58175                             //st = st.add(Date.DAY, -1 * st.format('w'));
58176                             
58177                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58178                             
58179                             var firstOfMonth = date.getFirstDayOfMonth();
58180                             var days = date.getDaysInMonth();
58181                             var d = 1;
58182                             var firstAdded = false;
58183                             for (var i = 0; i < result.totalRecords ; i++) {
58184                                 //var d= st.add(Date.DAY, i);
58185                                 var row = {};
58186                                 var added = 0;
58187                                 for(var w = 0 ; w < 7 ; w++){
58188                                     if(!firstAdded && firstOfMonth != w){
58189                                         continue;
58190                                     }
58191                                     if(d > days){
58192                                         continue;
58193                                     }
58194                                     firstAdded = true;
58195                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58196                                     row['weekday'+w] = String.format(
58197                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58198                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58199                                                     d,
58200                                                     date.format('Y-m-')+dd
58201                                                 );
58202                                     added++;
58203                                     if(typeof(_this.tdata[d]) != 'undefined'){
58204                                         Roo.each(_this.tdata[d], function(r){
58205                                             var is_sub = '';
58206                                             var deactive = '';
58207                                             var id = r.id;
58208                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58209                                             if(r.parent_id*1>0){
58210                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58211                                                 id = r.parent_id;
58212                                             }
58213                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58214                                                 deactive = 'de-act-link';
58215                                             }
58216                                             
58217                                             row['weekday'+w] += String.format(
58218                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58219                                                     id, //0
58220                                                     r.product_id_name, //1
58221                                                     r.when_dt.format('h:ia'), //2
58222                                                     is_sub, //3
58223                                                     deactive, //4
58224                                                     desc // 5
58225                                             );
58226                                         });
58227                                     }
58228                                     d++;
58229                                 }
58230                                 
58231                                 // only do this if something added..
58232                                 if(added > 0){ 
58233                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58234                                 }
58235                                 
58236                                 
58237                                 // push it twice. (second one with an hour..
58238                                 
58239                             }
58240                             //Roo.log(result);
58241                             this.fireEvent("load", this, o, o.request.arg);
58242                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58243                         },
58244                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58245                     proxy : {
58246                         xtype: 'HttpProxy',
58247                         xns: Roo.data,
58248                         method : 'GET',
58249                         url : baseURL + '/Roo/Shop_course.php'
58250                     },
58251                     reader : {
58252                         xtype: 'JsonReader',
58253                         xns: Roo.data,
58254                         id : 'id',
58255                         fields : [
58256                             {
58257                                 'name': 'id',
58258                                 'type': 'int'
58259                             },
58260                             {
58261                                 'name': 'when_dt',
58262                                 'type': 'string'
58263                             },
58264                             {
58265                                 'name': 'end_dt',
58266                                 'type': 'string'
58267                             },
58268                             {
58269                                 'name': 'parent_id',
58270                                 'type': 'int'
58271                             },
58272                             {
58273                                 'name': 'product_id',
58274                                 'type': 'int'
58275                             },
58276                             {
58277                                 'name': 'productitem_id',
58278                                 'type': 'int'
58279                             },
58280                             {
58281                                 'name': 'guid',
58282                                 'type': 'int'
58283                             }
58284                         ]
58285                     }
58286                 },
58287                 toolbar : {
58288                     xtype: 'Toolbar',
58289                     xns: Roo,
58290                     items : [
58291                         {
58292                             xtype: 'Button',
58293                             xns: Roo.Toolbar,
58294                             listeners : {
58295                                 click : function (_self, e)
58296                                 {
58297                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58298                                     sd.setMonth(sd.getMonth()-1);
58299                                     _this.monthField.setValue(sd.format('Y-m-d'));
58300                                     _this.grid.ds.load({});
58301                                 }
58302                             },
58303                             text : "Back"
58304                         },
58305                         {
58306                             xtype: 'Separator',
58307                             xns: Roo.Toolbar
58308                         },
58309                         {
58310                             xtype: 'MonthField',
58311                             xns: Roo.form,
58312                             listeners : {
58313                                 render : function (_self)
58314                                 {
58315                                     _this.monthField = _self;
58316                                    // _this.monthField.set  today
58317                                 },
58318                                 select : function (combo, date)
58319                                 {
58320                                     _this.grid.ds.load({});
58321                                 }
58322                             },
58323                             value : (function() { return new Date(); })()
58324                         },
58325                         {
58326                             xtype: 'Separator',
58327                             xns: Roo.Toolbar
58328                         },
58329                         {
58330                             xtype: 'TextItem',
58331                             xns: Roo.Toolbar,
58332                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58333                         },
58334                         {
58335                             xtype: 'Fill',
58336                             xns: Roo.Toolbar
58337                         },
58338                         {
58339                             xtype: 'Button',
58340                             xns: Roo.Toolbar,
58341                             listeners : {
58342                                 click : function (_self, e)
58343                                 {
58344                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58345                                     sd.setMonth(sd.getMonth()+1);
58346                                     _this.monthField.setValue(sd.format('Y-m-d'));
58347                                     _this.grid.ds.load({});
58348                                 }
58349                             },
58350                             text : "Next"
58351                         }
58352                     ]
58353                 },
58354                  
58355             }
58356         };
58357         
58358         *//*
58359  * Based on:
58360  * Ext JS Library 1.1.1
58361  * Copyright(c) 2006-2007, Ext JS, LLC.
58362  *
58363  * Originally Released Under LGPL - original licence link has changed is not relivant.
58364  *
58365  * Fork - LGPL
58366  * <script type="text/javascript">
58367  */
58368  
58369 /**
58370  * @class Roo.LoadMask
58371  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58372  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58373  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58374  * element's UpdateManager load indicator and will be destroyed after the initial load.
58375  * @constructor
58376  * Create a new LoadMask
58377  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58378  * @param {Object} config The config object
58379  */
58380 Roo.LoadMask = function(el, config){
58381     this.el = Roo.get(el);
58382     Roo.apply(this, config);
58383     if(this.store){
58384         this.store.on('beforeload', this.onBeforeLoad, this);
58385         this.store.on('load', this.onLoad, this);
58386         this.store.on('loadexception', this.onLoadException, this);
58387         this.removeMask = false;
58388     }else{
58389         var um = this.el.getUpdateManager();
58390         um.showLoadIndicator = false; // disable the default indicator
58391         um.on('beforeupdate', this.onBeforeLoad, this);
58392         um.on('update', this.onLoad, this);
58393         um.on('failure', this.onLoad, this);
58394         this.removeMask = true;
58395     }
58396 };
58397
58398 Roo.LoadMask.prototype = {
58399     /**
58400      * @cfg {Boolean} removeMask
58401      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58402      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58403      */
58404     /**
58405      * @cfg {String} msg
58406      * The text to display in a centered loading message box (defaults to 'Loading...')
58407      */
58408     msg : 'Loading...',
58409     /**
58410      * @cfg {String} msgCls
58411      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58412      */
58413     msgCls : 'x-mask-loading',
58414
58415     /**
58416      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58417      * @type Boolean
58418      */
58419     disabled: false,
58420
58421     /**
58422      * Disables the mask to prevent it from being displayed
58423      */
58424     disable : function(){
58425        this.disabled = true;
58426     },
58427
58428     /**
58429      * Enables the mask so that it can be displayed
58430      */
58431     enable : function(){
58432         this.disabled = false;
58433     },
58434     
58435     onLoadException : function()
58436     {
58437         Roo.log(arguments);
58438         
58439         if (typeof(arguments[3]) != 'undefined') {
58440             Roo.MessageBox.alert("Error loading",arguments[3]);
58441         } 
58442         /*
58443         try {
58444             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58445                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58446             }   
58447         } catch(e) {
58448             
58449         }
58450         */
58451     
58452         
58453         
58454         this.el.unmask(this.removeMask);
58455     },
58456     // private
58457     onLoad : function()
58458     {
58459         this.el.unmask(this.removeMask);
58460     },
58461
58462     // private
58463     onBeforeLoad : function(){
58464         if(!this.disabled){
58465             this.el.mask(this.msg, this.msgCls);
58466         }
58467     },
58468
58469     // private
58470     destroy : function(){
58471         if(this.store){
58472             this.store.un('beforeload', this.onBeforeLoad, this);
58473             this.store.un('load', this.onLoad, this);
58474             this.store.un('loadexception', this.onLoadException, this);
58475         }else{
58476             var um = this.el.getUpdateManager();
58477             um.un('beforeupdate', this.onBeforeLoad, this);
58478             um.un('update', this.onLoad, this);
58479             um.un('failure', this.onLoad, this);
58480         }
58481     }
58482 };/*
58483  * Based on:
58484  * Ext JS Library 1.1.1
58485  * Copyright(c) 2006-2007, Ext JS, LLC.
58486  *
58487  * Originally Released Under LGPL - original licence link has changed is not relivant.
58488  *
58489  * Fork - LGPL
58490  * <script type="text/javascript">
58491  */
58492
58493
58494 /**
58495  * @class Roo.XTemplate
58496  * @extends Roo.Template
58497  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58498 <pre><code>
58499 var t = new Roo.XTemplate(
58500         '&lt;select name="{name}"&gt;',
58501                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58502         '&lt;/select&gt;'
58503 );
58504  
58505 // then append, applying the master template values
58506  </code></pre>
58507  *
58508  * Supported features:
58509  *
58510  *  Tags:
58511
58512 <pre><code>
58513       {a_variable} - output encoded.
58514       {a_variable.format:("Y-m-d")} - call a method on the variable
58515       {a_variable:raw} - unencoded output
58516       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58517       {a_variable:this.method_on_template(...)} - call a method on the template object.
58518  
58519 </code></pre>
58520  *  The tpl tag:
58521 <pre><code>
58522         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58523         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58524         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58525         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58526   
58527         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58528         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58529 </code></pre>
58530  *      
58531  */
58532 Roo.XTemplate = function()
58533 {
58534     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58535     if (this.html) {
58536         this.compile();
58537     }
58538 };
58539
58540
58541 Roo.extend(Roo.XTemplate, Roo.Template, {
58542
58543     /**
58544      * The various sub templates
58545      */
58546     tpls : false,
58547     /**
58548      *
58549      * basic tag replacing syntax
58550      * WORD:WORD()
58551      *
58552      * // you can fake an object call by doing this
58553      *  x.t:(test,tesT) 
58554      * 
58555      */
58556     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58557
58558     /**
58559      * compile the template
58560      *
58561      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58562      *
58563      */
58564     compile: function()
58565     {
58566         var s = this.html;
58567      
58568         s = ['<tpl>', s, '</tpl>'].join('');
58569     
58570         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58571             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58572             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58573             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58574             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58575             m,
58576             id     = 0,
58577             tpls   = [];
58578     
58579         while(true == !!(m = s.match(re))){
58580             var forMatch   = m[0].match(nameRe),
58581                 ifMatch   = m[0].match(ifRe),
58582                 execMatch   = m[0].match(execRe),
58583                 namedMatch   = m[0].match(namedRe),
58584                 
58585                 exp  = null, 
58586                 fn   = null,
58587                 exec = null,
58588                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58589                 
58590             if (ifMatch) {
58591                 // if - puts fn into test..
58592                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58593                 if(exp){
58594                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58595                 }
58596             }
58597             
58598             if (execMatch) {
58599                 // exec - calls a function... returns empty if true is  returned.
58600                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58601                 if(exp){
58602                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58603                 }
58604             }
58605             
58606             
58607             if (name) {
58608                 // for = 
58609                 switch(name){
58610                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58611                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58612                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58613                 }
58614             }
58615             var uid = namedMatch ? namedMatch[1] : id;
58616             
58617             
58618             tpls.push({
58619                 id:     namedMatch ? namedMatch[1] : id,
58620                 target: name,
58621                 exec:   exec,
58622                 test:   fn,
58623                 body:   m[1] || ''
58624             });
58625             if (namedMatch) {
58626                 s = s.replace(m[0], '');
58627             } else { 
58628                 s = s.replace(m[0], '{xtpl'+ id + '}');
58629             }
58630             ++id;
58631         }
58632         this.tpls = [];
58633         for(var i = tpls.length-1; i >= 0; --i){
58634             this.compileTpl(tpls[i]);
58635             this.tpls[tpls[i].id] = tpls[i];
58636         }
58637         this.master = tpls[tpls.length-1];
58638         return this;
58639     },
58640     /**
58641      * same as applyTemplate, except it's done to one of the subTemplates
58642      * when using named templates, you can do:
58643      *
58644      * var str = pl.applySubTemplate('your-name', values);
58645      *
58646      * 
58647      * @param {Number} id of the template
58648      * @param {Object} values to apply to template
58649      * @param {Object} parent (normaly the instance of this object)
58650      */
58651     applySubTemplate : function(id, values, parent)
58652     {
58653         
58654         
58655         var t = this.tpls[id];
58656         
58657         
58658         try { 
58659             if(t.test && !t.test.call(this, values, parent)){
58660                 return '';
58661             }
58662         } catch(e) {
58663             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58664             Roo.log(e.toString());
58665             Roo.log(t.test);
58666             return ''
58667         }
58668         try { 
58669             
58670             if(t.exec && t.exec.call(this, values, parent)){
58671                 return '';
58672             }
58673         } catch(e) {
58674             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58675             Roo.log(e.toString());
58676             Roo.log(t.exec);
58677             return ''
58678         }
58679         try {
58680             var vs = t.target ? t.target.call(this, values, parent) : values;
58681             parent = t.target ? values : parent;
58682             if(t.target && vs instanceof Array){
58683                 var buf = [];
58684                 for(var i = 0, len = vs.length; i < len; i++){
58685                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58686                 }
58687                 return buf.join('');
58688             }
58689             return t.compiled.call(this, vs, parent);
58690         } catch (e) {
58691             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58692             Roo.log(e.toString());
58693             Roo.log(t.compiled);
58694             return '';
58695         }
58696     },
58697
58698     compileTpl : function(tpl)
58699     {
58700         var fm = Roo.util.Format;
58701         var useF = this.disableFormats !== true;
58702         var sep = Roo.isGecko ? "+" : ",";
58703         var undef = function(str) {
58704             Roo.log("Property not found :"  + str);
58705             return '';
58706         };
58707         
58708         var fn = function(m, name, format, args)
58709         {
58710             //Roo.log(arguments);
58711             args = args ? args.replace(/\\'/g,"'") : args;
58712             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58713             if (typeof(format) == 'undefined') {
58714                 format= 'htmlEncode';
58715             }
58716             if (format == 'raw' ) {
58717                 format = false;
58718             }
58719             
58720             if(name.substr(0, 4) == 'xtpl'){
58721                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58722             }
58723             
58724             // build an array of options to determine if value is undefined..
58725             
58726             // basically get 'xxxx.yyyy' then do
58727             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58728             //    (function () { Roo.log("Property not found"); return ''; })() :
58729             //    ......
58730             
58731             var udef_ar = [];
58732             var lookfor = '';
58733             Roo.each(name.split('.'), function(st) {
58734                 lookfor += (lookfor.length ? '.': '') + st;
58735                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58736             });
58737             
58738             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58739             
58740             
58741             if(format && useF){
58742                 
58743                 args = args ? ',' + args : "";
58744                  
58745                 if(format.substr(0, 5) != "this."){
58746                     format = "fm." + format + '(';
58747                 }else{
58748                     format = 'this.call("'+ format.substr(5) + '", ';
58749                     args = ", values";
58750                 }
58751                 
58752                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58753             }
58754              
58755             if (args.length) {
58756                 // called with xxyx.yuu:(test,test)
58757                 // change to ()
58758                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58759             }
58760             // raw.. - :raw modifier..
58761             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58762             
58763         };
58764         var body;
58765         // branched to use + in gecko and [].join() in others
58766         if(Roo.isGecko){
58767             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58768                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58769                     "';};};";
58770         }else{
58771             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58772             body.push(tpl.body.replace(/(\r\n|\n)/g,
58773                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58774             body.push("'].join('');};};");
58775             body = body.join('');
58776         }
58777         
58778         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58779        
58780         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58781         eval(body);
58782         
58783         return this;
58784     },
58785
58786     applyTemplate : function(values){
58787         return this.master.compiled.call(this, values, {});
58788         //var s = this.subs;
58789     },
58790
58791     apply : function(){
58792         return this.applyTemplate.apply(this, arguments);
58793     }
58794
58795  });
58796
58797 Roo.XTemplate.from = function(el){
58798     el = Roo.getDom(el);
58799     return new Roo.XTemplate(el.value || el.innerHTML);
58800 };