roojs-ui.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isGecko = !isSafari && ua.indexOf("gecko") > -1,
61         isBorderBox = isIE && !isStrict,
62         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
63         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
64         isLinux = (ua.indexOf("linux") != -1),
65         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
66         isIOS = /iphone|ipad/.test(ua),
67         isTouch =  (function() {
68             try {  
69                 document.createEvent("TouchEvent");  
70                 return true;  
71             } catch (e) {  
72                 return false;  
73             } 
74             
75         })();
76     // remove css image flicker
77         if(isIE && !isIE7){
78         try{
79             document.execCommand("BackgroundImageCache", false, true);
80         }catch(e){}
81     }
82     
83     Roo.apply(Roo, {
84         /**
85          * True if the browser is in strict mode
86          * @type Boolean
87          */
88         isStrict : isStrict,
89         /**
90          * True if the page is running over SSL
91          * @type Boolean
92          */
93         isSecure : isSecure,
94         /**
95          * True when the document is fully initialized and ready for action
96          * @type Boolean
97          */
98         isReady : false,
99         /**
100          * Turn on debugging output (currently only the factory uses this)
101          * @type Boolean
102          */
103         
104         debug: false,
105
106         /**
107          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
108          * @type Boolean
109          */
110         enableGarbageCollector : true,
111
112         /**
113          * True to automatically purge event listeners after uncaching an element (defaults to false).
114          * Note: this only happens if enableGarbageCollector is true.
115          * @type Boolean
116          */
117         enableListenerCollection:false,
118
119         /**
120          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
121          * the IE insecure content warning (defaults to javascript:false).
122          * @type String
123          */
124         SSL_SECURE_URL : "javascript:false",
125
126         /**
127          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
128          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
129          * @type String
130          */
131         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
132
133         emptyFn : function(){},
134         
135         /**
136          * Copies all the properties of config to obj if they don't already exist.
137          * @param {Object} obj The receiver of the properties
138          * @param {Object} config The source of the properties
139          * @return {Object} returns obj
140          */
141         applyIf : function(o, c){
142             if(o && c){
143                 for(var p in c){
144                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
145                 }
146             }
147             return o;
148         },
149
150         /**
151          * Applies event listeners to elements by selectors when the document is ready.
152          * The event name is specified with an @ suffix.
153 <pre><code>
154 Roo.addBehaviors({
155    // add a listener for click on all anchors in element with id foo
156    '#foo a@click' : function(e, t){
157        // do something
158    },
159
160    // add the same listener to multiple selectors (separated by comma BEFORE the @)
161    '#foo a, #bar span.some-class@mouseover' : function(){
162        // do something
163    }
164 });
165 </code></pre>
166          * @param {Object} obj The list of behaviors to apply
167          */
168         addBehaviors : function(o){
169             if(!Roo.isReady){
170                 Roo.onReady(function(){
171                     Roo.addBehaviors(o);
172                 });
173                 return;
174             }
175             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
176             for(var b in o){
177                 var parts = b.split('@');
178                 if(parts[1]){ // for Object prototype breakers
179                     var s = parts[0];
180                     if(!cache[s]){
181                         cache[s] = Roo.select(s);
182                     }
183                     cache[s].on(parts[1], o[b]);
184                 }
185             }
186             cache = null;
187         },
188
189         /**
190          * Generates unique ids. If the element already has an id, it is unchanged
191          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
192          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
193          * @return {String} The generated Id.
194          */
195         id : function(el, prefix){
196             prefix = prefix || "roo-gen";
197             el = Roo.getDom(el);
198             var id = prefix + (++idSeed);
199             return el ? (el.id ? el.id : (el.id = id)) : id;
200         },
201          
202        
203         /**
204          * Extends one class with another class and optionally overrides members with the passed literal. This class
205          * also adds the function "override()" to the class that can be used to override
206          * members on an instance.
207          * @param {Object} subclass The class inheriting the functionality
208          * @param {Object} superclass The class being extended
209          * @param {Object} overrides (optional) A literal with members
210          * @method extend
211          */
212         extend : function(){
213             // inline overrides
214             var io = function(o){
215                 for(var m in o){
216                     this[m] = o[m];
217                 }
218             };
219             return function(sb, sp, overrides){
220                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
221                     overrides = sp;
222                     sp = sb;
223                     sb = function(){sp.apply(this, arguments);};
224                 }
225                 var F = function(){}, sbp, spp = sp.prototype;
226                 F.prototype = spp;
227                 sbp = sb.prototype = new F();
228                 sbp.constructor=sb;
229                 sb.superclass=spp;
230                 
231                 if(spp.constructor == Object.prototype.constructor){
232                     spp.constructor=sp;
233                    
234                 }
235                 
236                 sb.override = function(o){
237                     Roo.override(sb, o);
238                 };
239                 sbp.override = io;
240                 Roo.override(sb, overrides);
241                 return sb;
242             };
243         }(),
244
245         /**
246          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
247          * Usage:<pre><code>
248 Roo.override(MyClass, {
249     newMethod1: function(){
250         // etc.
251     },
252     newMethod2: function(foo){
253         // etc.
254     }
255 });
256  </code></pre>
257          * @param {Object} origclass The class to override
258          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
259          * containing one or more methods.
260          * @method override
261          */
262         override : function(origclass, overrides){
263             if(overrides){
264                 var p = origclass.prototype;
265                 for(var method in overrides){
266                     p[method] = overrides[method];
267                 }
268             }
269         },
270         /**
271          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
272          * <pre><code>
273 Roo.namespace('Company', 'Company.data');
274 Company.Widget = function() { ... }
275 Company.data.CustomStore = function(config) { ... }
276 </code></pre>
277          * @param {String} namespace1
278          * @param {String} namespace2
279          * @param {String} etc
280          * @method namespace
281          */
282         namespace : function(){
283             var a=arguments, o=null, i, j, d, rt;
284             for (i=0; i<a.length; ++i) {
285                 d=a[i].split(".");
286                 rt = d[0];
287                 /** eval:var:o */
288                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
289                 for (j=1; j<d.length; ++j) {
290                     o[d[j]]=o[d[j]] || {};
291                     o=o[d[j]];
292                 }
293             }
294         },
295         /**
296          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
297          * <pre><code>
298 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
299 Roo.factory(conf, Roo.data);
300 </code></pre>
301          * @param {String} classname
302          * @param {String} namespace (optional)
303          * @method factory
304          */
305          
306         factory : function(c, ns)
307         {
308             // no xtype, no ns or c.xns - or forced off by c.xns
309             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
310                 return c;
311             }
312             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
313             if (c.constructor == ns[c.xtype]) {// already created...
314                 return c;
315             }
316             if (ns[c.xtype]) {
317                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
318                 var ret = new ns[c.xtype](c);
319                 ret.xns = false;
320                 return ret;
321             }
322             c.xns = false; // prevent recursion..
323             return c;
324         },
325          /**
326          * Logs to console if it can.
327          *
328          * @param {String|Object} string
329          * @method log
330          */
331         log : function(s)
332         {
333             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
334                 return; // alerT?
335             }
336             console.log(s);
337             
338         },
339         /**
340          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
341          * @param {Object} o
342          * @return {String}
343          */
344         urlEncode : function(o){
345             if(!o){
346                 return "";
347             }
348             var buf = [];
349             for(var key in o){
350                 var ov = o[key], k = Roo.encodeURIComponent(key);
351                 var type = typeof ov;
352                 if(type == 'undefined'){
353                     buf.push(k, "=&");
354                 }else if(type != "function" && type != "object"){
355                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
356                 }else if(ov instanceof Array){
357                     if (ov.length) {
358                             for(var i = 0, len = ov.length; i < len; i++) {
359                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
360                             }
361                         } else {
362                             buf.push(k, "=&");
363                         }
364                 }
365             }
366             buf.pop();
367             return buf.join("");
368         },
369          /**
370          * Safe version of encodeURIComponent
371          * @param {String} data 
372          * @return {String} 
373          */
374         
375         encodeURIComponent : function (data)
376         {
377             try {
378                 return encodeURIComponent(data);
379             } catch(e) {} // should be an uri encode error.
380             
381             if (data == '' || data == null){
382                return '';
383             }
384             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
385             function nibble_to_hex(nibble){
386                 var chars = '0123456789ABCDEF';
387                 return chars.charAt(nibble);
388             }
389             data = data.toString();
390             var buffer = '';
391             for(var i=0; i<data.length; i++){
392                 var c = data.charCodeAt(i);
393                 var bs = new Array();
394                 if (c > 0x10000){
395                         // 4 bytes
396                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
397                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
398                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
399                     bs[3] = 0x80 | (c & 0x3F);
400                 }else if (c > 0x800){
401                          // 3 bytes
402                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
403                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
404                     bs[2] = 0x80 | (c & 0x3F);
405                 }else if (c > 0x80){
406                        // 2 bytes
407                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
408                     bs[1] = 0x80 | (c & 0x3F);
409                 }else{
410                         // 1 byte
411                     bs[0] = c;
412                 }
413                 for(var j=0; j<bs.length; j++){
414                     var b = bs[j];
415                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
416                             + nibble_to_hex(b &0x0F);
417                     buffer += '%'+hex;
418                }
419             }
420             return buffer;    
421              
422         },
423
424         /**
425          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
426          * @param {String} string
427          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
428          * @return {Object} A literal with members
429          */
430         urlDecode : function(string, overwrite){
431             if(!string || !string.length){
432                 return {};
433             }
434             var obj = {};
435             var pairs = string.split('&');
436             var pair, name, value;
437             for(var i = 0, len = pairs.length; i < len; i++){
438                 pair = pairs[i].split('=');
439                 name = decodeURIComponent(pair[0]);
440                 value = decodeURIComponent(pair[1]);
441                 if(overwrite !== true){
442                     if(typeof obj[name] == "undefined"){
443                         obj[name] = value;
444                     }else if(typeof obj[name] == "string"){
445                         obj[name] = [obj[name]];
446                         obj[name].push(value);
447                     }else{
448                         obj[name].push(value);
449                     }
450                 }else{
451                     obj[name] = value;
452                 }
453             }
454             return obj;
455         },
456
457         /**
458          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
459          * passed array is not really an array, your function is called once with it.
460          * The supplied function is called with (Object item, Number index, Array allItems).
461          * @param {Array/NodeList/Mixed} array
462          * @param {Function} fn
463          * @param {Object} scope
464          */
465         each : function(array, fn, scope){
466             if(typeof array.length == "undefined" || typeof array == "string"){
467                 array = [array];
468             }
469             for(var i = 0, len = array.length; i < len; i++){
470                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
471             }
472         },
473
474         // deprecated
475         combine : function(){
476             var as = arguments, l = as.length, r = [];
477             for(var i = 0; i < l; i++){
478                 var a = as[i];
479                 if(a instanceof Array){
480                     r = r.concat(a);
481                 }else if(a.length !== undefined && !a.substr){
482                     r = r.concat(Array.prototype.slice.call(a, 0));
483                 }else{
484                     r.push(a);
485                 }
486             }
487             return r;
488         },
489
490         /**
491          * Escapes the passed string for use in a regular expression
492          * @param {String} str
493          * @return {String}
494          */
495         escapeRe : function(s) {
496             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
497         },
498
499         // internal
500         callback : function(cb, scope, args, delay){
501             if(typeof cb == "function"){
502                 if(delay){
503                     cb.defer(delay, scope, args || []);
504                 }else{
505                     cb.apply(scope, args || []);
506                 }
507             }
508         },
509
510         /**
511          * Return the dom node for the passed string (id), dom node, or Roo.Element
512          * @param {String/HTMLElement/Roo.Element} el
513          * @return HTMLElement
514          */
515         getDom : function(el){
516             if(!el){
517                 return null;
518             }
519             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
520         },
521
522         /**
523         * Shorthand for {@link Roo.ComponentMgr#get}
524         * @param {String} id
525         * @return Roo.Component
526         */
527         getCmp : function(id){
528             return Roo.ComponentMgr.get(id);
529         },
530          
531         num : function(v, defaultValue){
532             if(typeof v != 'number'){
533                 return defaultValue;
534             }
535             return v;
536         },
537
538         destroy : function(){
539             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
540                 var as = a[i];
541                 if(as){
542                     if(as.dom){
543                         as.removeAllListeners();
544                         as.remove();
545                         continue;
546                     }
547                     if(typeof as.purgeListeners == 'function'){
548                         as.purgeListeners();
549                     }
550                     if(typeof as.destroy == 'function'){
551                         as.destroy();
552                     }
553                 }
554             }
555         },
556
557         // inpired by a similar function in mootools library
558         /**
559          * Returns the type of object that is passed in. If the object passed in is null or undefined it
560          * return false otherwise it returns one of the following values:<ul>
561          * <li><b>string</b>: If the object passed is a string</li>
562          * <li><b>number</b>: If the object passed is a number</li>
563          * <li><b>boolean</b>: If the object passed is a boolean value</li>
564          * <li><b>function</b>: If the object passed is a function reference</li>
565          * <li><b>object</b>: If the object passed is an object</li>
566          * <li><b>array</b>: If the object passed is an array</li>
567          * <li><b>regexp</b>: If the object passed is a regular expression</li>
568          * <li><b>element</b>: If the object passed is a DOM Element</li>
569          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
570          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
571          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
572          * @param {Mixed} object
573          * @return {String}
574          */
575         type : function(o){
576             if(o === undefined || o === null){
577                 return false;
578             }
579             if(o.htmlElement){
580                 return 'element';
581             }
582             var t = typeof o;
583             if(t == 'object' && o.nodeName) {
584                 switch(o.nodeType) {
585                     case 1: return 'element';
586                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
587                 }
588             }
589             if(t == 'object' || t == 'function') {
590                 switch(o.constructor) {
591                     case Array: return 'array';
592                     case RegExp: return 'regexp';
593                 }
594                 if(typeof o.length == 'number' && typeof o.item == 'function') {
595                     return 'nodelist';
596                 }
597             }
598             return t;
599         },
600
601         /**
602          * Returns true if the passed value is null, undefined or an empty string (optional).
603          * @param {Mixed} value The value to test
604          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
605          * @return {Boolean}
606          */
607         isEmpty : function(v, allowBlank){
608             return v === null || v === undefined || (!allowBlank ? v === '' : false);
609         },
610         
611         /** @type Boolean */
612         isOpera : isOpera,
613         /** @type Boolean */
614         isSafari : isSafari,
615         /** @type Boolean */
616         isFirefox : isFirefox,
617         /** @type Boolean */
618         isIE : isIE,
619         /** @type Boolean */
620         isIE7 : isIE7,
621         /** @type Boolean */
622         isIE11 : isIE11,
623         /** @type Boolean */
624         isGecko : isGecko,
625         /** @type Boolean */
626         isBorderBox : isBorderBox,
627         /** @type Boolean */
628         isWindows : isWindows,
629         /** @type Boolean */
630         isLinux : isLinux,
631         /** @type Boolean */
632         isMac : isMac,
633         /** @type Boolean */
634         isIOS : isIOS,
635         /** @type Boolean */
636         isTouch : isTouch,
637
638         /**
639          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
640          * you may want to set this to true.
641          * @type Boolean
642          */
643         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
644         
645         
646                 
647         /**
648          * Selects a single element as a Roo Element
649          * This is about as close as you can get to jQuery's $('do crazy stuff')
650          * @param {String} selector The selector/xpath query
651          * @param {Node} root (optional) The start of the query (defaults to document).
652          * @return {Roo.Element}
653          */
654         selectNode : function(selector, root) 
655         {
656             var node = Roo.DomQuery.selectNode(selector,root);
657             return node ? Roo.get(node) : new Roo.Element(false);
658         }
659         
660     });
661
662
663 })();
664
665 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
666                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
667                 "Roo.app", "Roo.ux",
668                 "Roo.bootstrap",
669                 "Roo.bootstrap.dash");
670 /*
671  * Based on:
672  * Ext JS Library 1.1.1
673  * Copyright(c) 2006-2007, Ext JS, LLC.
674  *
675  * Originally Released Under LGPL - original licence link has changed is not relivant.
676  *
677  * Fork - LGPL
678  * <script type="text/javascript">
679  */
680
681 (function() {    
682     // wrappedn so fnCleanup is not in global scope...
683     if(Roo.isIE) {
684         function fnCleanUp() {
685             var p = Function.prototype;
686             delete p.createSequence;
687             delete p.defer;
688             delete p.createDelegate;
689             delete p.createCallback;
690             delete p.createInterceptor;
691
692             window.detachEvent("onunload", fnCleanUp);
693         }
694         window.attachEvent("onunload", fnCleanUp);
695     }
696 })();
697
698
699 /**
700  * @class Function
701  * These functions are available on every Function object (any JavaScript function).
702  */
703 Roo.apply(Function.prototype, {
704      /**
705      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
706      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
707      * Will create a function that is bound to those 2 args.
708      * @return {Function} The new function
709     */
710     createCallback : function(/*args...*/){
711         // make args available, in function below
712         var args = arguments;
713         var method = this;
714         return function() {
715             return method.apply(window, args);
716         };
717     },
718
719     /**
720      * Creates a delegate (callback) that sets the scope to obj.
721      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
722      * Will create a function that is automatically scoped to this.
723      * @param {Object} obj (optional) The object for which the scope is set
724      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
725      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
726      *                                             if a number the args are inserted at the specified position
727      * @return {Function} The new function
728      */
729     createDelegate : function(obj, args, appendArgs){
730         var method = this;
731         return function() {
732             var callArgs = args || arguments;
733             if(appendArgs === true){
734                 callArgs = Array.prototype.slice.call(arguments, 0);
735                 callArgs = callArgs.concat(args);
736             }else if(typeof appendArgs == "number"){
737                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
738                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
739                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
740             }
741             return method.apply(obj || window, callArgs);
742         };
743     },
744
745     /**
746      * Calls this function after the number of millseconds specified.
747      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
748      * @param {Object} obj (optional) The object for which the scope is set
749      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
750      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
751      *                                             if a number the args are inserted at the specified position
752      * @return {Number} The timeout id that can be used with clearTimeout
753      */
754     defer : function(millis, obj, args, appendArgs){
755         var fn = this.createDelegate(obj, args, appendArgs);
756         if(millis){
757             return setTimeout(fn, millis);
758         }
759         fn();
760         return 0;
761     },
762     /**
763      * Create a combined function call sequence of the original function + the passed function.
764      * The resulting function returns the results of the original function.
765      * The passed fcn is called with the parameters of the original function
766      * @param {Function} fcn The function to sequence
767      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
768      * @return {Function} The new function
769      */
770     createSequence : function(fcn, scope){
771         if(typeof fcn != "function"){
772             return this;
773         }
774         var method = this;
775         return function() {
776             var retval = method.apply(this || window, arguments);
777             fcn.apply(scope || this || window, arguments);
778             return retval;
779         };
780     },
781
782     /**
783      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
784      * The resulting function returns the results of the original function.
785      * The passed fcn is called with the parameters of the original function.
786      * @addon
787      * @param {Function} fcn The function to call before the original
788      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
789      * @return {Function} The new function
790      */
791     createInterceptor : function(fcn, scope){
792         if(typeof fcn != "function"){
793             return this;
794         }
795         var method = this;
796         return function() {
797             fcn.target = this;
798             fcn.method = method;
799             if(fcn.apply(scope || this || window, arguments) === false){
800                 return;
801             }
802             return method.apply(this || window, arguments);
803         };
804     }
805 });
806 /*
807  * Based on:
808  * Ext JS Library 1.1.1
809  * Copyright(c) 2006-2007, Ext JS, LLC.
810  *
811  * Originally Released Under LGPL - original licence link has changed is not relivant.
812  *
813  * Fork - LGPL
814  * <script type="text/javascript">
815  */
816
817 Roo.applyIf(String, {
818     
819     /** @scope String */
820     
821     /**
822      * Escapes the passed string for ' and \
823      * @param {String} string The string to escape
824      * @return {String} The escaped string
825      * @static
826      */
827     escape : function(string) {
828         return string.replace(/('|\\)/g, "\\$1");
829     },
830
831     /**
832      * Pads the left side of a string with a specified character.  This is especially useful
833      * for normalizing number and date strings.  Example usage:
834      * <pre><code>
835 var s = String.leftPad('123', 5, '0');
836 // s now contains the string: '00123'
837 </code></pre>
838      * @param {String} string The original string
839      * @param {Number} size The total length of the output string
840      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
841      * @return {String} The padded string
842      * @static
843      */
844     leftPad : function (val, size, ch) {
845         var result = new String(val);
846         if(ch === null || ch === undefined || ch === '') {
847             ch = " ";
848         }
849         while (result.length < size) {
850             result = ch + result;
851         }
852         return result;
853     },
854
855     /**
856      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
857      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
858      * <pre><code>
859 var cls = 'my-class', text = 'Some text';
860 var s = String.format('<div class="{0}">{1}</div>', cls, text);
861 // s now contains the string: '<div class="my-class">Some text</div>'
862 </code></pre>
863      * @param {String} string The tokenized string to be formatted
864      * @param {String} value1 The value to replace token {0}
865      * @param {String} value2 Etc...
866      * @return {String} The formatted string
867      * @static
868      */
869     format : function(format){
870         var args = Array.prototype.slice.call(arguments, 1);
871         return format.replace(/\{(\d+)\}/g, function(m, i){
872             return Roo.util.Format.htmlEncode(args[i]);
873         });
874     }
875 });
876
877 /**
878  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
879  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
880  * they are already different, the first value passed in is returned.  Note that this method returns the new value
881  * but does not change the current string.
882  * <pre><code>
883 // alternate sort directions
884 sort = sort.toggle('ASC', 'DESC');
885
886 // instead of conditional logic:
887 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
888 </code></pre>
889  * @param {String} value The value to compare to the current string
890  * @param {String} other The new value to use if the string already equals the first value passed in
891  * @return {String} The new value
892  */
893  
894 String.prototype.toggle = function(value, other){
895     return this == value ? other : value;
896 };/*
897  * Based on:
898  * Ext JS Library 1.1.1
899  * Copyright(c) 2006-2007, Ext JS, LLC.
900  *
901  * Originally Released Under LGPL - original licence link has changed is not relivant.
902  *
903  * Fork - LGPL
904  * <script type="text/javascript">
905  */
906
907  /**
908  * @class Number
909  */
910 Roo.applyIf(Number.prototype, {
911     /**
912      * Checks whether or not the current number is within a desired range.  If the number is already within the
913      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
914      * exceeded.  Note that this method returns the constrained value but does not change the current number.
915      * @param {Number} min The minimum number in the range
916      * @param {Number} max The maximum number in the range
917      * @return {Number} The constrained value if outside the range, otherwise the current value
918      */
919     constrain : function(min, max){
920         return Math.min(Math.max(this, min), max);
921     }
922 });/*
923  * Based on:
924  * Ext JS Library 1.1.1
925  * Copyright(c) 2006-2007, Ext JS, LLC.
926  *
927  * Originally Released Under LGPL - original licence link has changed is not relivant.
928  *
929  * Fork - LGPL
930  * <script type="text/javascript">
931  */
932  /**
933  * @class Array
934  */
935 Roo.applyIf(Array.prototype, {
936     /**
937      * 
938      * Checks whether or not the specified object exists in the array.
939      * @param {Object} o The object to check for
940      * @return {Number} The index of o in the array (or -1 if it is not found)
941      */
942     indexOf : function(o){
943        for (var i = 0, len = this.length; i < len; i++){
944               if(this[i] == o) return i;
945        }
946            return -1;
947     },
948
949     /**
950      * Removes the specified object from the array.  If the object is not found nothing happens.
951      * @param {Object} o The object to remove
952      */
953     remove : function(o){
954        var index = this.indexOf(o);
955        if(index != -1){
956            this.splice(index, 1);
957        }
958     },
959     /**
960      * Map (JS 1.6 compatibility)
961      * @param {Function} function  to call
962      */
963     map : function(fun )
964     {
965         var len = this.length >>> 0;
966         if (typeof fun != "function")
967             throw new TypeError();
968
969         var res = new Array(len);
970         var thisp = arguments[1];
971         for (var i = 0; i < len; i++)
972         {
973             if (i in this)
974                 res[i] = fun.call(thisp, this[i], i, this);
975         }
976
977         return res;
978     }
979     
980 });
981
982
983  /*
984  * Based on:
985  * Ext JS Library 1.1.1
986  * Copyright(c) 2006-2007, Ext JS, LLC.
987  *
988  * Originally Released Under LGPL - original licence link has changed is not relivant.
989  *
990  * Fork - LGPL
991  * <script type="text/javascript">
992  */
993
994 /**
995  * @class Date
996  *
997  * The date parsing and format syntax is a subset of
998  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
999  * supported will provide results equivalent to their PHP versions.
1000  *
1001  * Following is the list of all currently supported formats:
1002  *<pre>
1003 Sample date:
1004 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1005
1006 Format  Output      Description
1007 ------  ----------  --------------------------------------------------------------
1008   d      10         Day of the month, 2 digits with leading zeros
1009   D      Wed        A textual representation of a day, three letters
1010   j      10         Day of the month without leading zeros
1011   l      Wednesday  A full textual representation of the day of the week
1012   S      th         English ordinal day of month suffix, 2 chars (use with j)
1013   w      3          Numeric representation of the day of the week
1014   z      9          The julian date, or day of the year (0-365)
1015   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1016   F      January    A full textual representation of the month
1017   m      01         Numeric representation of a month, with leading zeros
1018   M      Jan        Month name abbreviation, three letters
1019   n      1          Numeric representation of a month, without leading zeros
1020   t      31         Number of days in the given month
1021   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1022   Y      2007       A full numeric representation of a year, 4 digits
1023   y      07         A two digit representation of a year
1024   a      pm         Lowercase Ante meridiem and Post meridiem
1025   A      PM         Uppercase Ante meridiem and Post meridiem
1026   g      3          12-hour format of an hour without leading zeros
1027   G      15         24-hour format of an hour without leading zeros
1028   h      03         12-hour format of an hour with leading zeros
1029   H      15         24-hour format of an hour with leading zeros
1030   i      05         Minutes with leading zeros
1031   s      01         Seconds, with leading zeros
1032   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1033   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1034   T      CST        Timezone setting of the machine running the code
1035   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1036 </pre>
1037  *
1038  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1039  * <pre><code>
1040 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1041 document.write(dt.format('Y-m-d'));                         //2007-01-10
1042 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1043 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1044  </code></pre>
1045  *
1046  * Here are some standard date/time patterns that you might find helpful.  They
1047  * are not part of the source of Date.js, but to use them you can simply copy this
1048  * block of code into any script that is included after Date.js and they will also become
1049  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1050  * <pre><code>
1051 Date.patterns = {
1052     ISO8601Long:"Y-m-d H:i:s",
1053     ISO8601Short:"Y-m-d",
1054     ShortDate: "n/j/Y",
1055     LongDate: "l, F d, Y",
1056     FullDateTime: "l, F d, Y g:i:s A",
1057     MonthDay: "F d",
1058     ShortTime: "g:i A",
1059     LongTime: "g:i:s A",
1060     SortableDateTime: "Y-m-d\\TH:i:s",
1061     UniversalSortableDateTime: "Y-m-d H:i:sO",
1062     YearMonth: "F, Y"
1063 };
1064 </code></pre>
1065  *
1066  * Example usage:
1067  * <pre><code>
1068 var dt = new Date();
1069 document.write(dt.format(Date.patterns.ShortDate));
1070  </code></pre>
1071  */
1072
1073 /*
1074  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1075  * They generate precompiled functions from date formats instead of parsing and
1076  * processing the pattern every time you format a date.  These functions are available
1077  * on every Date object (any javascript function).
1078  *
1079  * The original article and download are here:
1080  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1081  *
1082  */
1083  
1084  
1085  // was in core
1086 /**
1087  Returns the number of milliseconds between this date and date
1088  @param {Date} date (optional) Defaults to now
1089  @return {Number} The diff in milliseconds
1090  @member Date getElapsed
1091  */
1092 Date.prototype.getElapsed = function(date) {
1093         return Math.abs((date || new Date()).getTime()-this.getTime());
1094 };
1095 // was in date file..
1096
1097
1098 // private
1099 Date.parseFunctions = {count:0};
1100 // private
1101 Date.parseRegexes = [];
1102 // private
1103 Date.formatFunctions = {count:0};
1104
1105 // private
1106 Date.prototype.dateFormat = function(format) {
1107     if (Date.formatFunctions[format] == null) {
1108         Date.createNewFormat(format);
1109     }
1110     var func = Date.formatFunctions[format];
1111     return this[func]();
1112 };
1113
1114
1115 /**
1116  * Formats a date given the supplied format string
1117  * @param {String} format The format string
1118  * @return {String} The formatted date
1119  * @method
1120  */
1121 Date.prototype.format = Date.prototype.dateFormat;
1122
1123 // private
1124 Date.createNewFormat = function(format) {
1125     var funcName = "format" + Date.formatFunctions.count++;
1126     Date.formatFunctions[format] = funcName;
1127     var code = "Date.prototype." + funcName + " = function(){return ";
1128     var special = false;
1129     var ch = '';
1130     for (var i = 0; i < format.length; ++i) {
1131         ch = format.charAt(i);
1132         if (!special && ch == "\\") {
1133             special = true;
1134         }
1135         else if (special) {
1136             special = false;
1137             code += "'" + String.escape(ch) + "' + ";
1138         }
1139         else {
1140             code += Date.getFormatCode(ch);
1141         }
1142     }
1143     /** eval:var:zzzzzzzzzzzzz */
1144     eval(code.substring(0, code.length - 3) + ";}");
1145 };
1146
1147 // private
1148 Date.getFormatCode = function(character) {
1149     switch (character) {
1150     case "d":
1151         return "String.leftPad(this.getDate(), 2, '0') + ";
1152     case "D":
1153         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1154     case "j":
1155         return "this.getDate() + ";
1156     case "l":
1157         return "Date.dayNames[this.getDay()] + ";
1158     case "S":
1159         return "this.getSuffix() + ";
1160     case "w":
1161         return "this.getDay() + ";
1162     case "z":
1163         return "this.getDayOfYear() + ";
1164     case "W":
1165         return "this.getWeekOfYear() + ";
1166     case "F":
1167         return "Date.monthNames[this.getMonth()] + ";
1168     case "m":
1169         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1170     case "M":
1171         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1172     case "n":
1173         return "(this.getMonth() + 1) + ";
1174     case "t":
1175         return "this.getDaysInMonth() + ";
1176     case "L":
1177         return "(this.isLeapYear() ? 1 : 0) + ";
1178     case "Y":
1179         return "this.getFullYear() + ";
1180     case "y":
1181         return "('' + this.getFullYear()).substring(2, 4) + ";
1182     case "a":
1183         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1184     case "A":
1185         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1186     case "g":
1187         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1188     case "G":
1189         return "this.getHours() + ";
1190     case "h":
1191         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1192     case "H":
1193         return "String.leftPad(this.getHours(), 2, '0') + ";
1194     case "i":
1195         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1196     case "s":
1197         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1198     case "O":
1199         return "this.getGMTOffset() + ";
1200     case "P":
1201         return "this.getGMTColonOffset() + ";
1202     case "T":
1203         return "this.getTimezone() + ";
1204     case "Z":
1205         return "(this.getTimezoneOffset() * -60) + ";
1206     default:
1207         return "'" + String.escape(character) + "' + ";
1208     }
1209 };
1210
1211 /**
1212  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1213  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1214  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1215  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1216  * string or the parse operation will fail.
1217  * Example Usage:
1218 <pre><code>
1219 //dt = Fri May 25 2007 (current date)
1220 var dt = new Date();
1221
1222 //dt = Thu May 25 2006 (today's month/day in 2006)
1223 dt = Date.parseDate("2006", "Y");
1224
1225 //dt = Sun Jan 15 2006 (all date parts specified)
1226 dt = Date.parseDate("2006-1-15", "Y-m-d");
1227
1228 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1229 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1230 </code></pre>
1231  * @param {String} input The unparsed date as a string
1232  * @param {String} format The format the date is in
1233  * @return {Date} The parsed date
1234  * @static
1235  */
1236 Date.parseDate = function(input, format) {
1237     if (Date.parseFunctions[format] == null) {
1238         Date.createParser(format);
1239     }
1240     var func = Date.parseFunctions[format];
1241     return Date[func](input);
1242 };
1243 /**
1244  * @private
1245  */
1246
1247 Date.createParser = function(format) {
1248     var funcName = "parse" + Date.parseFunctions.count++;
1249     var regexNum = Date.parseRegexes.length;
1250     var currentGroup = 1;
1251     Date.parseFunctions[format] = funcName;
1252
1253     var code = "Date." + funcName + " = function(input){\n"
1254         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1255         + "var d = new Date();\n"
1256         + "y = d.getFullYear();\n"
1257         + "m = d.getMonth();\n"
1258         + "d = d.getDate();\n"
1259         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1260         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1261         + "if (results && results.length > 0) {";
1262     var regex = "";
1263
1264     var special = false;
1265     var ch = '';
1266     for (var i = 0; i < format.length; ++i) {
1267         ch = format.charAt(i);
1268         if (!special && ch == "\\") {
1269             special = true;
1270         }
1271         else if (special) {
1272             special = false;
1273             regex += String.escape(ch);
1274         }
1275         else {
1276             var obj = Date.formatCodeToRegex(ch, currentGroup);
1277             currentGroup += obj.g;
1278             regex += obj.s;
1279             if (obj.g && obj.c) {
1280                 code += obj.c;
1281             }
1282         }
1283     }
1284
1285     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1286         + "{v = new Date(y, m, d, h, i, s);}\n"
1287         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1288         + "{v = new Date(y, m, d, h, i);}\n"
1289         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1290         + "{v = new Date(y, m, d, h);}\n"
1291         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1292         + "{v = new Date(y, m, d);}\n"
1293         + "else if (y >= 0 && m >= 0)\n"
1294         + "{v = new Date(y, m);}\n"
1295         + "else if (y >= 0)\n"
1296         + "{v = new Date(y);}\n"
1297         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1298         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1299         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1300         + ";}";
1301
1302     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1303     /** eval:var:zzzzzzzzzzzzz */
1304     eval(code);
1305 };
1306
1307 // private
1308 Date.formatCodeToRegex = function(character, currentGroup) {
1309     switch (character) {
1310     case "D":
1311         return {g:0,
1312         c:null,
1313         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1314     case "j":
1315         return {g:1,
1316             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1317             s:"(\\d{1,2})"}; // day of month without leading zeroes
1318     case "d":
1319         return {g:1,
1320             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1321             s:"(\\d{2})"}; // day of month with leading zeroes
1322     case "l":
1323         return {g:0,
1324             c:null,
1325             s:"(?:" + Date.dayNames.join("|") + ")"};
1326     case "S":
1327         return {g:0,
1328             c:null,
1329             s:"(?:st|nd|rd|th)"};
1330     case "w":
1331         return {g:0,
1332             c:null,
1333             s:"\\d"};
1334     case "z":
1335         return {g:0,
1336             c:null,
1337             s:"(?:\\d{1,3})"};
1338     case "W":
1339         return {g:0,
1340             c:null,
1341             s:"(?:\\d{2})"};
1342     case "F":
1343         return {g:1,
1344             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1345             s:"(" + Date.monthNames.join("|") + ")"};
1346     case "M":
1347         return {g:1,
1348             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1349             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1350     case "n":
1351         return {g:1,
1352             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1353             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1354     case "m":
1355         return {g:1,
1356             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1357             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1358     case "t":
1359         return {g:0,
1360             c:null,
1361             s:"\\d{1,2}"};
1362     case "L":
1363         return {g:0,
1364             c:null,
1365             s:"(?:1|0)"};
1366     case "Y":
1367         return {g:1,
1368             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1369             s:"(\\d{4})"};
1370     case "y":
1371         return {g:1,
1372             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1373                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1374             s:"(\\d{1,2})"};
1375     case "a":
1376         return {g:1,
1377             c:"if (results[" + currentGroup + "] == 'am') {\n"
1378                 + "if (h == 12) { h = 0; }\n"
1379                 + "} else { if (h < 12) { h += 12; }}",
1380             s:"(am|pm)"};
1381     case "A":
1382         return {g:1,
1383             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1384                 + "if (h == 12) { h = 0; }\n"
1385                 + "} else { if (h < 12) { h += 12; }}",
1386             s:"(AM|PM)"};
1387     case "g":
1388     case "G":
1389         return {g:1,
1390             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1391             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1392     case "h":
1393     case "H":
1394         return {g:1,
1395             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1396             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1397     case "i":
1398         return {g:1,
1399             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1400             s:"(\\d{2})"};
1401     case "s":
1402         return {g:1,
1403             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1404             s:"(\\d{2})"};
1405     case "O":
1406         return {g:1,
1407             c:[
1408                 "o = results[", currentGroup, "];\n",
1409                 "var sn = o.substring(0,1);\n", // get + / - sign
1410                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1411                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1412                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1413                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1414             ].join(""),
1415             s:"([+\-]\\d{2,4})"};
1416     
1417     
1418     case "P":
1419         return {g:1,
1420                 c:[
1421                    "o = results[", currentGroup, "];\n",
1422                    "var sn = o.substring(0,1);\n",
1423                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1424                    "var mn = o.substring(4,6) % 60;\n",
1425                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1426                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1427             ].join(""),
1428             s:"([+\-]\\d{4})"};
1429     case "T":
1430         return {g:0,
1431             c:null,
1432             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1433     case "Z":
1434         return {g:1,
1435             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1436                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1437             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1438     default:
1439         return {g:0,
1440             c:null,
1441             s:String.escape(character)};
1442     }
1443 };
1444
1445 /**
1446  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1447  * @return {String} The abbreviated timezone name (e.g. 'CST')
1448  */
1449 Date.prototype.getTimezone = function() {
1450     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1451 };
1452
1453 /**
1454  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1455  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1456  */
1457 Date.prototype.getGMTOffset = function() {
1458     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1459         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1460         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1461 };
1462
1463 /**
1464  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1465  * @return {String} 2-characters representing hours and 2-characters representing minutes
1466  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1467  */
1468 Date.prototype.getGMTColonOffset = function() {
1469         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1470                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1471                 + ":"
1472                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1473 }
1474
1475 /**
1476  * Get the numeric day number of the year, adjusted for leap year.
1477  * @return {Number} 0 through 364 (365 in leap years)
1478  */
1479 Date.prototype.getDayOfYear = function() {
1480     var num = 0;
1481     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1482     for (var i = 0; i < this.getMonth(); ++i) {
1483         num += Date.daysInMonth[i];
1484     }
1485     return num + this.getDate() - 1;
1486 };
1487
1488 /**
1489  * Get the string representation of the numeric week number of the year
1490  * (equivalent to the format specifier 'W').
1491  * @return {String} '00' through '52'
1492  */
1493 Date.prototype.getWeekOfYear = function() {
1494     // Skip to Thursday of this week
1495     var now = this.getDayOfYear() + (4 - this.getDay());
1496     // Find the first Thursday of the year
1497     var jan1 = new Date(this.getFullYear(), 0, 1);
1498     var then = (7 - jan1.getDay() + 4);
1499     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1500 };
1501
1502 /**
1503  * Whether or not the current date is in a leap year.
1504  * @return {Boolean} True if the current date is in a leap year, else false
1505  */
1506 Date.prototype.isLeapYear = function() {
1507     var year = this.getFullYear();
1508     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1509 };
1510
1511 /**
1512  * Get the first day of the current month, adjusted for leap year.  The returned value
1513  * is the numeric day index within the week (0-6) which can be used in conjunction with
1514  * the {@link #monthNames} array to retrieve the textual day name.
1515  * Example:
1516  *<pre><code>
1517 var dt = new Date('1/10/2007');
1518 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1519 </code></pre>
1520  * @return {Number} The day number (0-6)
1521  */
1522 Date.prototype.getFirstDayOfMonth = function() {
1523     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1524     return (day < 0) ? (day + 7) : day;
1525 };
1526
1527 /**
1528  * Get the last day of the current month, adjusted for leap year.  The returned value
1529  * is the numeric day index within the week (0-6) which can be used in conjunction with
1530  * the {@link #monthNames} array to retrieve the textual day name.
1531  * Example:
1532  *<pre><code>
1533 var dt = new Date('1/10/2007');
1534 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1535 </code></pre>
1536  * @return {Number} The day number (0-6)
1537  */
1538 Date.prototype.getLastDayOfMonth = function() {
1539     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1540     return (day < 0) ? (day + 7) : day;
1541 };
1542
1543
1544 /**
1545  * Get the first date of this date's month
1546  * @return {Date}
1547  */
1548 Date.prototype.getFirstDateOfMonth = function() {
1549     return new Date(this.getFullYear(), this.getMonth(), 1);
1550 };
1551
1552 /**
1553  * Get the last date of this date's month
1554  * @return {Date}
1555  */
1556 Date.prototype.getLastDateOfMonth = function() {
1557     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1558 };
1559 /**
1560  * Get the number of days in the current month, adjusted for leap year.
1561  * @return {Number} The number of days in the month
1562  */
1563 Date.prototype.getDaysInMonth = function() {
1564     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1565     return Date.daysInMonth[this.getMonth()];
1566 };
1567
1568 /**
1569  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1570  * @return {String} 'st, 'nd', 'rd' or 'th'
1571  */
1572 Date.prototype.getSuffix = function() {
1573     switch (this.getDate()) {
1574         case 1:
1575         case 21:
1576         case 31:
1577             return "st";
1578         case 2:
1579         case 22:
1580             return "nd";
1581         case 3:
1582         case 23:
1583             return "rd";
1584         default:
1585             return "th";
1586     }
1587 };
1588
1589 // private
1590 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1591
1592 /**
1593  * An array of textual month names.
1594  * Override these values for international dates, for example...
1595  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1596  * @type Array
1597  * @static
1598  */
1599 Date.monthNames =
1600    ["January",
1601     "February",
1602     "March",
1603     "April",
1604     "May",
1605     "June",
1606     "July",
1607     "August",
1608     "September",
1609     "October",
1610     "November",
1611     "December"];
1612
1613 /**
1614  * An array of textual day names.
1615  * Override these values for international dates, for example...
1616  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1617  * @type Array
1618  * @static
1619  */
1620 Date.dayNames =
1621    ["Sunday",
1622     "Monday",
1623     "Tuesday",
1624     "Wednesday",
1625     "Thursday",
1626     "Friday",
1627     "Saturday"];
1628
1629 // private
1630 Date.y2kYear = 50;
1631 // private
1632 Date.monthNumbers = {
1633     Jan:0,
1634     Feb:1,
1635     Mar:2,
1636     Apr:3,
1637     May:4,
1638     Jun:5,
1639     Jul:6,
1640     Aug:7,
1641     Sep:8,
1642     Oct:9,
1643     Nov:10,
1644     Dec:11};
1645
1646 /**
1647  * Creates and returns a new Date instance with the exact same date value as the called instance.
1648  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1649  * variable will also be changed.  When the intention is to create a new variable that will not
1650  * modify the original instance, you should create a clone.
1651  *
1652  * Example of correctly cloning a date:
1653  * <pre><code>
1654 //wrong way:
1655 var orig = new Date('10/1/2006');
1656 var copy = orig;
1657 copy.setDate(5);
1658 document.write(orig);  //returns 'Thu Oct 05 2006'!
1659
1660 //correct way:
1661 var orig = new Date('10/1/2006');
1662 var copy = orig.clone();
1663 copy.setDate(5);
1664 document.write(orig);  //returns 'Thu Oct 01 2006'
1665 </code></pre>
1666  * @return {Date} The new Date instance
1667  */
1668 Date.prototype.clone = function() {
1669         return new Date(this.getTime());
1670 };
1671
1672 /**
1673  * Clears any time information from this date
1674  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1675  @return {Date} this or the clone
1676  */
1677 Date.prototype.clearTime = function(clone){
1678     if(clone){
1679         return this.clone().clearTime();
1680     }
1681     this.setHours(0);
1682     this.setMinutes(0);
1683     this.setSeconds(0);
1684     this.setMilliseconds(0);
1685     return this;
1686 };
1687
1688 // private
1689 // safari setMonth is broken
1690 if(Roo.isSafari){
1691     Date.brokenSetMonth = Date.prototype.setMonth;
1692         Date.prototype.setMonth = function(num){
1693                 if(num <= -1){
1694                         var n = Math.ceil(-num);
1695                         var back_year = Math.ceil(n/12);
1696                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1697                         this.setFullYear(this.getFullYear() - back_year);
1698                         return Date.brokenSetMonth.call(this, month);
1699                 } else {
1700                         return Date.brokenSetMonth.apply(this, arguments);
1701                 }
1702         };
1703 }
1704
1705 /** Date interval constant 
1706 * @static 
1707 * @type String */
1708 Date.MILLI = "ms";
1709 /** Date interval constant 
1710 * @static 
1711 * @type String */
1712 Date.SECOND = "s";
1713 /** Date interval constant 
1714 * @static 
1715 * @type String */
1716 Date.MINUTE = "mi";
1717 /** Date interval constant 
1718 * @static 
1719 * @type String */
1720 Date.HOUR = "h";
1721 /** Date interval constant 
1722 * @static 
1723 * @type String */
1724 Date.DAY = "d";
1725 /** Date interval constant 
1726 * @static 
1727 * @type String */
1728 Date.MONTH = "mo";
1729 /** Date interval constant 
1730 * @static 
1731 * @type String */
1732 Date.YEAR = "y";
1733
1734 /**
1735  * Provides a convenient method of performing basic date arithmetic.  This method
1736  * does not modify the Date instance being called - it creates and returns
1737  * a new Date instance containing the resulting date value.
1738  *
1739  * Examples:
1740  * <pre><code>
1741 //Basic usage:
1742 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1743 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1744
1745 //Negative values will subtract correctly:
1746 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1747 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1748
1749 //You can even chain several calls together in one line!
1750 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1751 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1752  </code></pre>
1753  *
1754  * @param {String} interval   A valid date interval enum value
1755  * @param {Number} value      The amount to add to the current date
1756  * @return {Date} The new Date instance
1757  */
1758 Date.prototype.add = function(interval, value){
1759   var d = this.clone();
1760   if (!interval || value === 0) return d;
1761   switch(interval.toLowerCase()){
1762     case Date.MILLI:
1763       d.setMilliseconds(this.getMilliseconds() + value);
1764       break;
1765     case Date.SECOND:
1766       d.setSeconds(this.getSeconds() + value);
1767       break;
1768     case Date.MINUTE:
1769       d.setMinutes(this.getMinutes() + value);
1770       break;
1771     case Date.HOUR:
1772       d.setHours(this.getHours() + value);
1773       break;
1774     case Date.DAY:
1775       d.setDate(this.getDate() + value);
1776       break;
1777     case Date.MONTH:
1778       var day = this.getDate();
1779       if(day > 28){
1780           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1781       }
1782       d.setDate(day);
1783       d.setMonth(this.getMonth() + value);
1784       break;
1785     case Date.YEAR:
1786       d.setFullYear(this.getFullYear() + value);
1787       break;
1788   }
1789   return d;
1790 };
1791 /*
1792  * Based on:
1793  * Ext JS Library 1.1.1
1794  * Copyright(c) 2006-2007, Ext JS, LLC.
1795  *
1796  * Originally Released Under LGPL - original licence link has changed is not relivant.
1797  *
1798  * Fork - LGPL
1799  * <script type="text/javascript">
1800  */
1801
1802 /**
1803  * @class Roo.lib.Dom
1804  * @static
1805  * 
1806  * Dom utils (from YIU afaik)
1807  * 
1808  **/
1809 Roo.lib.Dom = {
1810     /**
1811      * Get the view width
1812      * @param {Boolean} full True will get the full document, otherwise it's the view width
1813      * @return {Number} The width
1814      */
1815      
1816     getViewWidth : function(full) {
1817         return full ? this.getDocumentWidth() : this.getViewportWidth();
1818     },
1819     /**
1820      * Get the view height
1821      * @param {Boolean} full True will get the full document, otherwise it's the view height
1822      * @return {Number} The height
1823      */
1824     getViewHeight : function(full) {
1825         return full ? this.getDocumentHeight() : this.getViewportHeight();
1826     },
1827
1828     getDocumentHeight: function() {
1829         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1830         return Math.max(scrollHeight, this.getViewportHeight());
1831     },
1832
1833     getDocumentWidth: function() {
1834         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1835         return Math.max(scrollWidth, this.getViewportWidth());
1836     },
1837
1838     getViewportHeight: function() {
1839         var height = self.innerHeight;
1840         var mode = document.compatMode;
1841
1842         if ((mode || Roo.isIE) && !Roo.isOpera) {
1843             height = (mode == "CSS1Compat") ?
1844                      document.documentElement.clientHeight :
1845                      document.body.clientHeight;
1846         }
1847
1848         return height;
1849     },
1850
1851     getViewportWidth: function() {
1852         var width = self.innerWidth;
1853         var mode = document.compatMode;
1854
1855         if (mode || Roo.isIE) {
1856             width = (mode == "CSS1Compat") ?
1857                     document.documentElement.clientWidth :
1858                     document.body.clientWidth;
1859         }
1860         return width;
1861     },
1862
1863     isAncestor : function(p, c) {
1864         p = Roo.getDom(p);
1865         c = Roo.getDom(c);
1866         if (!p || !c) {
1867             return false;
1868         }
1869
1870         if (p.contains && !Roo.isSafari) {
1871             return p.contains(c);
1872         } else if (p.compareDocumentPosition) {
1873             return !!(p.compareDocumentPosition(c) & 16);
1874         } else {
1875             var parent = c.parentNode;
1876             while (parent) {
1877                 if (parent == p) {
1878                     return true;
1879                 }
1880                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1881                     return false;
1882                 }
1883                 parent = parent.parentNode;
1884             }
1885             return false;
1886         }
1887     },
1888
1889     getRegion : function(el) {
1890         return Roo.lib.Region.getRegion(el);
1891     },
1892
1893     getY : function(el) {
1894         return this.getXY(el)[1];
1895     },
1896
1897     getX : function(el) {
1898         return this.getXY(el)[0];
1899     },
1900
1901     getXY : function(el) {
1902         var p, pe, b, scroll, bd = document.body;
1903         el = Roo.getDom(el);
1904         var fly = Roo.lib.AnimBase.fly;
1905         if (el.getBoundingClientRect) {
1906             b = el.getBoundingClientRect();
1907             scroll = fly(document).getScroll();
1908             return [b.left + scroll.left, b.top + scroll.top];
1909         }
1910         var x = 0, y = 0;
1911
1912         p = el;
1913
1914         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1915
1916         while (p) {
1917
1918             x += p.offsetLeft;
1919             y += p.offsetTop;
1920
1921             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1922                 hasAbsolute = true;
1923             }
1924
1925             if (Roo.isGecko) {
1926                 pe = fly(p);
1927
1928                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1929                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1930
1931
1932                 x += bl;
1933                 y += bt;
1934
1935
1936                 if (p != el && pe.getStyle('overflow') != 'visible') {
1937                     x += bl;
1938                     y += bt;
1939                 }
1940             }
1941             p = p.offsetParent;
1942         }
1943
1944         if (Roo.isSafari && hasAbsolute) {
1945             x -= bd.offsetLeft;
1946             y -= bd.offsetTop;
1947         }
1948
1949         if (Roo.isGecko && !hasAbsolute) {
1950             var dbd = fly(bd);
1951             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1952             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1953         }
1954
1955         p = el.parentNode;
1956         while (p && p != bd) {
1957             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1958                 x -= p.scrollLeft;
1959                 y -= p.scrollTop;
1960             }
1961             p = p.parentNode;
1962         }
1963         return [x, y];
1964     },
1965  
1966   
1967
1968
1969     setXY : function(el, xy) {
1970         el = Roo.fly(el, '_setXY');
1971         el.position();
1972         var pts = el.translatePoints(xy);
1973         if (xy[0] !== false) {
1974             el.dom.style.left = pts.left + "px";
1975         }
1976         if (xy[1] !== false) {
1977             el.dom.style.top = pts.top + "px";
1978         }
1979     },
1980
1981     setX : function(el, x) {
1982         this.setXY(el, [x, false]);
1983     },
1984
1985     setY : function(el, y) {
1986         this.setXY(el, [false, y]);
1987     }
1988 };
1989 /*
1990  * Portions of this file are based on pieces of Yahoo User Interface Library
1991  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1992  * YUI licensed under the BSD License:
1993  * http://developer.yahoo.net/yui/license.txt
1994  * <script type="text/javascript">
1995  *
1996  */
1997
1998 Roo.lib.Event = function() {
1999     var loadComplete = false;
2000     var listeners = [];
2001     var unloadListeners = [];
2002     var retryCount = 0;
2003     var onAvailStack = [];
2004     var counter = 0;
2005     var lastError = null;
2006
2007     return {
2008         POLL_RETRYS: 200,
2009         POLL_INTERVAL: 20,
2010         EL: 0,
2011         TYPE: 1,
2012         FN: 2,
2013         WFN: 3,
2014         OBJ: 3,
2015         ADJ_SCOPE: 4,
2016         _interval: null,
2017
2018         startInterval: function() {
2019             if (!this._interval) {
2020                 var self = this;
2021                 var callback = function() {
2022                     self._tryPreloadAttach();
2023                 };
2024                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2025
2026             }
2027         },
2028
2029         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2030             onAvailStack.push({ id:         p_id,
2031                 fn:         p_fn,
2032                 obj:        p_obj,
2033                 override:   p_override,
2034                 checkReady: false    });
2035
2036             retryCount = this.POLL_RETRYS;
2037             this.startInterval();
2038         },
2039
2040
2041         addListener: function(el, eventName, fn) {
2042             el = Roo.getDom(el);
2043             if (!el || !fn) {
2044                 return false;
2045             }
2046
2047             if ("unload" == eventName) {
2048                 unloadListeners[unloadListeners.length] =
2049                 [el, eventName, fn];
2050                 return true;
2051             }
2052
2053             var wrappedFn = function(e) {
2054                 return fn(Roo.lib.Event.getEvent(e));
2055             };
2056
2057             var li = [el, eventName, fn, wrappedFn];
2058
2059             var index = listeners.length;
2060             listeners[index] = li;
2061
2062             this.doAdd(el, eventName, wrappedFn, false);
2063             return true;
2064
2065         },
2066
2067
2068         removeListener: function(el, eventName, fn) {
2069             var i, len;
2070
2071             el = Roo.getDom(el);
2072
2073             if(!fn) {
2074                 return this.purgeElement(el, false, eventName);
2075             }
2076
2077
2078             if ("unload" == eventName) {
2079
2080                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2081                     var li = unloadListeners[i];
2082                     if (li &&
2083                         li[0] == el &&
2084                         li[1] == eventName &&
2085                         li[2] == fn) {
2086                         unloadListeners.splice(i, 1);
2087                         return true;
2088                     }
2089                 }
2090
2091                 return false;
2092             }
2093
2094             var cacheItem = null;
2095
2096
2097             var index = arguments[3];
2098
2099             if ("undefined" == typeof index) {
2100                 index = this._getCacheIndex(el, eventName, fn);
2101             }
2102
2103             if (index >= 0) {
2104                 cacheItem = listeners[index];
2105             }
2106
2107             if (!el || !cacheItem) {
2108                 return false;
2109             }
2110
2111             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2112
2113             delete listeners[index][this.WFN];
2114             delete listeners[index][this.FN];
2115             listeners.splice(index, 1);
2116
2117             return true;
2118
2119         },
2120
2121
2122         getTarget: function(ev, resolveTextNode) {
2123             ev = ev.browserEvent || ev;
2124             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2125             var t = ev.target || ev.srcElement;
2126             return this.resolveTextNode(t);
2127         },
2128
2129
2130         resolveTextNode: function(node) {
2131             if (Roo.isSafari && node && 3 == node.nodeType) {
2132                 return node.parentNode;
2133             } else {
2134                 return node;
2135             }
2136         },
2137
2138
2139         getPageX: function(ev) {
2140             ev = ev.browserEvent || ev;
2141             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2142             var x = ev.pageX;
2143             if (!x && 0 !== x) {
2144                 x = ev.clientX || 0;
2145
2146                 if (Roo.isIE) {
2147                     x += this.getScroll()[1];
2148                 }
2149             }
2150
2151             return x;
2152         },
2153
2154
2155         getPageY: function(ev) {
2156             ev = ev.browserEvent || ev;
2157             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2158             var y = ev.pageY;
2159             if (!y && 0 !== y) {
2160                 y = ev.clientY || 0;
2161
2162                 if (Roo.isIE) {
2163                     y += this.getScroll()[0];
2164                 }
2165             }
2166
2167
2168             return y;
2169         },
2170
2171
2172         getXY: function(ev) {
2173             ev = ev.browserEvent || ev;
2174             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2175             return [this.getPageX(ev), this.getPageY(ev)];
2176         },
2177
2178
2179         getRelatedTarget: function(ev) {
2180             ev = ev.browserEvent || ev;
2181             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2182             var t = ev.relatedTarget;
2183             if (!t) {
2184                 if (ev.type == "mouseout") {
2185                     t = ev.toElement;
2186                 } else if (ev.type == "mouseover") {
2187                     t = ev.fromElement;
2188                 }
2189             }
2190
2191             return this.resolveTextNode(t);
2192         },
2193
2194
2195         getTime: function(ev) {
2196             ev = ev.browserEvent || ev;
2197             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2198             if (!ev.time) {
2199                 var t = new Date().getTime();
2200                 try {
2201                     ev.time = t;
2202                 } catch(ex) {
2203                     this.lastError = ex;
2204                     return t;
2205                 }
2206             }
2207
2208             return ev.time;
2209         },
2210
2211
2212         stopEvent: function(ev) {
2213             this.stopPropagation(ev);
2214             this.preventDefault(ev);
2215         },
2216
2217
2218         stopPropagation: function(ev) {
2219             ev = ev.browserEvent || ev;
2220             if (ev.stopPropagation) {
2221                 ev.stopPropagation();
2222             } else {
2223                 ev.cancelBubble = true;
2224             }
2225         },
2226
2227
2228         preventDefault: function(ev) {
2229             ev = ev.browserEvent || ev;
2230             if(ev.preventDefault) {
2231                 ev.preventDefault();
2232             } else {
2233                 ev.returnValue = false;
2234             }
2235         },
2236
2237
2238         getEvent: function(e) {
2239             var ev = e || window.event;
2240             if (!ev) {
2241                 var c = this.getEvent.caller;
2242                 while (c) {
2243                     ev = c.arguments[0];
2244                     if (ev && Event == ev.constructor) {
2245                         break;
2246                     }
2247                     c = c.caller;
2248                 }
2249             }
2250             return ev;
2251         },
2252
2253
2254         getCharCode: function(ev) {
2255             ev = ev.browserEvent || ev;
2256             return ev.charCode || ev.keyCode || 0;
2257         },
2258
2259
2260         _getCacheIndex: function(el, eventName, fn) {
2261             for (var i = 0,len = listeners.length; i < len; ++i) {
2262                 var li = listeners[i];
2263                 if (li &&
2264                     li[this.FN] == fn &&
2265                     li[this.EL] == el &&
2266                     li[this.TYPE] == eventName) {
2267                     return i;
2268                 }
2269             }
2270
2271             return -1;
2272         },
2273
2274
2275         elCache: {},
2276
2277
2278         getEl: function(id) {
2279             return document.getElementById(id);
2280         },
2281
2282
2283         clearCache: function() {
2284         },
2285
2286
2287         _load: function(e) {
2288             loadComplete = true;
2289             var EU = Roo.lib.Event;
2290
2291
2292             if (Roo.isIE) {
2293                 EU.doRemove(window, "load", EU._load);
2294             }
2295         },
2296
2297
2298         _tryPreloadAttach: function() {
2299
2300             if (this.locked) {
2301                 return false;
2302             }
2303
2304             this.locked = true;
2305
2306
2307             var tryAgain = !loadComplete;
2308             if (!tryAgain) {
2309                 tryAgain = (retryCount > 0);
2310             }
2311
2312
2313             var notAvail = [];
2314             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2315                 var item = onAvailStack[i];
2316                 if (item) {
2317                     var el = this.getEl(item.id);
2318
2319                     if (el) {
2320                         if (!item.checkReady ||
2321                             loadComplete ||
2322                             el.nextSibling ||
2323                             (document && document.body)) {
2324
2325                             var scope = el;
2326                             if (item.override) {
2327                                 if (item.override === true) {
2328                                     scope = item.obj;
2329                                 } else {
2330                                     scope = item.override;
2331                                 }
2332                             }
2333                             item.fn.call(scope, item.obj);
2334                             onAvailStack[i] = null;
2335                         }
2336                     } else {
2337                         notAvail.push(item);
2338                     }
2339                 }
2340             }
2341
2342             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2343
2344             if (tryAgain) {
2345
2346                 this.startInterval();
2347             } else {
2348                 clearInterval(this._interval);
2349                 this._interval = null;
2350             }
2351
2352             this.locked = false;
2353
2354             return true;
2355
2356         },
2357
2358
2359         purgeElement: function(el, recurse, eventName) {
2360             var elListeners = this.getListeners(el, eventName);
2361             if (elListeners) {
2362                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2363                     var l = elListeners[i];
2364                     this.removeListener(el, l.type, l.fn);
2365                 }
2366             }
2367
2368             if (recurse && el && el.childNodes) {
2369                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2370                     this.purgeElement(el.childNodes[i], recurse, eventName);
2371                 }
2372             }
2373         },
2374
2375
2376         getListeners: function(el, eventName) {
2377             var results = [], searchLists;
2378             if (!eventName) {
2379                 searchLists = [listeners, unloadListeners];
2380             } else if (eventName == "unload") {
2381                 searchLists = [unloadListeners];
2382             } else {
2383                 searchLists = [listeners];
2384             }
2385
2386             for (var j = 0; j < searchLists.length; ++j) {
2387                 var searchList = searchLists[j];
2388                 if (searchList && searchList.length > 0) {
2389                     for (var i = 0,len = searchList.length; i < len; ++i) {
2390                         var l = searchList[i];
2391                         if (l && l[this.EL] === el &&
2392                             (!eventName || eventName === l[this.TYPE])) {
2393                             results.push({
2394                                 type:   l[this.TYPE],
2395                                 fn:     l[this.FN],
2396                                 obj:    l[this.OBJ],
2397                                 adjust: l[this.ADJ_SCOPE],
2398                                 index:  i
2399                             });
2400                         }
2401                     }
2402                 }
2403             }
2404
2405             return (results.length) ? results : null;
2406         },
2407
2408
2409         _unload: function(e) {
2410
2411             var EU = Roo.lib.Event, i, j, l, len, index;
2412
2413             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2414                 l = unloadListeners[i];
2415                 if (l) {
2416                     var scope = window;
2417                     if (l[EU.ADJ_SCOPE]) {
2418                         if (l[EU.ADJ_SCOPE] === true) {
2419                             scope = l[EU.OBJ];
2420                         } else {
2421                             scope = l[EU.ADJ_SCOPE];
2422                         }
2423                     }
2424                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2425                     unloadListeners[i] = null;
2426                     l = null;
2427                     scope = null;
2428                 }
2429             }
2430
2431             unloadListeners = null;
2432
2433             if (listeners && listeners.length > 0) {
2434                 j = listeners.length;
2435                 while (j) {
2436                     index = j - 1;
2437                     l = listeners[index];
2438                     if (l) {
2439                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2440                                 l[EU.FN], index);
2441                     }
2442                     j = j - 1;
2443                 }
2444                 l = null;
2445
2446                 EU.clearCache();
2447             }
2448
2449             EU.doRemove(window, "unload", EU._unload);
2450
2451         },
2452
2453
2454         getScroll: function() {
2455             var dd = document.documentElement, db = document.body;
2456             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2457                 return [dd.scrollTop, dd.scrollLeft];
2458             } else if (db) {
2459                 return [db.scrollTop, db.scrollLeft];
2460             } else {
2461                 return [0, 0];
2462             }
2463         },
2464
2465
2466         doAdd: function () {
2467             if (window.addEventListener) {
2468                 return function(el, eventName, fn, capture) {
2469                     el.addEventListener(eventName, fn, (capture));
2470                 };
2471             } else if (window.attachEvent) {
2472                 return function(el, eventName, fn, capture) {
2473                     el.attachEvent("on" + eventName, fn);
2474                 };
2475             } else {
2476                 return function() {
2477                 };
2478             }
2479         }(),
2480
2481
2482         doRemove: function() {
2483             if (window.removeEventListener) {
2484                 return function (el, eventName, fn, capture) {
2485                     el.removeEventListener(eventName, fn, (capture));
2486                 };
2487             } else if (window.detachEvent) {
2488                 return function (el, eventName, fn) {
2489                     el.detachEvent("on" + eventName, fn);
2490                 };
2491             } else {
2492                 return function() {
2493                 };
2494             }
2495         }()
2496     };
2497     
2498 }();
2499 (function() {     
2500    
2501     var E = Roo.lib.Event;
2502     E.on = E.addListener;
2503     E.un = E.removeListener;
2504
2505     if (document && document.body) {
2506         E._load();
2507     } else {
2508         E.doAdd(window, "load", E._load);
2509     }
2510     E.doAdd(window, "unload", E._unload);
2511     E._tryPreloadAttach();
2512 })();
2513
2514 /*
2515  * Portions of this file are based on pieces of Yahoo User Interface Library
2516  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2517  * YUI licensed under the BSD License:
2518  * http://developer.yahoo.net/yui/license.txt
2519  * <script type="text/javascript">
2520  *
2521  */
2522
2523 (function() {
2524     /**
2525      * @class Roo.lib.Ajax
2526      *
2527      */
2528     Roo.lib.Ajax = {
2529         /**
2530          * @static 
2531          */
2532         request : function(method, uri, cb, data, options) {
2533             if(options){
2534                 var hs = options.headers;
2535                 if(hs){
2536                     for(var h in hs){
2537                         if(hs.hasOwnProperty(h)){
2538                             this.initHeader(h, hs[h], false);
2539                         }
2540                     }
2541                 }
2542                 if(options.xmlData){
2543                     this.initHeader('Content-Type', 'text/xml', false);
2544                     method = 'POST';
2545                     data = options.xmlData;
2546                 }
2547             }
2548
2549             return this.asyncRequest(method, uri, cb, data);
2550         },
2551
2552         serializeForm : function(form) {
2553             if(typeof form == 'string') {
2554                 form = (document.getElementById(form) || document.forms[form]);
2555             }
2556
2557             var el, name, val, disabled, data = '', hasSubmit = false;
2558             for (var i = 0; i < form.elements.length; i++) {
2559                 el = form.elements[i];
2560                 disabled = form.elements[i].disabled;
2561                 name = form.elements[i].name;
2562                 val = form.elements[i].value;
2563
2564                 if (!disabled && name){
2565                     switch (el.type)
2566                             {
2567                         case 'select-one':
2568                         case 'select-multiple':
2569                             for (var j = 0; j < el.options.length; j++) {
2570                                 if (el.options[j].selected) {
2571                                     if (Roo.isIE) {
2572                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2573                                     }
2574                                     else {
2575                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2576                                     }
2577                                 }
2578                             }
2579                             break;
2580                         case 'radio':
2581                         case 'checkbox':
2582                             if (el.checked) {
2583                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2584                             }
2585                             break;
2586                         case 'file':
2587
2588                         case undefined:
2589
2590                         case 'reset':
2591
2592                         case 'button':
2593
2594                             break;
2595                         case 'submit':
2596                             if(hasSubmit == false) {
2597                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2598                                 hasSubmit = true;
2599                             }
2600                             break;
2601                         default:
2602                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2603                             break;
2604                     }
2605                 }
2606             }
2607             data = data.substr(0, data.length - 1);
2608             return data;
2609         },
2610
2611         headers:{},
2612
2613         hasHeaders:false,
2614
2615         useDefaultHeader:true,
2616
2617         defaultPostHeader:'application/x-www-form-urlencoded',
2618
2619         useDefaultXhrHeader:true,
2620
2621         defaultXhrHeader:'XMLHttpRequest',
2622
2623         hasDefaultHeaders:true,
2624
2625         defaultHeaders:{},
2626
2627         poll:{},
2628
2629         timeout:{},
2630
2631         pollInterval:50,
2632
2633         transactionId:0,
2634
2635         setProgId:function(id)
2636         {
2637             this.activeX.unshift(id);
2638         },
2639
2640         setDefaultPostHeader:function(b)
2641         {
2642             this.useDefaultHeader = b;
2643         },
2644
2645         setDefaultXhrHeader:function(b)
2646         {
2647             this.useDefaultXhrHeader = b;
2648         },
2649
2650         setPollingInterval:function(i)
2651         {
2652             if (typeof i == 'number' && isFinite(i)) {
2653                 this.pollInterval = i;
2654             }
2655         },
2656
2657         createXhrObject:function(transactionId)
2658         {
2659             var obj,http;
2660             try
2661             {
2662
2663                 http = new XMLHttpRequest();
2664
2665                 obj = { conn:http, tId:transactionId };
2666             }
2667             catch(e)
2668             {
2669                 for (var i = 0; i < this.activeX.length; ++i) {
2670                     try
2671                     {
2672
2673                         http = new ActiveXObject(this.activeX[i]);
2674
2675                         obj = { conn:http, tId:transactionId };
2676                         break;
2677                     }
2678                     catch(e) {
2679                     }
2680                 }
2681             }
2682             finally
2683             {
2684                 return obj;
2685             }
2686         },
2687
2688         getConnectionObject:function()
2689         {
2690             var o;
2691             var tId = this.transactionId;
2692
2693             try
2694             {
2695                 o = this.createXhrObject(tId);
2696                 if (o) {
2697                     this.transactionId++;
2698                 }
2699             }
2700             catch(e) {
2701             }
2702             finally
2703             {
2704                 return o;
2705             }
2706         },
2707
2708         asyncRequest:function(method, uri, callback, postData)
2709         {
2710             var o = this.getConnectionObject();
2711
2712             if (!o) {
2713                 return null;
2714             }
2715             else {
2716                 o.conn.open(method, uri, true);
2717
2718                 if (this.useDefaultXhrHeader) {
2719                     if (!this.defaultHeaders['X-Requested-With']) {
2720                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2721                     }
2722                 }
2723
2724                 if(postData && this.useDefaultHeader){
2725                     this.initHeader('Content-Type', this.defaultPostHeader);
2726                 }
2727
2728                  if (this.hasDefaultHeaders || this.hasHeaders) {
2729                     this.setHeader(o);
2730                 }
2731
2732                 this.handleReadyState(o, callback);
2733                 o.conn.send(postData || null);
2734
2735                 return o;
2736             }
2737         },
2738
2739         handleReadyState:function(o, callback)
2740         {
2741             var oConn = this;
2742
2743             if (callback && callback.timeout) {
2744                 
2745                 this.timeout[o.tId] = window.setTimeout(function() {
2746                     oConn.abort(o, callback, true);
2747                 }, callback.timeout);
2748             }
2749
2750             this.poll[o.tId] = window.setInterval(
2751                     function() {
2752                         if (o.conn && o.conn.readyState == 4) {
2753                             window.clearInterval(oConn.poll[o.tId]);
2754                             delete oConn.poll[o.tId];
2755
2756                             if(callback && callback.timeout) {
2757                                 window.clearTimeout(oConn.timeout[o.tId]);
2758                                 delete oConn.timeout[o.tId];
2759                             }
2760
2761                             oConn.handleTransactionResponse(o, callback);
2762                         }
2763                     }
2764                     , this.pollInterval);
2765         },
2766
2767         handleTransactionResponse:function(o, callback, isAbort)
2768         {
2769
2770             if (!callback) {
2771                 this.releaseObject(o);
2772                 return;
2773             }
2774
2775             var httpStatus, responseObject;
2776
2777             try
2778             {
2779                 if (o.conn.status !== undefined && o.conn.status != 0) {
2780                     httpStatus = o.conn.status;
2781                 }
2782                 else {
2783                     httpStatus = 13030;
2784                 }
2785             }
2786             catch(e) {
2787
2788
2789                 httpStatus = 13030;
2790             }
2791
2792             if (httpStatus >= 200 && httpStatus < 300) {
2793                 responseObject = this.createResponseObject(o, callback.argument);
2794                 if (callback.success) {
2795                     if (!callback.scope) {
2796                         callback.success(responseObject);
2797                     }
2798                     else {
2799
2800
2801                         callback.success.apply(callback.scope, [responseObject]);
2802                     }
2803                 }
2804             }
2805             else {
2806                 switch (httpStatus) {
2807
2808                     case 12002:
2809                     case 12029:
2810                     case 12030:
2811                     case 12031:
2812                     case 12152:
2813                     case 13030:
2814                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2815                         if (callback.failure) {
2816                             if (!callback.scope) {
2817                                 callback.failure(responseObject);
2818                             }
2819                             else {
2820                                 callback.failure.apply(callback.scope, [responseObject]);
2821                             }
2822                         }
2823                         break;
2824                     default:
2825                         responseObject = this.createResponseObject(o, callback.argument);
2826                         if (callback.failure) {
2827                             if (!callback.scope) {
2828                                 callback.failure(responseObject);
2829                             }
2830                             else {
2831                                 callback.failure.apply(callback.scope, [responseObject]);
2832                             }
2833                         }
2834                 }
2835             }
2836
2837             this.releaseObject(o);
2838             responseObject = null;
2839         },
2840
2841         createResponseObject:function(o, callbackArg)
2842         {
2843             var obj = {};
2844             var headerObj = {};
2845
2846             try
2847             {
2848                 var headerStr = o.conn.getAllResponseHeaders();
2849                 var header = headerStr.split('\n');
2850                 for (var i = 0; i < header.length; i++) {
2851                     var delimitPos = header[i].indexOf(':');
2852                     if (delimitPos != -1) {
2853                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2854                     }
2855                 }
2856             }
2857             catch(e) {
2858             }
2859
2860             obj.tId = o.tId;
2861             obj.status = o.conn.status;
2862             obj.statusText = o.conn.statusText;
2863             obj.getResponseHeader = headerObj;
2864             obj.getAllResponseHeaders = headerStr;
2865             obj.responseText = o.conn.responseText;
2866             obj.responseXML = o.conn.responseXML;
2867
2868             if (typeof callbackArg !== undefined) {
2869                 obj.argument = callbackArg;
2870             }
2871
2872             return obj;
2873         },
2874
2875         createExceptionObject:function(tId, callbackArg, isAbort)
2876         {
2877             var COMM_CODE = 0;
2878             var COMM_ERROR = 'communication failure';
2879             var ABORT_CODE = -1;
2880             var ABORT_ERROR = 'transaction aborted';
2881
2882             var obj = {};
2883
2884             obj.tId = tId;
2885             if (isAbort) {
2886                 obj.status = ABORT_CODE;
2887                 obj.statusText = ABORT_ERROR;
2888             }
2889             else {
2890                 obj.status = COMM_CODE;
2891                 obj.statusText = COMM_ERROR;
2892             }
2893
2894             if (callbackArg) {
2895                 obj.argument = callbackArg;
2896             }
2897
2898             return obj;
2899         },
2900
2901         initHeader:function(label, value, isDefault)
2902         {
2903             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2904
2905             if (headerObj[label] === undefined) {
2906                 headerObj[label] = value;
2907             }
2908             else {
2909
2910
2911                 headerObj[label] = value + "," + headerObj[label];
2912             }
2913
2914             if (isDefault) {
2915                 this.hasDefaultHeaders = true;
2916             }
2917             else {
2918                 this.hasHeaders = true;
2919             }
2920         },
2921
2922
2923         setHeader:function(o)
2924         {
2925             if (this.hasDefaultHeaders) {
2926                 for (var prop in this.defaultHeaders) {
2927                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2928                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2929                     }
2930                 }
2931             }
2932
2933             if (this.hasHeaders) {
2934                 for (var prop in this.headers) {
2935                     if (this.headers.hasOwnProperty(prop)) {
2936                         o.conn.setRequestHeader(prop, this.headers[prop]);
2937                     }
2938                 }
2939                 this.headers = {};
2940                 this.hasHeaders = false;
2941             }
2942         },
2943
2944         resetDefaultHeaders:function() {
2945             delete this.defaultHeaders;
2946             this.defaultHeaders = {};
2947             this.hasDefaultHeaders = false;
2948         },
2949
2950         abort:function(o, callback, isTimeout)
2951         {
2952             if(this.isCallInProgress(o)) {
2953                 o.conn.abort();
2954                 window.clearInterval(this.poll[o.tId]);
2955                 delete this.poll[o.tId];
2956                 if (isTimeout) {
2957                     delete this.timeout[o.tId];
2958                 }
2959
2960                 this.handleTransactionResponse(o, callback, true);
2961
2962                 return true;
2963             }
2964             else {
2965                 return false;
2966             }
2967         },
2968
2969
2970         isCallInProgress:function(o)
2971         {
2972             if (o && o.conn) {
2973                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2974             }
2975             else {
2976
2977                 return false;
2978             }
2979         },
2980
2981
2982         releaseObject:function(o)
2983         {
2984
2985             o.conn = null;
2986
2987             o = null;
2988         },
2989
2990         activeX:[
2991         'MSXML2.XMLHTTP.3.0',
2992         'MSXML2.XMLHTTP',
2993         'Microsoft.XMLHTTP'
2994         ]
2995
2996
2997     };
2998 })();/*
2999  * Portions of this file are based on pieces of Yahoo User Interface Library
3000  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3001  * YUI licensed under the BSD License:
3002  * http://developer.yahoo.net/yui/license.txt
3003  * <script type="text/javascript">
3004  *
3005  */
3006
3007 Roo.lib.Region = function(t, r, b, l) {
3008     this.top = t;
3009     this[1] = t;
3010     this.right = r;
3011     this.bottom = b;
3012     this.left = l;
3013     this[0] = l;
3014 };
3015
3016
3017 Roo.lib.Region.prototype = {
3018     contains : function(region) {
3019         return ( region.left >= this.left &&
3020                  region.right <= this.right &&
3021                  region.top >= this.top &&
3022                  region.bottom <= this.bottom    );
3023
3024     },
3025
3026     getArea : function() {
3027         return ( (this.bottom - this.top) * (this.right - this.left) );
3028     },
3029
3030     intersect : function(region) {
3031         var t = Math.max(this.top, region.top);
3032         var r = Math.min(this.right, region.right);
3033         var b = Math.min(this.bottom, region.bottom);
3034         var l = Math.max(this.left, region.left);
3035
3036         if (b >= t && r >= l) {
3037             return new Roo.lib.Region(t, r, b, l);
3038         } else {
3039             return null;
3040         }
3041     },
3042     union : function(region) {
3043         var t = Math.min(this.top, region.top);
3044         var r = Math.max(this.right, region.right);
3045         var b = Math.max(this.bottom, region.bottom);
3046         var l = Math.min(this.left, region.left);
3047
3048         return new Roo.lib.Region(t, r, b, l);
3049     },
3050
3051     adjust : function(t, l, b, r) {
3052         this.top += t;
3053         this.left += l;
3054         this.right += r;
3055         this.bottom += b;
3056         return this;
3057     }
3058 };
3059
3060 Roo.lib.Region.getRegion = function(el) {
3061     var p = Roo.lib.Dom.getXY(el);
3062
3063     var t = p[1];
3064     var r = p[0] + el.offsetWidth;
3065     var b = p[1] + el.offsetHeight;
3066     var l = p[0];
3067
3068     return new Roo.lib.Region(t, r, b, l);
3069 };
3070 /*
3071  * Portions of this file are based on pieces of Yahoo User Interface Library
3072  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3073  * YUI licensed under the BSD License:
3074  * http://developer.yahoo.net/yui/license.txt
3075  * <script type="text/javascript">
3076  *
3077  */
3078 //@@dep Roo.lib.Region
3079
3080
3081 Roo.lib.Point = function(x, y) {
3082     if (x instanceof Array) {
3083         y = x[1];
3084         x = x[0];
3085     }
3086     this.x = this.right = this.left = this[0] = x;
3087     this.y = this.top = this.bottom = this[1] = y;
3088 };
3089
3090 Roo.lib.Point.prototype = new Roo.lib.Region();
3091 /*
3092  * Portions of this file are based on pieces of Yahoo User Interface Library
3093  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3094  * YUI licensed under the BSD License:
3095  * http://developer.yahoo.net/yui/license.txt
3096  * <script type="text/javascript">
3097  *
3098  */
3099  
3100 (function() {   
3101
3102     Roo.lib.Anim = {
3103         scroll : function(el, args, duration, easing, cb, scope) {
3104             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3105         },
3106
3107         motion : function(el, args, duration, easing, cb, scope) {
3108             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3109         },
3110
3111         color : function(el, args, duration, easing, cb, scope) {
3112             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3113         },
3114
3115         run : function(el, args, duration, easing, cb, scope, type) {
3116             type = type || Roo.lib.AnimBase;
3117             if (typeof easing == "string") {
3118                 easing = Roo.lib.Easing[easing];
3119             }
3120             var anim = new type(el, args, duration, easing);
3121             anim.animateX(function() {
3122                 Roo.callback(cb, scope);
3123             });
3124             return anim;
3125         }
3126     };
3127 })();/*
3128  * Portions of this file are based on pieces of Yahoo User Interface Library
3129  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3130  * YUI licensed under the BSD License:
3131  * http://developer.yahoo.net/yui/license.txt
3132  * <script type="text/javascript">
3133  *
3134  */
3135
3136 (function() {    
3137     var libFlyweight;
3138     
3139     function fly(el) {
3140         if (!libFlyweight) {
3141             libFlyweight = new Roo.Element.Flyweight();
3142         }
3143         libFlyweight.dom = el;
3144         return libFlyweight;
3145     }
3146
3147     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3148     
3149    
3150     
3151     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3152         if (el) {
3153             this.init(el, attributes, duration, method);
3154         }
3155     };
3156
3157     Roo.lib.AnimBase.fly = fly;
3158     
3159     
3160     
3161     Roo.lib.AnimBase.prototype = {
3162
3163         toString: function() {
3164             var el = this.getEl();
3165             var id = el.id || el.tagName;
3166             return ("Anim " + id);
3167         },
3168
3169         patterns: {
3170             noNegatives:        /width|height|opacity|padding/i,
3171             offsetAttribute:  /^((width|height)|(top|left))$/,
3172             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3173             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3174         },
3175
3176
3177         doMethod: function(attr, start, end) {
3178             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3179         },
3180
3181
3182         setAttribute: function(attr, val, unit) {
3183             if (this.patterns.noNegatives.test(attr)) {
3184                 val = (val > 0) ? val : 0;
3185             }
3186
3187             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3188         },
3189
3190
3191         getAttribute: function(attr) {
3192             var el = this.getEl();
3193             var val = fly(el).getStyle(attr);
3194
3195             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3196                 return parseFloat(val);
3197             }
3198
3199             var a = this.patterns.offsetAttribute.exec(attr) || [];
3200             var pos = !!( a[3] );
3201             var box = !!( a[2] );
3202
3203
3204             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3205                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3206             } else {
3207                 val = 0;
3208             }
3209
3210             return val;
3211         },
3212
3213
3214         getDefaultUnit: function(attr) {
3215             if (this.patterns.defaultUnit.test(attr)) {
3216                 return 'px';
3217             }
3218
3219             return '';
3220         },
3221
3222         animateX : function(callback, scope) {
3223             var f = function() {
3224                 this.onComplete.removeListener(f);
3225                 if (typeof callback == "function") {
3226                     callback.call(scope || this, this);
3227                 }
3228             };
3229             this.onComplete.addListener(f, this);
3230             this.animate();
3231         },
3232
3233
3234         setRuntimeAttribute: function(attr) {
3235             var start;
3236             var end;
3237             var attributes = this.attributes;
3238
3239             this.runtimeAttributes[attr] = {};
3240
3241             var isset = function(prop) {
3242                 return (typeof prop !== 'undefined');
3243             };
3244
3245             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3246                 return false;
3247             }
3248
3249             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3250
3251
3252             if (isset(attributes[attr]['to'])) {
3253                 end = attributes[attr]['to'];
3254             } else if (isset(attributes[attr]['by'])) {
3255                 if (start.constructor == Array) {
3256                     end = [];
3257                     for (var i = 0, len = start.length; i < len; ++i) {
3258                         end[i] = start[i] + attributes[attr]['by'][i];
3259                     }
3260                 } else {
3261                     end = start + attributes[attr]['by'];
3262                 }
3263             }
3264
3265             this.runtimeAttributes[attr].start = start;
3266             this.runtimeAttributes[attr].end = end;
3267
3268
3269             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3270         },
3271
3272
3273         init: function(el, attributes, duration, method) {
3274
3275             var isAnimated = false;
3276
3277
3278             var startTime = null;
3279
3280
3281             var actualFrames = 0;
3282
3283
3284             el = Roo.getDom(el);
3285
3286
3287             this.attributes = attributes || {};
3288
3289
3290             this.duration = duration || 1;
3291
3292
3293             this.method = method || Roo.lib.Easing.easeNone;
3294
3295
3296             this.useSeconds = true;
3297
3298
3299             this.currentFrame = 0;
3300
3301
3302             this.totalFrames = Roo.lib.AnimMgr.fps;
3303
3304
3305             this.getEl = function() {
3306                 return el;
3307             };
3308
3309
3310             this.isAnimated = function() {
3311                 return isAnimated;
3312             };
3313
3314
3315             this.getStartTime = function() {
3316                 return startTime;
3317             };
3318
3319             this.runtimeAttributes = {};
3320
3321
3322             this.animate = function() {
3323                 if (this.isAnimated()) {
3324                     return false;
3325                 }
3326
3327                 this.currentFrame = 0;
3328
3329                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3330
3331                 Roo.lib.AnimMgr.registerElement(this);
3332             };
3333
3334
3335             this.stop = function(finish) {
3336                 if (finish) {
3337                     this.currentFrame = this.totalFrames;
3338                     this._onTween.fire();
3339                 }
3340                 Roo.lib.AnimMgr.stop(this);
3341             };
3342
3343             var onStart = function() {
3344                 this.onStart.fire();
3345
3346                 this.runtimeAttributes = {};
3347                 for (var attr in this.attributes) {
3348                     this.setRuntimeAttribute(attr);
3349                 }
3350
3351                 isAnimated = true;
3352                 actualFrames = 0;
3353                 startTime = new Date();
3354             };
3355
3356
3357             var onTween = function() {
3358                 var data = {
3359                     duration: new Date() - this.getStartTime(),
3360                     currentFrame: this.currentFrame
3361                 };
3362
3363                 data.toString = function() {
3364                     return (
3365                             'duration: ' + data.duration +
3366                             ', currentFrame: ' + data.currentFrame
3367                             );
3368                 };
3369
3370                 this.onTween.fire(data);
3371
3372                 var runtimeAttributes = this.runtimeAttributes;
3373
3374                 for (var attr in runtimeAttributes) {
3375                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3376                 }
3377
3378                 actualFrames += 1;
3379             };
3380
3381             var onComplete = function() {
3382                 var actual_duration = (new Date() - startTime) / 1000 ;
3383
3384                 var data = {
3385                     duration: actual_duration,
3386                     frames: actualFrames,
3387                     fps: actualFrames / actual_duration
3388                 };
3389
3390                 data.toString = function() {
3391                     return (
3392                             'duration: ' + data.duration +
3393                             ', frames: ' + data.frames +
3394                             ', fps: ' + data.fps
3395                             );
3396                 };
3397
3398                 isAnimated = false;
3399                 actualFrames = 0;
3400                 this.onComplete.fire(data);
3401             };
3402
3403
3404             this._onStart = new Roo.util.Event(this);
3405             this.onStart = new Roo.util.Event(this);
3406             this.onTween = new Roo.util.Event(this);
3407             this._onTween = new Roo.util.Event(this);
3408             this.onComplete = new Roo.util.Event(this);
3409             this._onComplete = new Roo.util.Event(this);
3410             this._onStart.addListener(onStart);
3411             this._onTween.addListener(onTween);
3412             this._onComplete.addListener(onComplete);
3413         }
3414     };
3415 })();
3416 /*
3417  * Portions of this file are based on pieces of Yahoo User Interface Library
3418  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3419  * YUI licensed under the BSD License:
3420  * http://developer.yahoo.net/yui/license.txt
3421  * <script type="text/javascript">
3422  *
3423  */
3424
3425 Roo.lib.AnimMgr = new function() {
3426
3427     var thread = null;
3428
3429
3430     var queue = [];
3431
3432
3433     var tweenCount = 0;
3434
3435
3436     this.fps = 1000;
3437
3438
3439     this.delay = 1;
3440
3441
3442     this.registerElement = function(tween) {
3443         queue[queue.length] = tween;
3444         tweenCount += 1;
3445         tween._onStart.fire();
3446         this.start();
3447     };
3448
3449
3450     this.unRegister = function(tween, index) {
3451         tween._onComplete.fire();
3452         index = index || getIndex(tween);
3453         if (index != -1) {
3454             queue.splice(index, 1);
3455         }
3456
3457         tweenCount -= 1;
3458         if (tweenCount <= 0) {
3459             this.stop();
3460         }
3461     };
3462
3463
3464     this.start = function() {
3465         if (thread === null) {
3466             thread = setInterval(this.run, this.delay);
3467         }
3468     };
3469
3470
3471     this.stop = function(tween) {
3472         if (!tween) {
3473             clearInterval(thread);
3474
3475             for (var i = 0, len = queue.length; i < len; ++i) {
3476                 if (queue[0].isAnimated()) {
3477                     this.unRegister(queue[0], 0);
3478                 }
3479             }
3480
3481             queue = [];
3482             thread = null;
3483             tweenCount = 0;
3484         }
3485         else {
3486             this.unRegister(tween);
3487         }
3488     };
3489
3490
3491     this.run = function() {
3492         for (var i = 0, len = queue.length; i < len; ++i) {
3493             var tween = queue[i];
3494             if (!tween || !tween.isAnimated()) {
3495                 continue;
3496             }
3497
3498             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3499             {
3500                 tween.currentFrame += 1;
3501
3502                 if (tween.useSeconds) {
3503                     correctFrame(tween);
3504                 }
3505                 tween._onTween.fire();
3506             }
3507             else {
3508                 Roo.lib.AnimMgr.stop(tween, i);
3509             }
3510         }
3511     };
3512
3513     var getIndex = function(anim) {
3514         for (var i = 0, len = queue.length; i < len; ++i) {
3515             if (queue[i] == anim) {
3516                 return i;
3517             }
3518         }
3519         return -1;
3520     };
3521
3522
3523     var correctFrame = function(tween) {
3524         var frames = tween.totalFrames;
3525         var frame = tween.currentFrame;
3526         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3527         var elapsed = (new Date() - tween.getStartTime());
3528         var tweak = 0;
3529
3530         if (elapsed < tween.duration * 1000) {
3531             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3532         } else {
3533             tweak = frames - (frame + 1);
3534         }
3535         if (tweak > 0 && isFinite(tweak)) {
3536             if (tween.currentFrame + tweak >= frames) {
3537                 tweak = frames - (frame + 1);
3538             }
3539
3540             tween.currentFrame += tweak;
3541         }
3542     };
3543 };
3544
3545     /*
3546  * Portions of this file are based on pieces of Yahoo User Interface Library
3547  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3548  * YUI licensed under the BSD License:
3549  * http://developer.yahoo.net/yui/license.txt
3550  * <script type="text/javascript">
3551  *
3552  */
3553 Roo.lib.Bezier = new function() {
3554
3555         this.getPosition = function(points, t) {
3556             var n = points.length;
3557             var tmp = [];
3558
3559             for (var i = 0; i < n; ++i) {
3560                 tmp[i] = [points[i][0], points[i][1]];
3561             }
3562
3563             for (var j = 1; j < n; ++j) {
3564                 for (i = 0; i < n - j; ++i) {
3565                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3566                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3567                 }
3568             }
3569
3570             return [ tmp[0][0], tmp[0][1] ];
3571
3572         };
3573     };/*
3574  * Portions of this file are based on pieces of Yahoo User Interface Library
3575  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3576  * YUI licensed under the BSD License:
3577  * http://developer.yahoo.net/yui/license.txt
3578  * <script type="text/javascript">
3579  *
3580  */
3581 (function() {
3582
3583     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3584         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3585     };
3586
3587     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3588
3589     var fly = Roo.lib.AnimBase.fly;
3590     var Y = Roo.lib;
3591     var superclass = Y.ColorAnim.superclass;
3592     var proto = Y.ColorAnim.prototype;
3593
3594     proto.toString = function() {
3595         var el = this.getEl();
3596         var id = el.id || el.tagName;
3597         return ("ColorAnim " + id);
3598     };
3599
3600     proto.patterns.color = /color$/i;
3601     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3602     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3603     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3604     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3605
3606
3607     proto.parseColor = function(s) {
3608         if (s.length == 3) {
3609             return s;
3610         }
3611
3612         var c = this.patterns.hex.exec(s);
3613         if (c && c.length == 4) {
3614             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3615         }
3616
3617         c = this.patterns.rgb.exec(s);
3618         if (c && c.length == 4) {
3619             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3620         }
3621
3622         c = this.patterns.hex3.exec(s);
3623         if (c && c.length == 4) {
3624             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3625         }
3626
3627         return null;
3628     };
3629     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3630     proto.getAttribute = function(attr) {
3631         var el = this.getEl();
3632         if (this.patterns.color.test(attr)) {
3633             var val = fly(el).getStyle(attr);
3634
3635             if (this.patterns.transparent.test(val)) {
3636                 var parent = el.parentNode;
3637                 val = fly(parent).getStyle(attr);
3638
3639                 while (parent && this.patterns.transparent.test(val)) {
3640                     parent = parent.parentNode;
3641                     val = fly(parent).getStyle(attr);
3642                     if (parent.tagName.toUpperCase() == 'HTML') {
3643                         val = '#fff';
3644                     }
3645                 }
3646             }
3647         } else {
3648             val = superclass.getAttribute.call(this, attr);
3649         }
3650
3651         return val;
3652     };
3653     proto.getAttribute = function(attr) {
3654         var el = this.getEl();
3655         if (this.patterns.color.test(attr)) {
3656             var val = fly(el).getStyle(attr);
3657
3658             if (this.patterns.transparent.test(val)) {
3659                 var parent = el.parentNode;
3660                 val = fly(parent).getStyle(attr);
3661
3662                 while (parent && this.patterns.transparent.test(val)) {
3663                     parent = parent.parentNode;
3664                     val = fly(parent).getStyle(attr);
3665                     if (parent.tagName.toUpperCase() == 'HTML') {
3666                         val = '#fff';
3667                     }
3668                 }
3669             }
3670         } else {
3671             val = superclass.getAttribute.call(this, attr);
3672         }
3673
3674         return val;
3675     };
3676
3677     proto.doMethod = function(attr, start, end) {
3678         var val;
3679
3680         if (this.patterns.color.test(attr)) {
3681             val = [];
3682             for (var i = 0, len = start.length; i < len; ++i) {
3683                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3684             }
3685
3686             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3687         }
3688         else {
3689             val = superclass.doMethod.call(this, attr, start, end);
3690         }
3691
3692         return val;
3693     };
3694
3695     proto.setRuntimeAttribute = function(attr) {
3696         superclass.setRuntimeAttribute.call(this, attr);
3697
3698         if (this.patterns.color.test(attr)) {
3699             var attributes = this.attributes;
3700             var start = this.parseColor(this.runtimeAttributes[attr].start);
3701             var end = this.parseColor(this.runtimeAttributes[attr].end);
3702
3703             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3704                 end = this.parseColor(attributes[attr].by);
3705
3706                 for (var i = 0, len = start.length; i < len; ++i) {
3707                     end[i] = start[i] + end[i];
3708                 }
3709             }
3710
3711             this.runtimeAttributes[attr].start = start;
3712             this.runtimeAttributes[attr].end = end;
3713         }
3714     };
3715 })();
3716
3717 /*
3718  * Portions of this file are based on pieces of Yahoo User Interface Library
3719  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3720  * YUI licensed under the BSD License:
3721  * http://developer.yahoo.net/yui/license.txt
3722  * <script type="text/javascript">
3723  *
3724  */
3725 Roo.lib.Easing = {
3726
3727
3728     easeNone: function (t, b, c, d) {
3729         return c * t / d + b;
3730     },
3731
3732
3733     easeIn: function (t, b, c, d) {
3734         return c * (t /= d) * t + b;
3735     },
3736
3737
3738     easeOut: function (t, b, c, d) {
3739         return -c * (t /= d) * (t - 2) + b;
3740     },
3741
3742
3743     easeBoth: function (t, b, c, d) {
3744         if ((t /= d / 2) < 1) {
3745             return c / 2 * t * t + b;
3746         }
3747
3748         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3749     },
3750
3751
3752     easeInStrong: function (t, b, c, d) {
3753         return c * (t /= d) * t * t * t + b;
3754     },
3755
3756
3757     easeOutStrong: function (t, b, c, d) {
3758         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3759     },
3760
3761
3762     easeBothStrong: function (t, b, c, d) {
3763         if ((t /= d / 2) < 1) {
3764             return c / 2 * t * t * t * t + b;
3765         }
3766
3767         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3768     },
3769
3770
3771
3772     elasticIn: function (t, b, c, d, a, p) {
3773         if (t == 0) {
3774             return b;
3775         }
3776         if ((t /= d) == 1) {
3777             return b + c;
3778         }
3779         if (!p) {
3780             p = d * .3;
3781         }
3782
3783         if (!a || a < Math.abs(c)) {
3784             a = c;
3785             var s = p / 4;
3786         }
3787         else {
3788             var s = p / (2 * Math.PI) * Math.asin(c / a);
3789         }
3790
3791         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3792     },
3793
3794
3795     elasticOut: function (t, b, c, d, a, p) {
3796         if (t == 0) {
3797             return b;
3798         }
3799         if ((t /= d) == 1) {
3800             return b + c;
3801         }
3802         if (!p) {
3803             p = d * .3;
3804         }
3805
3806         if (!a || a < Math.abs(c)) {
3807             a = c;
3808             var s = p / 4;
3809         }
3810         else {
3811             var s = p / (2 * Math.PI) * Math.asin(c / a);
3812         }
3813
3814         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3815     },
3816
3817
3818     elasticBoth: function (t, b, c, d, a, p) {
3819         if (t == 0) {
3820             return b;
3821         }
3822
3823         if ((t /= d / 2) == 2) {
3824             return b + c;
3825         }
3826
3827         if (!p) {
3828             p = d * (.3 * 1.5);
3829         }
3830
3831         if (!a || a < Math.abs(c)) {
3832             a = c;
3833             var s = p / 4;
3834         }
3835         else {
3836             var s = p / (2 * Math.PI) * Math.asin(c / a);
3837         }
3838
3839         if (t < 1) {
3840             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3841                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3842         }
3843         return a * Math.pow(2, -10 * (t -= 1)) *
3844                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3845     },
3846
3847
3848
3849     backIn: function (t, b, c, d, s) {
3850         if (typeof s == 'undefined') {
3851             s = 1.70158;
3852         }
3853         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3854     },
3855
3856
3857     backOut: function (t, b, c, d, s) {
3858         if (typeof s == 'undefined') {
3859             s = 1.70158;
3860         }
3861         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3862     },
3863
3864
3865     backBoth: function (t, b, c, d, s) {
3866         if (typeof s == 'undefined') {
3867             s = 1.70158;
3868         }
3869
3870         if ((t /= d / 2 ) < 1) {
3871             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3872         }
3873         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3874     },
3875
3876
3877     bounceIn: function (t, b, c, d) {
3878         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3879     },
3880
3881
3882     bounceOut: function (t, b, c, d) {
3883         if ((t /= d) < (1 / 2.75)) {
3884             return c * (7.5625 * t * t) + b;
3885         } else if (t < (2 / 2.75)) {
3886             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3887         } else if (t < (2.5 / 2.75)) {
3888             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3889         }
3890         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3891     },
3892
3893
3894     bounceBoth: function (t, b, c, d) {
3895         if (t < d / 2) {
3896             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3897         }
3898         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3899     }
3900 };/*
3901  * Portions of this file are based on pieces of Yahoo User Interface Library
3902  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3903  * YUI licensed under the BSD License:
3904  * http://developer.yahoo.net/yui/license.txt
3905  * <script type="text/javascript">
3906  *
3907  */
3908     (function() {
3909         Roo.lib.Motion = function(el, attributes, duration, method) {
3910             if (el) {
3911                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3912             }
3913         };
3914
3915         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3916
3917
3918         var Y = Roo.lib;
3919         var superclass = Y.Motion.superclass;
3920         var proto = Y.Motion.prototype;
3921
3922         proto.toString = function() {
3923             var el = this.getEl();
3924             var id = el.id || el.tagName;
3925             return ("Motion " + id);
3926         };
3927
3928         proto.patterns.points = /^points$/i;
3929
3930         proto.setAttribute = function(attr, val, unit) {
3931             if (this.patterns.points.test(attr)) {
3932                 unit = unit || 'px';
3933                 superclass.setAttribute.call(this, 'left', val[0], unit);
3934                 superclass.setAttribute.call(this, 'top', val[1], unit);
3935             } else {
3936                 superclass.setAttribute.call(this, attr, val, unit);
3937             }
3938         };
3939
3940         proto.getAttribute = function(attr) {
3941             if (this.patterns.points.test(attr)) {
3942                 var val = [
3943                         superclass.getAttribute.call(this, 'left'),
3944                         superclass.getAttribute.call(this, 'top')
3945                         ];
3946             } else {
3947                 val = superclass.getAttribute.call(this, attr);
3948             }
3949
3950             return val;
3951         };
3952
3953         proto.doMethod = function(attr, start, end) {
3954             var val = null;
3955
3956             if (this.patterns.points.test(attr)) {
3957                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3958                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3959             } else {
3960                 val = superclass.doMethod.call(this, attr, start, end);
3961             }
3962             return val;
3963         };
3964
3965         proto.setRuntimeAttribute = function(attr) {
3966             if (this.patterns.points.test(attr)) {
3967                 var el = this.getEl();
3968                 var attributes = this.attributes;
3969                 var start;
3970                 var control = attributes['points']['control'] || [];
3971                 var end;
3972                 var i, len;
3973
3974                 if (control.length > 0 && !(control[0] instanceof Array)) {
3975                     control = [control];
3976                 } else {
3977                     var tmp = [];
3978                     for (i = 0,len = control.length; i < len; ++i) {
3979                         tmp[i] = control[i];
3980                     }
3981                     control = tmp;
3982                 }
3983
3984                 Roo.fly(el).position();
3985
3986                 if (isset(attributes['points']['from'])) {
3987                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3988                 }
3989                 else {
3990                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3991                 }
3992
3993                 start = this.getAttribute('points');
3994
3995
3996                 if (isset(attributes['points']['to'])) {
3997                     end = translateValues.call(this, attributes['points']['to'], start);
3998
3999                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4000                     for (i = 0,len = control.length; i < len; ++i) {
4001                         control[i] = translateValues.call(this, control[i], start);
4002                     }
4003
4004
4005                 } else if (isset(attributes['points']['by'])) {
4006                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4007
4008                     for (i = 0,len = control.length; i < len; ++i) {
4009                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4010                     }
4011                 }
4012
4013                 this.runtimeAttributes[attr] = [start];
4014
4015                 if (control.length > 0) {
4016                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4017                 }
4018
4019                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4020             }
4021             else {
4022                 superclass.setRuntimeAttribute.call(this, attr);
4023             }
4024         };
4025
4026         var translateValues = function(val, start) {
4027             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4028             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4029
4030             return val;
4031         };
4032
4033         var isset = function(prop) {
4034             return (typeof prop !== 'undefined');
4035         };
4036     })();
4037 /*
4038  * Portions of this file are based on pieces of Yahoo User Interface Library
4039  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4040  * YUI licensed under the BSD License:
4041  * http://developer.yahoo.net/yui/license.txt
4042  * <script type="text/javascript">
4043  *
4044  */
4045     (function() {
4046         Roo.lib.Scroll = function(el, attributes, duration, method) {
4047             if (el) {
4048                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4049             }
4050         };
4051
4052         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4053
4054
4055         var Y = Roo.lib;
4056         var superclass = Y.Scroll.superclass;
4057         var proto = Y.Scroll.prototype;
4058
4059         proto.toString = function() {
4060             var el = this.getEl();
4061             var id = el.id || el.tagName;
4062             return ("Scroll " + id);
4063         };
4064
4065         proto.doMethod = function(attr, start, end) {
4066             var val = null;
4067
4068             if (attr == 'scroll') {
4069                 val = [
4070                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4071                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4072                         ];
4073
4074             } else {
4075                 val = superclass.doMethod.call(this, attr, start, end);
4076             }
4077             return val;
4078         };
4079
4080         proto.getAttribute = function(attr) {
4081             var val = null;
4082             var el = this.getEl();
4083
4084             if (attr == 'scroll') {
4085                 val = [ el.scrollLeft, el.scrollTop ];
4086             } else {
4087                 val = superclass.getAttribute.call(this, attr);
4088             }
4089
4090             return val;
4091         };
4092
4093         proto.setAttribute = function(attr, val, unit) {
4094             var el = this.getEl();
4095
4096             if (attr == 'scroll') {
4097                 el.scrollLeft = val[0];
4098                 el.scrollTop = val[1];
4099             } else {
4100                 superclass.setAttribute.call(this, attr, val, unit);
4101             }
4102         };
4103     })();
4104 /*
4105  * Based on:
4106  * Ext JS Library 1.1.1
4107  * Copyright(c) 2006-2007, Ext JS, LLC.
4108  *
4109  * Originally Released Under LGPL - original licence link has changed is not relivant.
4110  *
4111  * Fork - LGPL
4112  * <script type="text/javascript">
4113  */
4114
4115
4116 // nasty IE9 hack - what a pile of crap that is..
4117
4118  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4119     Range.prototype.createContextualFragment = function (html) {
4120         var doc = window.document;
4121         var container = doc.createElement("div");
4122         container.innerHTML = html;
4123         var frag = doc.createDocumentFragment(), n;
4124         while ((n = container.firstChild)) {
4125             frag.appendChild(n);
4126         }
4127         return frag;
4128     };
4129 }
4130
4131 /**
4132  * @class Roo.DomHelper
4133  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4134  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4135  * @singleton
4136  */
4137 Roo.DomHelper = function(){
4138     var tempTableEl = null;
4139     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4140     var tableRe = /^table|tbody|tr|td$/i;
4141     var xmlns = {};
4142     // build as innerHTML where available
4143     /** @ignore */
4144     var createHtml = function(o){
4145         if(typeof o == 'string'){
4146             return o;
4147         }
4148         var b = "";
4149         if(!o.tag){
4150             o.tag = "div";
4151         }
4152         b += "<" + o.tag;
4153         for(var attr in o){
4154             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4155             if(attr == "style"){
4156                 var s = o["style"];
4157                 if(typeof s == "function"){
4158                     s = s.call();
4159                 }
4160                 if(typeof s == "string"){
4161                     b += ' style="' + s + '"';
4162                 }else if(typeof s == "object"){
4163                     b += ' style="';
4164                     for(var key in s){
4165                         if(typeof s[key] != "function"){
4166                             b += key + ":" + s[key] + ";";
4167                         }
4168                     }
4169                     b += '"';
4170                 }
4171             }else{
4172                 if(attr == "cls"){
4173                     b += ' class="' + o["cls"] + '"';
4174                 }else if(attr == "htmlFor"){
4175                     b += ' for="' + o["htmlFor"] + '"';
4176                 }else{
4177                     b += " " + attr + '="' + o[attr] + '"';
4178                 }
4179             }
4180         }
4181         if(emptyTags.test(o.tag)){
4182             b += "/>";
4183         }else{
4184             b += ">";
4185             var cn = o.children || o.cn;
4186             if(cn){
4187                 //http://bugs.kde.org/show_bug.cgi?id=71506
4188                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4189                     for(var i = 0, len = cn.length; i < len; i++) {
4190                         b += createHtml(cn[i], b);
4191                     }
4192                 }else{
4193                     b += createHtml(cn, b);
4194                 }
4195             }
4196             if(o.html){
4197                 b += o.html;
4198             }
4199             b += "</" + o.tag + ">";
4200         }
4201         return b;
4202     };
4203
4204     // build as dom
4205     /** @ignore */
4206     var createDom = function(o, parentNode){
4207          
4208         // defininition craeted..
4209         var ns = false;
4210         if (o.ns && o.ns != 'html') {
4211                
4212             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4213                 xmlns[o.ns] = o.xmlns;
4214                 ns = o.xmlns;
4215             }
4216             if (typeof(xmlns[o.ns]) == 'undefined') {
4217                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4218             }
4219             ns = xmlns[o.ns];
4220         }
4221         
4222         
4223         if (typeof(o) == 'string') {
4224             return parentNode.appendChild(document.createTextNode(o));
4225         }
4226         o.tag = o.tag || div;
4227         if (o.ns && Roo.isIE) {
4228             ns = false;
4229             o.tag = o.ns + ':' + o.tag;
4230             
4231         }
4232         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4233         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4234         for(var attr in o){
4235             
4236             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4237                     attr == "style" || typeof o[attr] == "function") continue;
4238                     
4239             if(attr=="cls" && Roo.isIE){
4240                 el.className = o["cls"];
4241             }else{
4242                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4243                 else el[attr] = o[attr];
4244             }
4245         }
4246         Roo.DomHelper.applyStyles(el, o.style);
4247         var cn = o.children || o.cn;
4248         if(cn){
4249             //http://bugs.kde.org/show_bug.cgi?id=71506
4250              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4251                 for(var i = 0, len = cn.length; i < len; i++) {
4252                     createDom(cn[i], el);
4253                 }
4254             }else{
4255                 createDom(cn, el);
4256             }
4257         }
4258         if(o.html){
4259             el.innerHTML = o.html;
4260         }
4261         if(parentNode){
4262            parentNode.appendChild(el);
4263         }
4264         return el;
4265     };
4266
4267     var ieTable = function(depth, s, h, e){
4268         tempTableEl.innerHTML = [s, h, e].join('');
4269         var i = -1, el = tempTableEl;
4270         while(++i < depth){
4271             el = el.firstChild;
4272         }
4273         return el;
4274     };
4275
4276     // kill repeat to save bytes
4277     var ts = '<table>',
4278         te = '</table>',
4279         tbs = ts+'<tbody>',
4280         tbe = '</tbody>'+te,
4281         trs = tbs + '<tr>',
4282         tre = '</tr>'+tbe;
4283
4284     /**
4285      * @ignore
4286      * Nasty code for IE's broken table implementation
4287      */
4288     var insertIntoTable = function(tag, where, el, html){
4289         if(!tempTableEl){
4290             tempTableEl = document.createElement('div');
4291         }
4292         var node;
4293         var before = null;
4294         if(tag == 'td'){
4295             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4296                 return;
4297             }
4298             if(where == 'beforebegin'){
4299                 before = el;
4300                 el = el.parentNode;
4301             } else{
4302                 before = el.nextSibling;
4303                 el = el.parentNode;
4304             }
4305             node = ieTable(4, trs, html, tre);
4306         }
4307         else if(tag == 'tr'){
4308             if(where == 'beforebegin'){
4309                 before = el;
4310                 el = el.parentNode;
4311                 node = ieTable(3, tbs, html, tbe);
4312             } else if(where == 'afterend'){
4313                 before = el.nextSibling;
4314                 el = el.parentNode;
4315                 node = ieTable(3, tbs, html, tbe);
4316             } else{ // INTO a TR
4317                 if(where == 'afterbegin'){
4318                     before = el.firstChild;
4319                 }
4320                 node = ieTable(4, trs, html, tre);
4321             }
4322         } else if(tag == 'tbody'){
4323             if(where == 'beforebegin'){
4324                 before = el;
4325                 el = el.parentNode;
4326                 node = ieTable(2, ts, html, te);
4327             } else if(where == 'afterend'){
4328                 before = el.nextSibling;
4329                 el = el.parentNode;
4330                 node = ieTable(2, ts, html, te);
4331             } else{
4332                 if(where == 'afterbegin'){
4333                     before = el.firstChild;
4334                 }
4335                 node = ieTable(3, tbs, html, tbe);
4336             }
4337         } else{ // TABLE
4338             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4339                 return;
4340             }
4341             if(where == 'afterbegin'){
4342                 before = el.firstChild;
4343             }
4344             node = ieTable(2, ts, html, te);
4345         }
4346         el.insertBefore(node, before);
4347         return node;
4348     };
4349
4350     return {
4351     /** True to force the use of DOM instead of html fragments @type Boolean */
4352     useDom : false,
4353
4354     /**
4355      * Returns the markup for the passed Element(s) config
4356      * @param {Object} o The Dom object spec (and children)
4357      * @return {String}
4358      */
4359     markup : function(o){
4360         return createHtml(o);
4361     },
4362
4363     /**
4364      * Applies a style specification to an element
4365      * @param {String/HTMLElement} el The element to apply styles to
4366      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4367      * a function which returns such a specification.
4368      */
4369     applyStyles : function(el, styles){
4370         if(styles){
4371            el = Roo.fly(el);
4372            if(typeof styles == "string"){
4373                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4374                var matches;
4375                while ((matches = re.exec(styles)) != null){
4376                    el.setStyle(matches[1], matches[2]);
4377                }
4378            }else if (typeof styles == "object"){
4379                for (var style in styles){
4380                   el.setStyle(style, styles[style]);
4381                }
4382            }else if (typeof styles == "function"){
4383                 Roo.DomHelper.applyStyles(el, styles.call());
4384            }
4385         }
4386     },
4387
4388     /**
4389      * Inserts an HTML fragment into the Dom
4390      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4391      * @param {HTMLElement} el The context element
4392      * @param {String} html The HTML fragmenet
4393      * @return {HTMLElement} The new node
4394      */
4395     insertHtml : function(where, el, html){
4396         where = where.toLowerCase();
4397         if(el.insertAdjacentHTML){
4398             if(tableRe.test(el.tagName)){
4399                 var rs;
4400                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4401                     return rs;
4402                 }
4403             }
4404             switch(where){
4405                 case "beforebegin":
4406                     el.insertAdjacentHTML('BeforeBegin', html);
4407                     return el.previousSibling;
4408                 case "afterbegin":
4409                     el.insertAdjacentHTML('AfterBegin', html);
4410                     return el.firstChild;
4411                 case "beforeend":
4412                     el.insertAdjacentHTML('BeforeEnd', html);
4413                     return el.lastChild;
4414                 case "afterend":
4415                     el.insertAdjacentHTML('AfterEnd', html);
4416                     return el.nextSibling;
4417             }
4418             throw 'Illegal insertion point -> "' + where + '"';
4419         }
4420         var range = el.ownerDocument.createRange();
4421         var frag;
4422         switch(where){
4423              case "beforebegin":
4424                 range.setStartBefore(el);
4425                 frag = range.createContextualFragment(html);
4426                 el.parentNode.insertBefore(frag, el);
4427                 return el.previousSibling;
4428              case "afterbegin":
4429                 if(el.firstChild){
4430                     range.setStartBefore(el.firstChild);
4431                     frag = range.createContextualFragment(html);
4432                     el.insertBefore(frag, el.firstChild);
4433                     return el.firstChild;
4434                 }else{
4435                     el.innerHTML = html;
4436                     return el.firstChild;
4437                 }
4438             case "beforeend":
4439                 if(el.lastChild){
4440                     range.setStartAfter(el.lastChild);
4441                     frag = range.createContextualFragment(html);
4442                     el.appendChild(frag);
4443                     return el.lastChild;
4444                 }else{
4445                     el.innerHTML = html;
4446                     return el.lastChild;
4447                 }
4448             case "afterend":
4449                 range.setStartAfter(el);
4450                 frag = range.createContextualFragment(html);
4451                 el.parentNode.insertBefore(frag, el.nextSibling);
4452                 return el.nextSibling;
4453             }
4454             throw 'Illegal insertion point -> "' + where + '"';
4455     },
4456
4457     /**
4458      * Creates new Dom element(s) and inserts them before el
4459      * @param {String/HTMLElement/Element} el The context element
4460      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4461      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4462      * @return {HTMLElement/Roo.Element} The new node
4463      */
4464     insertBefore : function(el, o, returnElement){
4465         return this.doInsert(el, o, returnElement, "beforeBegin");
4466     },
4467
4468     /**
4469      * Creates new Dom element(s) and inserts them after el
4470      * @param {String/HTMLElement/Element} el The context element
4471      * @param {Object} o The Dom object spec (and children)
4472      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4473      * @return {HTMLElement/Roo.Element} The new node
4474      */
4475     insertAfter : function(el, o, returnElement){
4476         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4477     },
4478
4479     /**
4480      * Creates new Dom element(s) and inserts them as the first child of el
4481      * @param {String/HTMLElement/Element} el The context element
4482      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4483      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4484      * @return {HTMLElement/Roo.Element} The new node
4485      */
4486     insertFirst : function(el, o, returnElement){
4487         return this.doInsert(el, o, returnElement, "afterBegin");
4488     },
4489
4490     // private
4491     doInsert : function(el, o, returnElement, pos, sibling){
4492         el = Roo.getDom(el);
4493         var newNode;
4494         if(this.useDom || o.ns){
4495             newNode = createDom(o, null);
4496             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4497         }else{
4498             var html = createHtml(o);
4499             newNode = this.insertHtml(pos, el, html);
4500         }
4501         return returnElement ? Roo.get(newNode, true) : newNode;
4502     },
4503
4504     /**
4505      * Creates new Dom element(s) and appends them to el
4506      * @param {String/HTMLElement/Element} el The context element
4507      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4508      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4509      * @return {HTMLElement/Roo.Element} The new node
4510      */
4511     append : function(el, o, returnElement){
4512         el = Roo.getDom(el);
4513         var newNode;
4514         if(this.useDom || o.ns){
4515             newNode = createDom(o, null);
4516             el.appendChild(newNode);
4517         }else{
4518             var html = createHtml(o);
4519             newNode = this.insertHtml("beforeEnd", el, html);
4520         }
4521         return returnElement ? Roo.get(newNode, true) : newNode;
4522     },
4523
4524     /**
4525      * Creates new Dom element(s) and overwrites the contents of el with them
4526      * @param {String/HTMLElement/Element} el The context element
4527      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4528      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4529      * @return {HTMLElement/Roo.Element} The new node
4530      */
4531     overwrite : function(el, o, returnElement){
4532         el = Roo.getDom(el);
4533         if (o.ns) {
4534           
4535             while (el.childNodes.length) {
4536                 el.removeChild(el.firstChild);
4537             }
4538             createDom(o, el);
4539         } else {
4540             el.innerHTML = createHtml(o);   
4541         }
4542         
4543         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4544     },
4545
4546     /**
4547      * Creates a new Roo.DomHelper.Template from the Dom object spec
4548      * @param {Object} o The Dom object spec (and children)
4549      * @return {Roo.DomHelper.Template} The new template
4550      */
4551     createTemplate : function(o){
4552         var html = createHtml(o);
4553         return new Roo.Template(html);
4554     }
4555     };
4556 }();
4557 /*
4558  * Based on:
4559  * Ext JS Library 1.1.1
4560  * Copyright(c) 2006-2007, Ext JS, LLC.
4561  *
4562  * Originally Released Under LGPL - original licence link has changed is not relivant.
4563  *
4564  * Fork - LGPL
4565  * <script type="text/javascript">
4566  */
4567  
4568 /**
4569 * @class Roo.Template
4570 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4571 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4572 * Usage:
4573 <pre><code>
4574 var t = new Roo.Template({
4575     html :  '&lt;div name="{id}"&gt;' + 
4576         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4577         '&lt;/div&gt;',
4578     myformat: function (value, allValues) {
4579         return 'XX' + value;
4580     }
4581 });
4582 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4583 </code></pre>
4584 * For more information see this blog post with examples:
4585 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4586      - Create Elements using DOM, HTML fragments and Templates</a>. 
4587 * @constructor
4588 * @param {Object} cfg - Configuration object.
4589 */
4590 Roo.Template = function(cfg){
4591     // BC!
4592     if(cfg instanceof Array){
4593         cfg = cfg.join("");
4594     }else if(arguments.length > 1){
4595         cfg = Array.prototype.join.call(arguments, "");
4596     }
4597     
4598     
4599     if (typeof(cfg) == 'object') {
4600         Roo.apply(this,cfg)
4601     } else {
4602         // bc
4603         this.html = cfg;
4604     }
4605     if (this.url) {
4606         this.load();
4607     }
4608     
4609 };
4610 Roo.Template.prototype = {
4611     
4612     /**
4613      * @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..
4614      *                    it should be fixed so that template is observable...
4615      */
4616     url : false,
4617     /**
4618      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4619      */
4620     html : '',
4621     /**
4622      * Returns an HTML fragment of this template with the specified values applied.
4623      * @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'})
4624      * @return {String} The HTML fragment
4625      */
4626     applyTemplate : function(values){
4627         try {
4628            
4629             if(this.compiled){
4630                 return this.compiled(values);
4631             }
4632             var useF = this.disableFormats !== true;
4633             var fm = Roo.util.Format, tpl = this;
4634             var fn = function(m, name, format, args){
4635                 if(format && useF){
4636                     if(format.substr(0, 5) == "this."){
4637                         return tpl.call(format.substr(5), values[name], values);
4638                     }else{
4639                         if(args){
4640                             // quoted values are required for strings in compiled templates, 
4641                             // but for non compiled we need to strip them
4642                             // quoted reversed for jsmin
4643                             var re = /^\s*['"](.*)["']\s*$/;
4644                             args = args.split(',');
4645                             for(var i = 0, len = args.length; i < len; i++){
4646                                 args[i] = args[i].replace(re, "$1");
4647                             }
4648                             args = [values[name]].concat(args);
4649                         }else{
4650                             args = [values[name]];
4651                         }
4652                         return fm[format].apply(fm, args);
4653                     }
4654                 }else{
4655                     return values[name] !== undefined ? values[name] : "";
4656                 }
4657             };
4658             return this.html.replace(this.re, fn);
4659         } catch (e) {
4660             Roo.log(e);
4661             throw e;
4662         }
4663          
4664     },
4665     
4666     loading : false,
4667       
4668     load : function ()
4669     {
4670          
4671         if (this.loading) {
4672             return;
4673         }
4674         var _t = this;
4675         
4676         this.loading = true;
4677         this.compiled = false;
4678         
4679         var cx = new Roo.data.Connection();
4680         cx.request({
4681             url : this.url,
4682             method : 'GET',
4683             success : function (response) {
4684                 _t.loading = false;
4685                 _t.html = response.responseText;
4686                 _t.url = false;
4687                 _t.compile();
4688              },
4689             failure : function(response) {
4690                 Roo.log("Template failed to load from " + _t.url);
4691                 _t.loading = false;
4692             }
4693         });
4694     },
4695
4696     /**
4697      * Sets the HTML used as the template and optionally compiles it.
4698      * @param {String} html
4699      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4700      * @return {Roo.Template} this
4701      */
4702     set : function(html, compile){
4703         this.html = html;
4704         this.compiled = null;
4705         if(compile){
4706             this.compile();
4707         }
4708         return this;
4709     },
4710     
4711     /**
4712      * True to disable format functions (defaults to false)
4713      * @type Boolean
4714      */
4715     disableFormats : false,
4716     
4717     /**
4718     * The regular expression used to match template variables 
4719     * @type RegExp
4720     * @property 
4721     */
4722     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4723     
4724     /**
4725      * Compiles the template into an internal function, eliminating the RegEx overhead.
4726      * @return {Roo.Template} this
4727      */
4728     compile : function(){
4729         var fm = Roo.util.Format;
4730         var useF = this.disableFormats !== true;
4731         var sep = Roo.isGecko ? "+" : ",";
4732         var fn = function(m, name, format, args){
4733             if(format && useF){
4734                 args = args ? ',' + args : "";
4735                 if(format.substr(0, 5) != "this."){
4736                     format = "fm." + format + '(';
4737                 }else{
4738                     format = 'this.call("'+ format.substr(5) + '", ';
4739                     args = ", values";
4740                 }
4741             }else{
4742                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4743             }
4744             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4745         };
4746         var body;
4747         // branched to use + in gecko and [].join() in others
4748         if(Roo.isGecko){
4749             body = "this.compiled = function(values){ return '" +
4750                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4751                     "';};";
4752         }else{
4753             body = ["this.compiled = function(values){ return ['"];
4754             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4755             body.push("'].join('');};");
4756             body = body.join('');
4757         }
4758         /**
4759          * eval:var:values
4760          * eval:var:fm
4761          */
4762         eval(body);
4763         return this;
4764     },
4765     
4766     // private function used to call members
4767     call : function(fnName, value, allValues){
4768         return this[fnName](value, allValues);
4769     },
4770     
4771     /**
4772      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4773      * @param {String/HTMLElement/Roo.Element} el The context element
4774      * @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'})
4775      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4776      * @return {HTMLElement/Roo.Element} The new node or Element
4777      */
4778     insertFirst: function(el, values, returnElement){
4779         return this.doInsert('afterBegin', el, values, returnElement);
4780     },
4781
4782     /**
4783      * Applies the supplied values to the template and inserts the new node(s) before el.
4784      * @param {String/HTMLElement/Roo.Element} el The context element
4785      * @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'})
4786      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4787      * @return {HTMLElement/Roo.Element} The new node or Element
4788      */
4789     insertBefore: function(el, values, returnElement){
4790         return this.doInsert('beforeBegin', el, values, returnElement);
4791     },
4792
4793     /**
4794      * Applies the supplied values to the template and inserts the new node(s) after el.
4795      * @param {String/HTMLElement/Roo.Element} el The context element
4796      * @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'})
4797      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4798      * @return {HTMLElement/Roo.Element} The new node or Element
4799      */
4800     insertAfter : function(el, values, returnElement){
4801         return this.doInsert('afterEnd', el, values, returnElement);
4802     },
4803     
4804     /**
4805      * Applies the supplied values to the template and appends the new node(s) to el.
4806      * @param {String/HTMLElement/Roo.Element} el The context element
4807      * @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'})
4808      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4809      * @return {HTMLElement/Roo.Element} The new node or Element
4810      */
4811     append : function(el, values, returnElement){
4812         return this.doInsert('beforeEnd', el, values, returnElement);
4813     },
4814
4815     doInsert : function(where, el, values, returnEl){
4816         el = Roo.getDom(el);
4817         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4818         return returnEl ? Roo.get(newNode, true) : newNode;
4819     },
4820
4821     /**
4822      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4823      * @param {String/HTMLElement/Roo.Element} el The context element
4824      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4825      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4826      * @return {HTMLElement/Roo.Element} The new node or Element
4827      */
4828     overwrite : function(el, values, returnElement){
4829         el = Roo.getDom(el);
4830         el.innerHTML = this.applyTemplate(values);
4831         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4832     }
4833 };
4834 /**
4835  * Alias for {@link #applyTemplate}
4836  * @method
4837  */
4838 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4839
4840 // backwards compat
4841 Roo.DomHelper.Template = Roo.Template;
4842
4843 /**
4844  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4845  * @param {String/HTMLElement} el A DOM element or its id
4846  * @returns {Roo.Template} The created template
4847  * @static
4848  */
4849 Roo.Template.from = function(el){
4850     el = Roo.getDom(el);
4851     return new Roo.Template(el.value || el.innerHTML);
4852 };/*
4853  * Based on:
4854  * Ext JS Library 1.1.1
4855  * Copyright(c) 2006-2007, Ext JS, LLC.
4856  *
4857  * Originally Released Under LGPL - original licence link has changed is not relivant.
4858  *
4859  * Fork - LGPL
4860  * <script type="text/javascript">
4861  */
4862  
4863
4864 /*
4865  * This is code is also distributed under MIT license for use
4866  * with jQuery and prototype JavaScript libraries.
4867  */
4868 /**
4869  * @class Roo.DomQuery
4870 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).
4871 <p>
4872 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>
4873
4874 <p>
4875 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.
4876 </p>
4877 <h4>Element Selectors:</h4>
4878 <ul class="list">
4879     <li> <b>*</b> any element</li>
4880     <li> <b>E</b> an element with the tag E</li>
4881     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4882     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4883     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4884     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4885 </ul>
4886 <h4>Attribute Selectors:</h4>
4887 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4888 <ul class="list">
4889     <li> <b>E[foo]</b> has an attribute "foo"</li>
4890     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4891     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4892     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4893     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4894     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4895     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4896 </ul>
4897 <h4>Pseudo Classes:</h4>
4898 <ul class="list">
4899     <li> <b>E:first-child</b> E is the first child of its parent</li>
4900     <li> <b>E:last-child</b> E is the last child of its parent</li>
4901     <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>
4902     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4903     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4904     <li> <b>E:only-child</b> E is the only child of its parent</li>
4905     <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>
4906     <li> <b>E:first</b> the first E in the resultset</li>
4907     <li> <b>E:last</b> the last E in the resultset</li>
4908     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4909     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4910     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4911     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4912     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4913     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4914     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4915     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4916     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4917 </ul>
4918 <h4>CSS Value Selectors:</h4>
4919 <ul class="list">
4920     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4921     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4922     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4923     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4924     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4925     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4926 </ul>
4927  * @singleton
4928  */
4929 Roo.DomQuery = function(){
4930     var cache = {}, simpleCache = {}, valueCache = {};
4931     var nonSpace = /\S/;
4932     var trimRe = /^\s+|\s+$/g;
4933     var tplRe = /\{(\d+)\}/g;
4934     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4935     var tagTokenRe = /^(#)?([\w-\*]+)/;
4936     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4937
4938     function child(p, index){
4939         var i = 0;
4940         var n = p.firstChild;
4941         while(n){
4942             if(n.nodeType == 1){
4943                if(++i == index){
4944                    return n;
4945                }
4946             }
4947             n = n.nextSibling;
4948         }
4949         return null;
4950     };
4951
4952     function next(n){
4953         while((n = n.nextSibling) && n.nodeType != 1);
4954         return n;
4955     };
4956
4957     function prev(n){
4958         while((n = n.previousSibling) && n.nodeType != 1);
4959         return n;
4960     };
4961
4962     function children(d){
4963         var n = d.firstChild, ni = -1;
4964             while(n){
4965                 var nx = n.nextSibling;
4966                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4967                     d.removeChild(n);
4968                 }else{
4969                     n.nodeIndex = ++ni;
4970                 }
4971                 n = nx;
4972             }
4973             return this;
4974         };
4975
4976     function byClassName(c, a, v){
4977         if(!v){
4978             return c;
4979         }
4980         var r = [], ri = -1, cn;
4981         for(var i = 0, ci; ci = c[i]; i++){
4982             if((' '+ci.className+' ').indexOf(v) != -1){
4983                 r[++ri] = ci;
4984             }
4985         }
4986         return r;
4987     };
4988
4989     function attrValue(n, attr){
4990         if(!n.tagName && typeof n.length != "undefined"){
4991             n = n[0];
4992         }
4993         if(!n){
4994             return null;
4995         }
4996         if(attr == "for"){
4997             return n.htmlFor;
4998         }
4999         if(attr == "class" || attr == "className"){
5000             return n.className;
5001         }
5002         return n.getAttribute(attr) || n[attr];
5003
5004     };
5005
5006     function getNodes(ns, mode, tagName){
5007         var result = [], ri = -1, cs;
5008         if(!ns){
5009             return result;
5010         }
5011         tagName = tagName || "*";
5012         if(typeof ns.getElementsByTagName != "undefined"){
5013             ns = [ns];
5014         }
5015         if(!mode){
5016             for(var i = 0, ni; ni = ns[i]; i++){
5017                 cs = ni.getElementsByTagName(tagName);
5018                 for(var j = 0, ci; ci = cs[j]; j++){
5019                     result[++ri] = ci;
5020                 }
5021             }
5022         }else if(mode == "/" || mode == ">"){
5023             var utag = tagName.toUpperCase();
5024             for(var i = 0, ni, cn; ni = ns[i]; i++){
5025                 cn = ni.children || ni.childNodes;
5026                 for(var j = 0, cj; cj = cn[j]; j++){
5027                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5028                         result[++ri] = cj;
5029                     }
5030                 }
5031             }
5032         }else if(mode == "+"){
5033             var utag = tagName.toUpperCase();
5034             for(var i = 0, n; n = ns[i]; i++){
5035                 while((n = n.nextSibling) && n.nodeType != 1);
5036                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5037                     result[++ri] = n;
5038                 }
5039             }
5040         }else if(mode == "~"){
5041             for(var i = 0, n; n = ns[i]; i++){
5042                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5043                 if(n){
5044                     result[++ri] = n;
5045                 }
5046             }
5047         }
5048         return result;
5049     };
5050
5051     function concat(a, b){
5052         if(b.slice){
5053             return a.concat(b);
5054         }
5055         for(var i = 0, l = b.length; i < l; i++){
5056             a[a.length] = b[i];
5057         }
5058         return a;
5059     }
5060
5061     function byTag(cs, tagName){
5062         if(cs.tagName || cs == document){
5063             cs = [cs];
5064         }
5065         if(!tagName){
5066             return cs;
5067         }
5068         var r = [], ri = -1;
5069         tagName = tagName.toLowerCase();
5070         for(var i = 0, ci; ci = cs[i]; i++){
5071             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5072                 r[++ri] = ci;
5073             }
5074         }
5075         return r;
5076     };
5077
5078     function byId(cs, attr, id){
5079         if(cs.tagName || cs == document){
5080             cs = [cs];
5081         }
5082         if(!id){
5083             return cs;
5084         }
5085         var r = [], ri = -1;
5086         for(var i = 0,ci; ci = cs[i]; i++){
5087             if(ci && ci.id == id){
5088                 r[++ri] = ci;
5089                 return r;
5090             }
5091         }
5092         return r;
5093     };
5094
5095     function byAttribute(cs, attr, value, op, custom){
5096         var r = [], ri = -1, st = custom=="{";
5097         var f = Roo.DomQuery.operators[op];
5098         for(var i = 0, ci; ci = cs[i]; i++){
5099             var a;
5100             if(st){
5101                 a = Roo.DomQuery.getStyle(ci, attr);
5102             }
5103             else if(attr == "class" || attr == "className"){
5104                 a = ci.className;
5105             }else if(attr == "for"){
5106                 a = ci.htmlFor;
5107             }else if(attr == "href"){
5108                 a = ci.getAttribute("href", 2);
5109             }else{
5110                 a = ci.getAttribute(attr);
5111             }
5112             if((f && f(a, value)) || (!f && a)){
5113                 r[++ri] = ci;
5114             }
5115         }
5116         return r;
5117     };
5118
5119     function byPseudo(cs, name, value){
5120         return Roo.DomQuery.pseudos[name](cs, value);
5121     };
5122
5123     // This is for IE MSXML which does not support expandos.
5124     // IE runs the same speed using setAttribute, however FF slows way down
5125     // and Safari completely fails so they need to continue to use expandos.
5126     var isIE = window.ActiveXObject ? true : false;
5127
5128     // this eval is stop the compressor from
5129     // renaming the variable to something shorter
5130     
5131     /** eval:var:batch */
5132     var batch = 30803; 
5133
5134     var key = 30803;
5135
5136     function nodupIEXml(cs){
5137         var d = ++key;
5138         cs[0].setAttribute("_nodup", d);
5139         var r = [cs[0]];
5140         for(var i = 1, len = cs.length; i < len; i++){
5141             var c = cs[i];
5142             if(!c.getAttribute("_nodup") != d){
5143                 c.setAttribute("_nodup", d);
5144                 r[r.length] = c;
5145             }
5146         }
5147         for(var i = 0, len = cs.length; i < len; i++){
5148             cs[i].removeAttribute("_nodup");
5149         }
5150         return r;
5151     }
5152
5153     function nodup(cs){
5154         if(!cs){
5155             return [];
5156         }
5157         var len = cs.length, c, i, r = cs, cj, ri = -1;
5158         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5159             return cs;
5160         }
5161         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5162             return nodupIEXml(cs);
5163         }
5164         var d = ++key;
5165         cs[0]._nodup = d;
5166         for(i = 1; c = cs[i]; i++){
5167             if(c._nodup != d){
5168                 c._nodup = d;
5169             }else{
5170                 r = [];
5171                 for(var j = 0; j < i; j++){
5172                     r[++ri] = cs[j];
5173                 }
5174                 for(j = i+1; cj = cs[j]; j++){
5175                     if(cj._nodup != d){
5176                         cj._nodup = d;
5177                         r[++ri] = cj;
5178                     }
5179                 }
5180                 return r;
5181             }
5182         }
5183         return r;
5184     }
5185
5186     function quickDiffIEXml(c1, c2){
5187         var d = ++key;
5188         for(var i = 0, len = c1.length; i < len; i++){
5189             c1[i].setAttribute("_qdiff", d);
5190         }
5191         var r = [];
5192         for(var i = 0, len = c2.length; i < len; i++){
5193             if(c2[i].getAttribute("_qdiff") != d){
5194                 r[r.length] = c2[i];
5195             }
5196         }
5197         for(var i = 0, len = c1.length; i < len; i++){
5198            c1[i].removeAttribute("_qdiff");
5199         }
5200         return r;
5201     }
5202
5203     function quickDiff(c1, c2){
5204         var len1 = c1.length;
5205         if(!len1){
5206             return c2;
5207         }
5208         if(isIE && c1[0].selectSingleNode){
5209             return quickDiffIEXml(c1, c2);
5210         }
5211         var d = ++key;
5212         for(var i = 0; i < len1; i++){
5213             c1[i]._qdiff = d;
5214         }
5215         var r = [];
5216         for(var i = 0, len = c2.length; i < len; i++){
5217             if(c2[i]._qdiff != d){
5218                 r[r.length] = c2[i];
5219             }
5220         }
5221         return r;
5222     }
5223
5224     function quickId(ns, mode, root, id){
5225         if(ns == root){
5226            var d = root.ownerDocument || root;
5227            return d.getElementById(id);
5228         }
5229         ns = getNodes(ns, mode, "*");
5230         return byId(ns, null, id);
5231     }
5232
5233     return {
5234         getStyle : function(el, name){
5235             return Roo.fly(el).getStyle(name);
5236         },
5237         /**
5238          * Compiles a selector/xpath query into a reusable function. The returned function
5239          * takes one parameter "root" (optional), which is the context node from where the query should start.
5240          * @param {String} selector The selector/xpath query
5241          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5242          * @return {Function}
5243          */
5244         compile : function(path, type){
5245             type = type || "select";
5246             
5247             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5248             var q = path, mode, lq;
5249             var tk = Roo.DomQuery.matchers;
5250             var tklen = tk.length;
5251             var mm;
5252
5253             // accept leading mode switch
5254             var lmode = q.match(modeRe);
5255             if(lmode && lmode[1]){
5256                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5257                 q = q.replace(lmode[1], "");
5258             }
5259             // strip leading slashes
5260             while(path.substr(0, 1)=="/"){
5261                 path = path.substr(1);
5262             }
5263
5264             while(q && lq != q){
5265                 lq = q;
5266                 var tm = q.match(tagTokenRe);
5267                 if(type == "select"){
5268                     if(tm){
5269                         if(tm[1] == "#"){
5270                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5271                         }else{
5272                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5273                         }
5274                         q = q.replace(tm[0], "");
5275                     }else if(q.substr(0, 1) != '@'){
5276                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5277                     }
5278                 }else{
5279                     if(tm){
5280                         if(tm[1] == "#"){
5281                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5282                         }else{
5283                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5284                         }
5285                         q = q.replace(tm[0], "");
5286                     }
5287                 }
5288                 while(!(mm = q.match(modeRe))){
5289                     var matched = false;
5290                     for(var j = 0; j < tklen; j++){
5291                         var t = tk[j];
5292                         var m = q.match(t.re);
5293                         if(m){
5294                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5295                                                     return m[i];
5296                                                 });
5297                             q = q.replace(m[0], "");
5298                             matched = true;
5299                             break;
5300                         }
5301                     }
5302                     // prevent infinite loop on bad selector
5303                     if(!matched){
5304                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5305                     }
5306                 }
5307                 if(mm[1]){
5308                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5309                     q = q.replace(mm[1], "");
5310                 }
5311             }
5312             fn[fn.length] = "return nodup(n);\n}";
5313             
5314              /** 
5315               * list of variables that need from compression as they are used by eval.
5316              *  eval:var:batch 
5317              *  eval:var:nodup
5318              *  eval:var:byTag
5319              *  eval:var:ById
5320              *  eval:var:getNodes
5321              *  eval:var:quickId
5322              *  eval:var:mode
5323              *  eval:var:root
5324              *  eval:var:n
5325              *  eval:var:byClassName
5326              *  eval:var:byPseudo
5327              *  eval:var:byAttribute
5328              *  eval:var:attrValue
5329              * 
5330              **/ 
5331             eval(fn.join(""));
5332             return f;
5333         },
5334
5335         /**
5336          * Selects a group of elements.
5337          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5338          * @param {Node} root (optional) The start of the query (defaults to document).
5339          * @return {Array}
5340          */
5341         select : function(path, root, type){
5342             if(!root || root == document){
5343                 root = document;
5344             }
5345             if(typeof root == "string"){
5346                 root = document.getElementById(root);
5347             }
5348             var paths = path.split(",");
5349             var results = [];
5350             for(var i = 0, len = paths.length; i < len; i++){
5351                 var p = paths[i].replace(trimRe, "");
5352                 if(!cache[p]){
5353                     cache[p] = Roo.DomQuery.compile(p);
5354                     if(!cache[p]){
5355                         throw p + " is not a valid selector";
5356                     }
5357                 }
5358                 var result = cache[p](root);
5359                 if(result && result != document){
5360                     results = results.concat(result);
5361                 }
5362             }
5363             if(paths.length > 1){
5364                 return nodup(results);
5365             }
5366             return results;
5367         },
5368
5369         /**
5370          * Selects a single element.
5371          * @param {String} selector The selector/xpath query
5372          * @param {Node} root (optional) The start of the query (defaults to document).
5373          * @return {Element}
5374          */
5375         selectNode : function(path, root){
5376             return Roo.DomQuery.select(path, root)[0];
5377         },
5378
5379         /**
5380          * Selects the value of a node, optionally replacing null with the defaultValue.
5381          * @param {String} selector The selector/xpath query
5382          * @param {Node} root (optional) The start of the query (defaults to document).
5383          * @param {String} defaultValue
5384          */
5385         selectValue : function(path, root, defaultValue){
5386             path = path.replace(trimRe, "");
5387             if(!valueCache[path]){
5388                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5389             }
5390             var n = valueCache[path](root);
5391             n = n[0] ? n[0] : n;
5392             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5393             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5394         },
5395
5396         /**
5397          * Selects the value of a node, parsing integers and floats.
5398          * @param {String} selector The selector/xpath query
5399          * @param {Node} root (optional) The start of the query (defaults to document).
5400          * @param {Number} defaultValue
5401          * @return {Number}
5402          */
5403         selectNumber : function(path, root, defaultValue){
5404             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5405             return parseFloat(v);
5406         },
5407
5408         /**
5409          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5410          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5411          * @param {String} selector The simple selector to test
5412          * @return {Boolean}
5413          */
5414         is : function(el, ss){
5415             if(typeof el == "string"){
5416                 el = document.getElementById(el);
5417             }
5418             var isArray = (el instanceof Array);
5419             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5420             return isArray ? (result.length == el.length) : (result.length > 0);
5421         },
5422
5423         /**
5424          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5425          * @param {Array} el An array of elements to filter
5426          * @param {String} selector The simple selector to test
5427          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5428          * the selector instead of the ones that match
5429          * @return {Array}
5430          */
5431         filter : function(els, ss, nonMatches){
5432             ss = ss.replace(trimRe, "");
5433             if(!simpleCache[ss]){
5434                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5435             }
5436             var result = simpleCache[ss](els);
5437             return nonMatches ? quickDiff(result, els) : result;
5438         },
5439
5440         /**
5441          * Collection of matching regular expressions and code snippets.
5442          */
5443         matchers : [{
5444                 re: /^\.([\w-]+)/,
5445                 select: 'n = byClassName(n, null, " {1} ");'
5446             }, {
5447                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5448                 select: 'n = byPseudo(n, "{1}", "{2}");'
5449             },{
5450                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5451                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5452             }, {
5453                 re: /^#([\w-]+)/,
5454                 select: 'n = byId(n, null, "{1}");'
5455             },{
5456                 re: /^@([\w-]+)/,
5457                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5458             }
5459         ],
5460
5461         /**
5462          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5463          * 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;.
5464          */
5465         operators : {
5466             "=" : function(a, v){
5467                 return a == v;
5468             },
5469             "!=" : function(a, v){
5470                 return a != v;
5471             },
5472             "^=" : function(a, v){
5473                 return a && a.substr(0, v.length) == v;
5474             },
5475             "$=" : function(a, v){
5476                 return a && a.substr(a.length-v.length) == v;
5477             },
5478             "*=" : function(a, v){
5479                 return a && a.indexOf(v) !== -1;
5480             },
5481             "%=" : function(a, v){
5482                 return (a % v) == 0;
5483             },
5484             "|=" : function(a, v){
5485                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5486             },
5487             "~=" : function(a, v){
5488                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5489             }
5490         },
5491
5492         /**
5493          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5494          * and the argument (if any) supplied in the selector.
5495          */
5496         pseudos : {
5497             "first-child" : function(c){
5498                 var r = [], ri = -1, n;
5499                 for(var i = 0, ci; ci = n = c[i]; i++){
5500                     while((n = n.previousSibling) && n.nodeType != 1);
5501                     if(!n){
5502                         r[++ri] = ci;
5503                     }
5504                 }
5505                 return r;
5506             },
5507
5508             "last-child" : function(c){
5509                 var r = [], ri = -1, n;
5510                 for(var i = 0, ci; ci = n = c[i]; i++){
5511                     while((n = n.nextSibling) && n.nodeType != 1);
5512                     if(!n){
5513                         r[++ri] = ci;
5514                     }
5515                 }
5516                 return r;
5517             },
5518
5519             "nth-child" : function(c, a) {
5520                 var r = [], ri = -1;
5521                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5522                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5523                 for(var i = 0, n; n = c[i]; i++){
5524                     var pn = n.parentNode;
5525                     if (batch != pn._batch) {
5526                         var j = 0;
5527                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5528                             if(cn.nodeType == 1){
5529                                cn.nodeIndex = ++j;
5530                             }
5531                         }
5532                         pn._batch = batch;
5533                     }
5534                     if (f == 1) {
5535                         if (l == 0 || n.nodeIndex == l){
5536                             r[++ri] = n;
5537                         }
5538                     } else if ((n.nodeIndex + l) % f == 0){
5539                         r[++ri] = n;
5540                     }
5541                 }
5542
5543                 return r;
5544             },
5545
5546             "only-child" : function(c){
5547                 var r = [], ri = -1;;
5548                 for(var i = 0, ci; ci = c[i]; i++){
5549                     if(!prev(ci) && !next(ci)){
5550                         r[++ri] = ci;
5551                     }
5552                 }
5553                 return r;
5554             },
5555
5556             "empty" : function(c){
5557                 var r = [], ri = -1;
5558                 for(var i = 0, ci; ci = c[i]; i++){
5559                     var cns = ci.childNodes, j = 0, cn, empty = true;
5560                     while(cn = cns[j]){
5561                         ++j;
5562                         if(cn.nodeType == 1 || cn.nodeType == 3){
5563                             empty = false;
5564                             break;
5565                         }
5566                     }
5567                     if(empty){
5568                         r[++ri] = ci;
5569                     }
5570                 }
5571                 return r;
5572             },
5573
5574             "contains" : function(c, v){
5575                 var r = [], ri = -1;
5576                 for(var i = 0, ci; ci = c[i]; i++){
5577                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5578                         r[++ri] = ci;
5579                     }
5580                 }
5581                 return r;
5582             },
5583
5584             "nodeValue" : function(c, v){
5585                 var r = [], ri = -1;
5586                 for(var i = 0, ci; ci = c[i]; i++){
5587                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5588                         r[++ri] = ci;
5589                     }
5590                 }
5591                 return r;
5592             },
5593
5594             "checked" : function(c){
5595                 var r = [], ri = -1;
5596                 for(var i = 0, ci; ci = c[i]; i++){
5597                     if(ci.checked == true){
5598                         r[++ri] = ci;
5599                     }
5600                 }
5601                 return r;
5602             },
5603
5604             "not" : function(c, ss){
5605                 return Roo.DomQuery.filter(c, ss, true);
5606             },
5607
5608             "odd" : function(c){
5609                 return this["nth-child"](c, "odd");
5610             },
5611
5612             "even" : function(c){
5613                 return this["nth-child"](c, "even");
5614             },
5615
5616             "nth" : function(c, a){
5617                 return c[a-1] || [];
5618             },
5619
5620             "first" : function(c){
5621                 return c[0] || [];
5622             },
5623
5624             "last" : function(c){
5625                 return c[c.length-1] || [];
5626             },
5627
5628             "has" : function(c, ss){
5629                 var s = Roo.DomQuery.select;
5630                 var r = [], ri = -1;
5631                 for(var i = 0, ci; ci = c[i]; i++){
5632                     if(s(ss, ci).length > 0){
5633                         r[++ri] = ci;
5634                     }
5635                 }
5636                 return r;
5637             },
5638
5639             "next" : function(c, ss){
5640                 var is = Roo.DomQuery.is;
5641                 var r = [], ri = -1;
5642                 for(var i = 0, ci; ci = c[i]; i++){
5643                     var n = next(ci);
5644                     if(n && is(n, ss)){
5645                         r[++ri] = ci;
5646                     }
5647                 }
5648                 return r;
5649             },
5650
5651             "prev" : function(c, ss){
5652                 var is = Roo.DomQuery.is;
5653                 var r = [], ri = -1;
5654                 for(var i = 0, ci; ci = c[i]; i++){
5655                     var n = prev(ci);
5656                     if(n && is(n, ss)){
5657                         r[++ri] = ci;
5658                     }
5659                 }
5660                 return r;
5661             }
5662         }
5663     };
5664 }();
5665
5666 /**
5667  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5668  * @param {String} path The selector/xpath query
5669  * @param {Node} root (optional) The start of the query (defaults to document).
5670  * @return {Array}
5671  * @member Roo
5672  * @method query
5673  */
5674 Roo.query = Roo.DomQuery.select;
5675 /*
5676  * Based on:
5677  * Ext JS Library 1.1.1
5678  * Copyright(c) 2006-2007, Ext JS, LLC.
5679  *
5680  * Originally Released Under LGPL - original licence link has changed is not relivant.
5681  *
5682  * Fork - LGPL
5683  * <script type="text/javascript">
5684  */
5685
5686 /**
5687  * @class Roo.util.Observable
5688  * Base class that provides a common interface for publishing events. Subclasses are expected to
5689  * to have a property "events" with all the events defined.<br>
5690  * For example:
5691  * <pre><code>
5692  Employee = function(name){
5693     this.name = name;
5694     this.addEvents({
5695         "fired" : true,
5696         "quit" : true
5697     });
5698  }
5699  Roo.extend(Employee, Roo.util.Observable);
5700 </code></pre>
5701  * @param {Object} config properties to use (incuding events / listeners)
5702  */
5703
5704 Roo.util.Observable = function(cfg){
5705     
5706     cfg = cfg|| {};
5707     this.addEvents(cfg.events || {});
5708     if (cfg.events) {
5709         delete cfg.events; // make sure
5710     }
5711      
5712     Roo.apply(this, cfg);
5713     
5714     if(this.listeners){
5715         this.on(this.listeners);
5716         delete this.listeners;
5717     }
5718 };
5719 Roo.util.Observable.prototype = {
5720     /** 
5721  * @cfg {Object} listeners  list of events and functions to call for this object, 
5722  * For example :
5723  * <pre><code>
5724     listeners :  { 
5725        'click' : function(e) {
5726            ..... 
5727         } ,
5728         .... 
5729     } 
5730   </code></pre>
5731  */
5732     
5733     
5734     /**
5735      * Fires the specified event with the passed parameters (minus the event name).
5736      * @param {String} eventName
5737      * @param {Object...} args Variable number of parameters are passed to handlers
5738      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5739      */
5740     fireEvent : function(){
5741         var ce = this.events[arguments[0].toLowerCase()];
5742         if(typeof ce == "object"){
5743             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5744         }else{
5745             return true;
5746         }
5747     },
5748
5749     // private
5750     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5751
5752     /**
5753      * Appends an event handler to this component
5754      * @param {String}   eventName The type of event to listen for
5755      * @param {Function} handler The method the event invokes
5756      * @param {Object}   scope (optional) The scope in which to execute the handler
5757      * function. The handler function's "this" context.
5758      * @param {Object}   options (optional) An object containing handler configuration
5759      * properties. This may contain any of the following properties:<ul>
5760      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5761      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5762      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5763      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5764      * by the specified number of milliseconds. If the event fires again within that time, the original
5765      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5766      * </ul><br>
5767      * <p>
5768      * <b>Combining Options</b><br>
5769      * Using the options argument, it is possible to combine different types of listeners:<br>
5770      * <br>
5771      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5772                 <pre><code>
5773                 el.on('click', this.onClick, this, {
5774                         single: true,
5775                 delay: 100,
5776                 forumId: 4
5777                 });
5778                 </code></pre>
5779      * <p>
5780      * <b>Attaching multiple handlers in 1 call</b><br>
5781      * The method also allows for a single argument to be passed which is a config object containing properties
5782      * which specify multiple handlers.
5783      * <pre><code>
5784                 el.on({
5785                         'click': {
5786                         fn: this.onClick,
5787                         scope: this,
5788                         delay: 100
5789                 }, 
5790                 'mouseover': {
5791                         fn: this.onMouseOver,
5792                         scope: this
5793                 },
5794                 'mouseout': {
5795                         fn: this.onMouseOut,
5796                         scope: this
5797                 }
5798                 });
5799                 </code></pre>
5800      * <p>
5801      * Or a shorthand syntax which passes the same scope object to all handlers:
5802         <pre><code>
5803                 el.on({
5804                         'click': this.onClick,
5805                 'mouseover': this.onMouseOver,
5806                 'mouseout': this.onMouseOut,
5807                 scope: this
5808                 });
5809                 </code></pre>
5810      */
5811     addListener : function(eventName, fn, scope, o){
5812         if(typeof eventName == "object"){
5813             o = eventName;
5814             for(var e in o){
5815                 if(this.filterOptRe.test(e)){
5816                     continue;
5817                 }
5818                 if(typeof o[e] == "function"){
5819                     // shared options
5820                     this.addListener(e, o[e], o.scope,  o);
5821                 }else{
5822                     // individual options
5823                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5824                 }
5825             }
5826             return;
5827         }
5828         o = (!o || typeof o == "boolean") ? {} : o;
5829         eventName = eventName.toLowerCase();
5830         var ce = this.events[eventName] || true;
5831         if(typeof ce == "boolean"){
5832             ce = new Roo.util.Event(this, eventName);
5833             this.events[eventName] = ce;
5834         }
5835         ce.addListener(fn, scope, o);
5836     },
5837
5838     /**
5839      * Removes a listener
5840      * @param {String}   eventName     The type of event to listen for
5841      * @param {Function} handler        The handler to remove
5842      * @param {Object}   scope  (optional) The scope (this object) for the handler
5843      */
5844     removeListener : function(eventName, fn, scope){
5845         var ce = this.events[eventName.toLowerCase()];
5846         if(typeof ce == "object"){
5847             ce.removeListener(fn, scope);
5848         }
5849     },
5850
5851     /**
5852      * Removes all listeners for this object
5853      */
5854     purgeListeners : function(){
5855         for(var evt in this.events){
5856             if(typeof this.events[evt] == "object"){
5857                  this.events[evt].clearListeners();
5858             }
5859         }
5860     },
5861
5862     relayEvents : function(o, events){
5863         var createHandler = function(ename){
5864             return function(){
5865                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5866             };
5867         };
5868         for(var i = 0, len = events.length; i < len; i++){
5869             var ename = events[i];
5870             if(!this.events[ename]){ this.events[ename] = true; };
5871             o.on(ename, createHandler(ename), this);
5872         }
5873     },
5874
5875     /**
5876      * Used to define events on this Observable
5877      * @param {Object} object The object with the events defined
5878      */
5879     addEvents : function(o){
5880         if(!this.events){
5881             this.events = {};
5882         }
5883         Roo.applyIf(this.events, o);
5884     },
5885
5886     /**
5887      * Checks to see if this object has any listeners for a specified event
5888      * @param {String} eventName The name of the event to check for
5889      * @return {Boolean} True if the event is being listened for, else false
5890      */
5891     hasListener : function(eventName){
5892         var e = this.events[eventName];
5893         return typeof e == "object" && e.listeners.length > 0;
5894     }
5895 };
5896 /**
5897  * Appends an event handler to this element (shorthand for addListener)
5898  * @param {String}   eventName     The type of event to listen for
5899  * @param {Function} handler        The method the event invokes
5900  * @param {Object}   scope (optional) The scope in which to execute the handler
5901  * function. The handler function's "this" context.
5902  * @param {Object}   options  (optional)
5903  * @method
5904  */
5905 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5906 /**
5907  * Removes a listener (shorthand for removeListener)
5908  * @param {String}   eventName     The type of event to listen for
5909  * @param {Function} handler        The handler to remove
5910  * @param {Object}   scope  (optional) The scope (this object) for the handler
5911  * @method
5912  */
5913 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5914
5915 /**
5916  * Starts capture on the specified Observable. All events will be passed
5917  * to the supplied function with the event name + standard signature of the event
5918  * <b>before</b> the event is fired. If the supplied function returns false,
5919  * the event will not fire.
5920  * @param {Observable} o The Observable to capture
5921  * @param {Function} fn The function to call
5922  * @param {Object} scope (optional) The scope (this object) for the fn
5923  * @static
5924  */
5925 Roo.util.Observable.capture = function(o, fn, scope){
5926     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5927 };
5928
5929 /**
5930  * Removes <b>all</b> added captures from the Observable.
5931  * @param {Observable} o The Observable to release
5932  * @static
5933  */
5934 Roo.util.Observable.releaseCapture = function(o){
5935     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5936 };
5937
5938 (function(){
5939
5940     var createBuffered = function(h, o, scope){
5941         var task = new Roo.util.DelayedTask();
5942         return function(){
5943             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5944         };
5945     };
5946
5947     var createSingle = function(h, e, fn, scope){
5948         return function(){
5949             e.removeListener(fn, scope);
5950             return h.apply(scope, arguments);
5951         };
5952     };
5953
5954     var createDelayed = function(h, o, scope){
5955         return function(){
5956             var args = Array.prototype.slice.call(arguments, 0);
5957             setTimeout(function(){
5958                 h.apply(scope, args);
5959             }, o.delay || 10);
5960         };
5961     };
5962
5963     Roo.util.Event = function(obj, name){
5964         this.name = name;
5965         this.obj = obj;
5966         this.listeners = [];
5967     };
5968
5969     Roo.util.Event.prototype = {
5970         addListener : function(fn, scope, options){
5971             var o = options || {};
5972             scope = scope || this.obj;
5973             if(!this.isListening(fn, scope)){
5974                 var l = {fn: fn, scope: scope, options: o};
5975                 var h = fn;
5976                 if(o.delay){
5977                     h = createDelayed(h, o, scope);
5978                 }
5979                 if(o.single){
5980                     h = createSingle(h, this, fn, scope);
5981                 }
5982                 if(o.buffer){
5983                     h = createBuffered(h, o, scope);
5984                 }
5985                 l.fireFn = h;
5986                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5987                     this.listeners.push(l);
5988                 }else{
5989                     this.listeners = this.listeners.slice(0);
5990                     this.listeners.push(l);
5991                 }
5992             }
5993         },
5994
5995         findListener : function(fn, scope){
5996             scope = scope || this.obj;
5997             var ls = this.listeners;
5998             for(var i = 0, len = ls.length; i < len; i++){
5999                 var l = ls[i];
6000                 if(l.fn == fn && l.scope == scope){
6001                     return i;
6002                 }
6003             }
6004             return -1;
6005         },
6006
6007         isListening : function(fn, scope){
6008             return this.findListener(fn, scope) != -1;
6009         },
6010
6011         removeListener : function(fn, scope){
6012             var index;
6013             if((index = this.findListener(fn, scope)) != -1){
6014                 if(!this.firing){
6015                     this.listeners.splice(index, 1);
6016                 }else{
6017                     this.listeners = this.listeners.slice(0);
6018                     this.listeners.splice(index, 1);
6019                 }
6020                 return true;
6021             }
6022             return false;
6023         },
6024
6025         clearListeners : function(){
6026             this.listeners = [];
6027         },
6028
6029         fire : function(){
6030             var ls = this.listeners, scope, len = ls.length;
6031             if(len > 0){
6032                 this.firing = true;
6033                 var args = Array.prototype.slice.call(arguments, 0);
6034                 for(var i = 0; i < len; i++){
6035                     var l = ls[i];
6036                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6037                         this.firing = false;
6038                         return false;
6039                     }
6040                 }
6041                 this.firing = false;
6042             }
6043             return true;
6044         }
6045     };
6046 })();/*
6047  * Based on:
6048  * Ext JS Library 1.1.1
6049  * Copyright(c) 2006-2007, Ext JS, LLC.
6050  *
6051  * Originally Released Under LGPL - original licence link has changed is not relivant.
6052  *
6053  * Fork - LGPL
6054  * <script type="text/javascript">
6055  */
6056
6057 /**
6058  * @class Roo.EventManager
6059  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6060  * several useful events directly.
6061  * See {@link Roo.EventObject} for more details on normalized event objects.
6062  * @singleton
6063  */
6064 Roo.EventManager = function(){
6065     var docReadyEvent, docReadyProcId, docReadyState = false;
6066     var resizeEvent, resizeTask, textEvent, textSize;
6067     var E = Roo.lib.Event;
6068     var D = Roo.lib.Dom;
6069
6070     
6071     
6072
6073     var fireDocReady = function(){
6074         if(!docReadyState){
6075             docReadyState = true;
6076             Roo.isReady = true;
6077             if(docReadyProcId){
6078                 clearInterval(docReadyProcId);
6079             }
6080             if(Roo.isGecko || Roo.isOpera) {
6081                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6082             }
6083             if(Roo.isIE){
6084                 var defer = document.getElementById("ie-deferred-loader");
6085                 if(defer){
6086                     defer.onreadystatechange = null;
6087                     defer.parentNode.removeChild(defer);
6088                 }
6089             }
6090             if(docReadyEvent){
6091                 docReadyEvent.fire();
6092                 docReadyEvent.clearListeners();
6093             }
6094         }
6095     };
6096     
6097     var initDocReady = function(){
6098         docReadyEvent = new Roo.util.Event();
6099         if(Roo.isGecko || Roo.isOpera) {
6100             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6101         }else if(Roo.isIE){
6102             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6103             var defer = document.getElementById("ie-deferred-loader");
6104             defer.onreadystatechange = function(){
6105                 if(this.readyState == "complete"){
6106                     fireDocReady();
6107                 }
6108             };
6109         }else if(Roo.isSafari){ 
6110             docReadyProcId = setInterval(function(){
6111                 var rs = document.readyState;
6112                 if(rs == "complete") {
6113                     fireDocReady();     
6114                  }
6115             }, 10);
6116         }
6117         // no matter what, make sure it fires on load
6118         E.on(window, "load", fireDocReady);
6119     };
6120
6121     var createBuffered = function(h, o){
6122         var task = new Roo.util.DelayedTask(h);
6123         return function(e){
6124             // create new event object impl so new events don't wipe out properties
6125             e = new Roo.EventObjectImpl(e);
6126             task.delay(o.buffer, h, null, [e]);
6127         };
6128     };
6129
6130     var createSingle = function(h, el, ename, fn){
6131         return function(e){
6132             Roo.EventManager.removeListener(el, ename, fn);
6133             h(e);
6134         };
6135     };
6136
6137     var createDelayed = function(h, o){
6138         return function(e){
6139             // create new event object impl so new events don't wipe out properties
6140             e = new Roo.EventObjectImpl(e);
6141             setTimeout(function(){
6142                 h(e);
6143             }, o.delay || 10);
6144         };
6145     };
6146     var transitionEndVal = false;
6147     
6148     var transitionEnd = function()
6149     {
6150         if (transitionEndVal) {
6151             return transitionEndVal;
6152         }
6153         var el = document.createElement('div');
6154
6155         var transEndEventNames = {
6156             WebkitTransition : 'webkitTransitionEnd',
6157             MozTransition    : 'transitionend',
6158             OTransition      : 'oTransitionEnd otransitionend',
6159             transition       : 'transitionend'
6160         };
6161     
6162         for (var name in transEndEventNames) {
6163             if (el.style[name] !== undefined) {
6164                 transitionEndVal = transEndEventNames[name];
6165                 return  transitionEndVal ;
6166             }
6167         }
6168     }
6169     
6170
6171     var listen = function(element, ename, opt, fn, scope){
6172         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6173         fn = fn || o.fn; scope = scope || o.scope;
6174         var el = Roo.getDom(element);
6175         
6176         
6177         if(!el){
6178             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6179         }
6180         
6181         if (ename == 'transitionend') {
6182             ename = transitionEnd();
6183         }
6184         var h = function(e){
6185             e = Roo.EventObject.setEvent(e);
6186             var t;
6187             if(o.delegate){
6188                 t = e.getTarget(o.delegate, el);
6189                 if(!t){
6190                     return;
6191                 }
6192             }else{
6193                 t = e.target;
6194             }
6195             if(o.stopEvent === true){
6196                 e.stopEvent();
6197             }
6198             if(o.preventDefault === true){
6199                e.preventDefault();
6200             }
6201             if(o.stopPropagation === true){
6202                 e.stopPropagation();
6203             }
6204
6205             if(o.normalized === false){
6206                 e = e.browserEvent;
6207             }
6208
6209             fn.call(scope || el, e, t, o);
6210         };
6211         if(o.delay){
6212             h = createDelayed(h, o);
6213         }
6214         if(o.single){
6215             h = createSingle(h, el, ename, fn);
6216         }
6217         if(o.buffer){
6218             h = createBuffered(h, o);
6219         }
6220         fn._handlers = fn._handlers || [];
6221         
6222         
6223         fn._handlers.push([Roo.id(el), ename, h]);
6224         
6225         
6226          
6227         E.on(el, ename, h);
6228         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6229             el.addEventListener("DOMMouseScroll", h, false);
6230             E.on(window, 'unload', function(){
6231                 el.removeEventListener("DOMMouseScroll", h, false);
6232             });
6233         }
6234         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6235             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6236         }
6237         return h;
6238     };
6239
6240     var stopListening = function(el, ename, fn){
6241         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6242         if(hds){
6243             for(var i = 0, len = hds.length; i < len; i++){
6244                 var h = hds[i];
6245                 if(h[0] == id && h[1] == ename){
6246                     hd = h[2];
6247                     hds.splice(i, 1);
6248                     break;
6249                 }
6250             }
6251         }
6252         E.un(el, ename, hd);
6253         el = Roo.getDom(el);
6254         if(ename == "mousewheel" && el.addEventListener){
6255             el.removeEventListener("DOMMouseScroll", hd, false);
6256         }
6257         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6258             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6259         }
6260     };
6261
6262     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6263     
6264     var pub = {
6265         
6266         
6267         /** 
6268          * Fix for doc tools
6269          * @scope Roo.EventManager
6270          */
6271         
6272         
6273         /** 
6274          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6275          * object with a Roo.EventObject
6276          * @param {Function} fn        The method the event invokes
6277          * @param {Object}   scope    An object that becomes the scope of the handler
6278          * @param {boolean}  override If true, the obj passed in becomes
6279          *                             the execution scope of the listener
6280          * @return {Function} The wrapped function
6281          * @deprecated
6282          */
6283         wrap : function(fn, scope, override){
6284             return function(e){
6285                 Roo.EventObject.setEvent(e);
6286                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6287             };
6288         },
6289         
6290         /**
6291      * Appends an event handler to an element (shorthand for addListener)
6292      * @param {String/HTMLElement}   element        The html element or id to assign the
6293      * @param {String}   eventName The type of event to listen for
6294      * @param {Function} handler The method the event invokes
6295      * @param {Object}   scope (optional) The scope in which to execute the handler
6296      * function. The handler function's "this" context.
6297      * @param {Object}   options (optional) An object containing handler configuration
6298      * properties. This may contain any of the following properties:<ul>
6299      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6300      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6301      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6302      * <li>preventDefault {Boolean} True to prevent the default action</li>
6303      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6304      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6305      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6306      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6307      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6308      * by the specified number of milliseconds. If the event fires again within that time, the original
6309      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6310      * </ul><br>
6311      * <p>
6312      * <b>Combining Options</b><br>
6313      * Using the options argument, it is possible to combine different types of listeners:<br>
6314      * <br>
6315      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6316      * Code:<pre><code>
6317 el.on('click', this.onClick, this, {
6318     single: true,
6319     delay: 100,
6320     stopEvent : true,
6321     forumId: 4
6322 });</code></pre>
6323      * <p>
6324      * <b>Attaching multiple handlers in 1 call</b><br>
6325       * The method also allows for a single argument to be passed which is a config object containing properties
6326      * which specify multiple handlers.
6327      * <p>
6328      * Code:<pre><code>
6329 el.on({
6330     'click' : {
6331         fn: this.onClick
6332         scope: this,
6333         delay: 100
6334     },
6335     'mouseover' : {
6336         fn: this.onMouseOver
6337         scope: this
6338     },
6339     'mouseout' : {
6340         fn: this.onMouseOut
6341         scope: this
6342     }
6343 });</code></pre>
6344      * <p>
6345      * Or a shorthand syntax:<br>
6346      * Code:<pre><code>
6347 el.on({
6348     'click' : this.onClick,
6349     'mouseover' : this.onMouseOver,
6350     'mouseout' : this.onMouseOut
6351     scope: this
6352 });</code></pre>
6353      */
6354         addListener : function(element, eventName, fn, scope, options){
6355             if(typeof eventName == "object"){
6356                 var o = eventName;
6357                 for(var e in o){
6358                     if(propRe.test(e)){
6359                         continue;
6360                     }
6361                     if(typeof o[e] == "function"){
6362                         // shared options
6363                         listen(element, e, o, o[e], o.scope);
6364                     }else{
6365                         // individual options
6366                         listen(element, e, o[e]);
6367                     }
6368                 }
6369                 return;
6370             }
6371             return listen(element, eventName, options, fn, scope);
6372         },
6373         
6374         /**
6375          * Removes an event handler
6376          *
6377          * @param {String/HTMLElement}   element        The id or html element to remove the 
6378          *                             event from
6379          * @param {String}   eventName     The type of event
6380          * @param {Function} fn
6381          * @return {Boolean} True if a listener was actually removed
6382          */
6383         removeListener : function(element, eventName, fn){
6384             return stopListening(element, eventName, fn);
6385         },
6386         
6387         /**
6388          * Fires when the document is ready (before onload and before images are loaded). Can be 
6389          * accessed shorthanded Roo.onReady().
6390          * @param {Function} fn        The method the event invokes
6391          * @param {Object}   scope    An  object that becomes the scope of the handler
6392          * @param {boolean}  options
6393          */
6394         onDocumentReady : function(fn, scope, options){
6395             if(docReadyState){ // if it already fired
6396                 docReadyEvent.addListener(fn, scope, options);
6397                 docReadyEvent.fire();
6398                 docReadyEvent.clearListeners();
6399                 return;
6400             }
6401             if(!docReadyEvent){
6402                 initDocReady();
6403             }
6404             docReadyEvent.addListener(fn, scope, options);
6405         },
6406         
6407         /**
6408          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6409          * @param {Function} fn        The method the event invokes
6410          * @param {Object}   scope    An object that becomes the scope of the handler
6411          * @param {boolean}  options
6412          */
6413         onWindowResize : function(fn, scope, options){
6414             if(!resizeEvent){
6415                 resizeEvent = new Roo.util.Event();
6416                 resizeTask = new Roo.util.DelayedTask(function(){
6417                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6418                 });
6419                 E.on(window, "resize", function(){
6420                     if(Roo.isIE){
6421                         resizeTask.delay(50);
6422                     }else{
6423                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6424                     }
6425                 });
6426             }
6427             resizeEvent.addListener(fn, scope, options);
6428         },
6429
6430         /**
6431          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6432          * @param {Function} fn        The method the event invokes
6433          * @param {Object}   scope    An object that becomes the scope of the handler
6434          * @param {boolean}  options
6435          */
6436         onTextResize : function(fn, scope, options){
6437             if(!textEvent){
6438                 textEvent = new Roo.util.Event();
6439                 var textEl = new Roo.Element(document.createElement('div'));
6440                 textEl.dom.className = 'x-text-resize';
6441                 textEl.dom.innerHTML = 'X';
6442                 textEl.appendTo(document.body);
6443                 textSize = textEl.dom.offsetHeight;
6444                 setInterval(function(){
6445                     if(textEl.dom.offsetHeight != textSize){
6446                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6447                     }
6448                 }, this.textResizeInterval);
6449             }
6450             textEvent.addListener(fn, scope, options);
6451         },
6452
6453         /**
6454          * Removes the passed window resize listener.
6455          * @param {Function} fn        The method the event invokes
6456          * @param {Object}   scope    The scope of handler
6457          */
6458         removeResizeListener : function(fn, scope){
6459             if(resizeEvent){
6460                 resizeEvent.removeListener(fn, scope);
6461             }
6462         },
6463
6464         // private
6465         fireResize : function(){
6466             if(resizeEvent){
6467                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6468             }   
6469         },
6470         /**
6471          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6472          */
6473         ieDeferSrc : false,
6474         /**
6475          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6476          */
6477         textResizeInterval : 50
6478     };
6479     
6480     /**
6481      * Fix for doc tools
6482      * @scopeAlias pub=Roo.EventManager
6483      */
6484     
6485      /**
6486      * Appends an event handler to an element (shorthand for addListener)
6487      * @param {String/HTMLElement}   element        The html element or id to assign the
6488      * @param {String}   eventName The type of event to listen for
6489      * @param {Function} handler The method the event invokes
6490      * @param {Object}   scope (optional) The scope in which to execute the handler
6491      * function. The handler function's "this" context.
6492      * @param {Object}   options (optional) An object containing handler configuration
6493      * properties. This may contain any of the following properties:<ul>
6494      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6495      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6496      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6497      * <li>preventDefault {Boolean} True to prevent the default action</li>
6498      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6499      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6500      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6501      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6502      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6503      * by the specified number of milliseconds. If the event fires again within that time, the original
6504      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6505      * </ul><br>
6506      * <p>
6507      * <b>Combining Options</b><br>
6508      * Using the options argument, it is possible to combine different types of listeners:<br>
6509      * <br>
6510      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6511      * Code:<pre><code>
6512 el.on('click', this.onClick, this, {
6513     single: true,
6514     delay: 100,
6515     stopEvent : true,
6516     forumId: 4
6517 });</code></pre>
6518      * <p>
6519      * <b>Attaching multiple handlers in 1 call</b><br>
6520       * The method also allows for a single argument to be passed which is a config object containing properties
6521      * which specify multiple handlers.
6522      * <p>
6523      * Code:<pre><code>
6524 el.on({
6525     'click' : {
6526         fn: this.onClick
6527         scope: this,
6528         delay: 100
6529     },
6530     'mouseover' : {
6531         fn: this.onMouseOver
6532         scope: this
6533     },
6534     'mouseout' : {
6535         fn: this.onMouseOut
6536         scope: this
6537     }
6538 });</code></pre>
6539      * <p>
6540      * Or a shorthand syntax:<br>
6541      * Code:<pre><code>
6542 el.on({
6543     'click' : this.onClick,
6544     'mouseover' : this.onMouseOver,
6545     'mouseout' : this.onMouseOut
6546     scope: this
6547 });</code></pre>
6548      */
6549     pub.on = pub.addListener;
6550     pub.un = pub.removeListener;
6551
6552     pub.stoppedMouseDownEvent = new Roo.util.Event();
6553     return pub;
6554 }();
6555 /**
6556   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6557   * @param {Function} fn        The method the event invokes
6558   * @param {Object}   scope    An  object that becomes the scope of the handler
6559   * @param {boolean}  override If true, the obj passed in becomes
6560   *                             the execution scope of the listener
6561   * @member Roo
6562   * @method onReady
6563  */
6564 Roo.onReady = Roo.EventManager.onDocumentReady;
6565
6566 Roo.onReady(function(){
6567     var bd = Roo.get(document.body);
6568     if(!bd){ return; }
6569
6570     var cls = [
6571             Roo.isIE ? "roo-ie"
6572             : Roo.isGecko ? "roo-gecko"
6573             : Roo.isOpera ? "roo-opera"
6574             : Roo.isSafari ? "roo-safari" : ""];
6575
6576     if(Roo.isMac){
6577         cls.push("roo-mac");
6578     }
6579     if(Roo.isLinux){
6580         cls.push("roo-linux");
6581     }
6582     if(Roo.isIOS){
6583         cls.push("roo-ios");
6584     }
6585     if(Roo.isBorderBox){
6586         cls.push('roo-border-box');
6587     }
6588     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6589         var p = bd.dom.parentNode;
6590         if(p){
6591             p.className += ' roo-strict';
6592         }
6593     }
6594     bd.addClass(cls.join(' '));
6595 });
6596
6597 /**
6598  * @class Roo.EventObject
6599  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6600  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6601  * Example:
6602  * <pre><code>
6603  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6604     e.preventDefault();
6605     var target = e.getTarget();
6606     ...
6607  }
6608  var myDiv = Roo.get("myDiv");
6609  myDiv.on("click", handleClick);
6610  //or
6611  Roo.EventManager.on("myDiv", 'click', handleClick);
6612  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6613  </code></pre>
6614  * @singleton
6615  */
6616 Roo.EventObject = function(){
6617     
6618     var E = Roo.lib.Event;
6619     
6620     // safari keypress events for special keys return bad keycodes
6621     var safariKeys = {
6622         63234 : 37, // left
6623         63235 : 39, // right
6624         63232 : 38, // up
6625         63233 : 40, // down
6626         63276 : 33, // page up
6627         63277 : 34, // page down
6628         63272 : 46, // delete
6629         63273 : 36, // home
6630         63275 : 35  // end
6631     };
6632
6633     // normalize button clicks
6634     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6635                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6636
6637     Roo.EventObjectImpl = function(e){
6638         if(e){
6639             this.setEvent(e.browserEvent || e);
6640         }
6641     };
6642     Roo.EventObjectImpl.prototype = {
6643         /**
6644          * Used to fix doc tools.
6645          * @scope Roo.EventObject.prototype
6646          */
6647             
6648
6649         
6650         
6651         /** The normal browser event */
6652         browserEvent : null,
6653         /** The button pressed in a mouse event */
6654         button : -1,
6655         /** True if the shift key was down during the event */
6656         shiftKey : false,
6657         /** True if the control key was down during the event */
6658         ctrlKey : false,
6659         /** True if the alt key was down during the event */
6660         altKey : false,
6661
6662         /** Key constant 
6663         * @type Number */
6664         BACKSPACE : 8,
6665         /** Key constant 
6666         * @type Number */
6667         TAB : 9,
6668         /** Key constant 
6669         * @type Number */
6670         RETURN : 13,
6671         /** Key constant 
6672         * @type Number */
6673         ENTER : 13,
6674         /** Key constant 
6675         * @type Number */
6676         SHIFT : 16,
6677         /** Key constant 
6678         * @type Number */
6679         CONTROL : 17,
6680         /** Key constant 
6681         * @type Number */
6682         ESC : 27,
6683         /** Key constant 
6684         * @type Number */
6685         SPACE : 32,
6686         /** Key constant 
6687         * @type Number */
6688         PAGEUP : 33,
6689         /** Key constant 
6690         * @type Number */
6691         PAGEDOWN : 34,
6692         /** Key constant 
6693         * @type Number */
6694         END : 35,
6695         /** Key constant 
6696         * @type Number */
6697         HOME : 36,
6698         /** Key constant 
6699         * @type Number */
6700         LEFT : 37,
6701         /** Key constant 
6702         * @type Number */
6703         UP : 38,
6704         /** Key constant 
6705         * @type Number */
6706         RIGHT : 39,
6707         /** Key constant 
6708         * @type Number */
6709         DOWN : 40,
6710         /** Key constant 
6711         * @type Number */
6712         DELETE : 46,
6713         /** Key constant 
6714         * @type Number */
6715         F5 : 116,
6716
6717            /** @private */
6718         setEvent : function(e){
6719             if(e == this || (e && e.browserEvent)){ // already wrapped
6720                 return e;
6721             }
6722             this.browserEvent = e;
6723             if(e){
6724                 // normalize buttons
6725                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6726                 if(e.type == 'click' && this.button == -1){
6727                     this.button = 0;
6728                 }
6729                 this.type = e.type;
6730                 this.shiftKey = e.shiftKey;
6731                 // mac metaKey behaves like ctrlKey
6732                 this.ctrlKey = e.ctrlKey || e.metaKey;
6733                 this.altKey = e.altKey;
6734                 // in getKey these will be normalized for the mac
6735                 this.keyCode = e.keyCode;
6736                 // keyup warnings on firefox.
6737                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6738                 // cache the target for the delayed and or buffered events
6739                 this.target = E.getTarget(e);
6740                 // same for XY
6741                 this.xy = E.getXY(e);
6742             }else{
6743                 this.button = -1;
6744                 this.shiftKey = false;
6745                 this.ctrlKey = false;
6746                 this.altKey = false;
6747                 this.keyCode = 0;
6748                 this.charCode =0;
6749                 this.target = null;
6750                 this.xy = [0, 0];
6751             }
6752             return this;
6753         },
6754
6755         /**
6756          * Stop the event (preventDefault and stopPropagation)
6757          */
6758         stopEvent : function(){
6759             if(this.browserEvent){
6760                 if(this.browserEvent.type == 'mousedown'){
6761                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6762                 }
6763                 E.stopEvent(this.browserEvent);
6764             }
6765         },
6766
6767         /**
6768          * Prevents the browsers default handling of the event.
6769          */
6770         preventDefault : function(){
6771             if(this.browserEvent){
6772                 E.preventDefault(this.browserEvent);
6773             }
6774         },
6775
6776         /** @private */
6777         isNavKeyPress : function(){
6778             var k = this.keyCode;
6779             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6780             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6781         },
6782
6783         isSpecialKey : function(){
6784             var k = this.keyCode;
6785             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6786             (k == 16) || (k == 17) ||
6787             (k >= 18 && k <= 20) ||
6788             (k >= 33 && k <= 35) ||
6789             (k >= 36 && k <= 39) ||
6790             (k >= 44 && k <= 45);
6791         },
6792         /**
6793          * Cancels bubbling of the event.
6794          */
6795         stopPropagation : function(){
6796             if(this.browserEvent){
6797                 if(this.type == 'mousedown'){
6798                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6799                 }
6800                 E.stopPropagation(this.browserEvent);
6801             }
6802         },
6803
6804         /**
6805          * Gets the key code for the event.
6806          * @return {Number}
6807          */
6808         getCharCode : function(){
6809             return this.charCode || this.keyCode;
6810         },
6811
6812         /**
6813          * Returns a normalized keyCode for the event.
6814          * @return {Number} The key code
6815          */
6816         getKey : function(){
6817             var k = this.keyCode || this.charCode;
6818             return Roo.isSafari ? (safariKeys[k] || k) : k;
6819         },
6820
6821         /**
6822          * Gets the x coordinate of the event.
6823          * @return {Number}
6824          */
6825         getPageX : function(){
6826             return this.xy[0];
6827         },
6828
6829         /**
6830          * Gets the y coordinate of the event.
6831          * @return {Number}
6832          */
6833         getPageY : function(){
6834             return this.xy[1];
6835         },
6836
6837         /**
6838          * Gets the time of the event.
6839          * @return {Number}
6840          */
6841         getTime : function(){
6842             if(this.browserEvent){
6843                 return E.getTime(this.browserEvent);
6844             }
6845             return null;
6846         },
6847
6848         /**
6849          * Gets the page coordinates of the event.
6850          * @return {Array} The xy values like [x, y]
6851          */
6852         getXY : function(){
6853             return this.xy;
6854         },
6855
6856         /**
6857          * Gets the target for the event.
6858          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6859          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6860                 search as a number or element (defaults to 10 || document.body)
6861          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6862          * @return {HTMLelement}
6863          */
6864         getTarget : function(selector, maxDepth, returnEl){
6865             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6866         },
6867         /**
6868          * Gets the related target.
6869          * @return {HTMLElement}
6870          */
6871         getRelatedTarget : function(){
6872             if(this.browserEvent){
6873                 return E.getRelatedTarget(this.browserEvent);
6874             }
6875             return null;
6876         },
6877
6878         /**
6879          * Normalizes mouse wheel delta across browsers
6880          * @return {Number} The delta
6881          */
6882         getWheelDelta : function(){
6883             var e = this.browserEvent;
6884             var delta = 0;
6885             if(e.wheelDelta){ /* IE/Opera. */
6886                 delta = e.wheelDelta/120;
6887             }else if(e.detail){ /* Mozilla case. */
6888                 delta = -e.detail/3;
6889             }
6890             return delta;
6891         },
6892
6893         /**
6894          * Returns true if the control, meta, shift or alt key was pressed during this event.
6895          * @return {Boolean}
6896          */
6897         hasModifier : function(){
6898             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6899         },
6900
6901         /**
6902          * Returns true if the target of this event equals el or is a child of el
6903          * @param {String/HTMLElement/Element} el
6904          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6905          * @return {Boolean}
6906          */
6907         within : function(el, related){
6908             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6909             return t && Roo.fly(el).contains(t);
6910         },
6911
6912         getPoint : function(){
6913             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6914         }
6915     };
6916
6917     return new Roo.EventObjectImpl();
6918 }();
6919             
6920     /*
6921  * Based on:
6922  * Ext JS Library 1.1.1
6923  * Copyright(c) 2006-2007, Ext JS, LLC.
6924  *
6925  * Originally Released Under LGPL - original licence link has changed is not relivant.
6926  *
6927  * Fork - LGPL
6928  * <script type="text/javascript">
6929  */
6930
6931  
6932 // was in Composite Element!??!?!
6933  
6934 (function(){
6935     var D = Roo.lib.Dom;
6936     var E = Roo.lib.Event;
6937     var A = Roo.lib.Anim;
6938
6939     // local style camelizing for speed
6940     var propCache = {};
6941     var camelRe = /(-[a-z])/gi;
6942     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6943     var view = document.defaultView;
6944
6945 /**
6946  * @class Roo.Element
6947  * Represents an Element in the DOM.<br><br>
6948  * Usage:<br>
6949 <pre><code>
6950 var el = Roo.get("my-div");
6951
6952 // or with getEl
6953 var el = getEl("my-div");
6954
6955 // or with a DOM element
6956 var el = Roo.get(myDivElement);
6957 </code></pre>
6958  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6959  * each call instead of constructing a new one.<br><br>
6960  * <b>Animations</b><br />
6961  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6962  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6963 <pre>
6964 Option    Default   Description
6965 --------- --------  ---------------------------------------------
6966 duration  .35       The duration of the animation in seconds
6967 easing    easeOut   The YUI easing method
6968 callback  none      A function to execute when the anim completes
6969 scope     this      The scope (this) of the callback function
6970 </pre>
6971 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6972 * manipulate the animation. Here's an example:
6973 <pre><code>
6974 var el = Roo.get("my-div");
6975
6976 // no animation
6977 el.setWidth(100);
6978
6979 // default animation
6980 el.setWidth(100, true);
6981
6982 // animation with some options set
6983 el.setWidth(100, {
6984     duration: 1,
6985     callback: this.foo,
6986     scope: this
6987 });
6988
6989 // using the "anim" property to get the Anim object
6990 var opt = {
6991     duration: 1,
6992     callback: this.foo,
6993     scope: this
6994 };
6995 el.setWidth(100, opt);
6996 ...
6997 if(opt.anim.isAnimated()){
6998     opt.anim.stop();
6999 }
7000 </code></pre>
7001 * <b> Composite (Collections of) Elements</b><br />
7002  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7003  * @constructor Create a new Element directly.
7004  * @param {String/HTMLElement} element
7005  * @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).
7006  */
7007     Roo.Element = function(element, forceNew){
7008         var dom = typeof element == "string" ?
7009                 document.getElementById(element) : element;
7010         if(!dom){ // invalid id/element
7011             return null;
7012         }
7013         var id = dom.id;
7014         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7015             return Roo.Element.cache[id];
7016         }
7017
7018         /**
7019          * The DOM element
7020          * @type HTMLElement
7021          */
7022         this.dom = dom;
7023
7024         /**
7025          * The DOM element ID
7026          * @type String
7027          */
7028         this.id = id || Roo.id(dom);
7029     };
7030
7031     var El = Roo.Element;
7032
7033     El.prototype = {
7034         /**
7035          * The element's default display mode  (defaults to "")
7036          * @type String
7037          */
7038         originalDisplay : "",
7039
7040         visibilityMode : 1,
7041         /**
7042          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7043          * @type String
7044          */
7045         defaultUnit : "px",
7046         /**
7047          * Sets the element's visibility mode. When setVisible() is called it
7048          * will use this to determine whether to set the visibility or the display property.
7049          * @param visMode Element.VISIBILITY or Element.DISPLAY
7050          * @return {Roo.Element} this
7051          */
7052         setVisibilityMode : function(visMode){
7053             this.visibilityMode = visMode;
7054             return this;
7055         },
7056         /**
7057          * Convenience method for setVisibilityMode(Element.DISPLAY)
7058          * @param {String} display (optional) What to set display to when visible
7059          * @return {Roo.Element} this
7060          */
7061         enableDisplayMode : function(display){
7062             this.setVisibilityMode(El.DISPLAY);
7063             if(typeof display != "undefined") this.originalDisplay = display;
7064             return this;
7065         },
7066
7067         /**
7068          * 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)
7069          * @param {String} selector The simple selector to test
7070          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7071                 search as a number or element (defaults to 10 || document.body)
7072          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7073          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7074          */
7075         findParent : function(simpleSelector, maxDepth, returnEl){
7076             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7077             maxDepth = maxDepth || 50;
7078             if(typeof maxDepth != "number"){
7079                 stopEl = Roo.getDom(maxDepth);
7080                 maxDepth = 10;
7081             }
7082             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7083                 if(dq.is(p, simpleSelector)){
7084                     return returnEl ? Roo.get(p) : p;
7085                 }
7086                 depth++;
7087                 p = p.parentNode;
7088             }
7089             return null;
7090         },
7091
7092
7093         /**
7094          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7095          * @param {String} selector The simple selector to test
7096          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7097                 search as a number or element (defaults to 10 || document.body)
7098          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7099          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7100          */
7101         findParentNode : function(simpleSelector, maxDepth, returnEl){
7102             var p = Roo.fly(this.dom.parentNode, '_internal');
7103             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7104         },
7105
7106         /**
7107          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7108          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7109          * @param {String} selector The simple selector to test
7110          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7111                 search as a number or element (defaults to 10 || document.body)
7112          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7113          */
7114         up : function(simpleSelector, maxDepth){
7115             return this.findParentNode(simpleSelector, maxDepth, true);
7116         },
7117
7118
7119
7120         /**
7121          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7122          * @param {String} selector The simple selector to test
7123          * @return {Boolean} True if this element matches the selector, else false
7124          */
7125         is : function(simpleSelector){
7126             return Roo.DomQuery.is(this.dom, simpleSelector);
7127         },
7128
7129         /**
7130          * Perform animation on this element.
7131          * @param {Object} args The YUI animation control args
7132          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7133          * @param {Function} onComplete (optional) Function to call when animation completes
7134          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7135          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7136          * @return {Roo.Element} this
7137          */
7138         animate : function(args, duration, onComplete, easing, animType){
7139             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7140             return this;
7141         },
7142
7143         /*
7144          * @private Internal animation call
7145          */
7146         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7147             animType = animType || 'run';
7148             opt = opt || {};
7149             var anim = Roo.lib.Anim[animType](
7150                 this.dom, args,
7151                 (opt.duration || defaultDur) || .35,
7152                 (opt.easing || defaultEase) || 'easeOut',
7153                 function(){
7154                     Roo.callback(cb, this);
7155                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7156                 },
7157                 this
7158             );
7159             opt.anim = anim;
7160             return anim;
7161         },
7162
7163         // private legacy anim prep
7164         preanim : function(a, i){
7165             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7166         },
7167
7168         /**
7169          * Removes worthless text nodes
7170          * @param {Boolean} forceReclean (optional) By default the element
7171          * keeps track if it has been cleaned already so
7172          * you can call this over and over. However, if you update the element and
7173          * need to force a reclean, you can pass true.
7174          */
7175         clean : function(forceReclean){
7176             if(this.isCleaned && forceReclean !== true){
7177                 return this;
7178             }
7179             var ns = /\S/;
7180             var d = this.dom, n = d.firstChild, ni = -1;
7181             while(n){
7182                 var nx = n.nextSibling;
7183                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7184                     d.removeChild(n);
7185                 }else{
7186                     n.nodeIndex = ++ni;
7187                 }
7188                 n = nx;
7189             }
7190             this.isCleaned = true;
7191             return this;
7192         },
7193
7194         // private
7195         calcOffsetsTo : function(el){
7196             el = Roo.get(el);
7197             var d = el.dom;
7198             var restorePos = false;
7199             if(el.getStyle('position') == 'static'){
7200                 el.position('relative');
7201                 restorePos = true;
7202             }
7203             var x = 0, y =0;
7204             var op = this.dom;
7205             while(op && op != d && op.tagName != 'HTML'){
7206                 x+= op.offsetLeft;
7207                 y+= op.offsetTop;
7208                 op = op.offsetParent;
7209             }
7210             if(restorePos){
7211                 el.position('static');
7212             }
7213             return [x, y];
7214         },
7215
7216         /**
7217          * Scrolls this element into view within the passed container.
7218          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7219          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7220          * @return {Roo.Element} this
7221          */
7222         scrollIntoView : function(container, hscroll){
7223             var c = Roo.getDom(container) || document.body;
7224             var el = this.dom;
7225
7226             var o = this.calcOffsetsTo(c),
7227                 l = o[0],
7228                 t = o[1],
7229                 b = t+el.offsetHeight,
7230                 r = l+el.offsetWidth;
7231
7232             var ch = c.clientHeight;
7233             var ct = parseInt(c.scrollTop, 10);
7234             var cl = parseInt(c.scrollLeft, 10);
7235             var cb = ct + ch;
7236             var cr = cl + c.clientWidth;
7237
7238             if(t < ct){
7239                 c.scrollTop = t;
7240             }else if(b > cb){
7241                 c.scrollTop = b-ch;
7242             }
7243
7244             if(hscroll !== false){
7245                 if(l < cl){
7246                     c.scrollLeft = l;
7247                 }else if(r > cr){
7248                     c.scrollLeft = r-c.clientWidth;
7249                 }
7250             }
7251             return this;
7252         },
7253
7254         // private
7255         scrollChildIntoView : function(child, hscroll){
7256             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7257         },
7258
7259         /**
7260          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7261          * the new height may not be available immediately.
7262          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7263          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7264          * @param {Function} onComplete (optional) Function to call when animation completes
7265          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7266          * @return {Roo.Element} this
7267          */
7268         autoHeight : function(animate, duration, onComplete, easing){
7269             var oldHeight = this.getHeight();
7270             this.clip();
7271             this.setHeight(1); // force clipping
7272             setTimeout(function(){
7273                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7274                 if(!animate){
7275                     this.setHeight(height);
7276                     this.unclip();
7277                     if(typeof onComplete == "function"){
7278                         onComplete();
7279                     }
7280                 }else{
7281                     this.setHeight(oldHeight); // restore original height
7282                     this.setHeight(height, animate, duration, function(){
7283                         this.unclip();
7284                         if(typeof onComplete == "function") onComplete();
7285                     }.createDelegate(this), easing);
7286                 }
7287             }.createDelegate(this), 0);
7288             return this;
7289         },
7290
7291         /**
7292          * Returns true if this element is an ancestor of the passed element
7293          * @param {HTMLElement/String} el The element to check
7294          * @return {Boolean} True if this element is an ancestor of el, else false
7295          */
7296         contains : function(el){
7297             if(!el){return false;}
7298             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7299         },
7300
7301         /**
7302          * Checks whether the element is currently visible using both visibility and display properties.
7303          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7304          * @return {Boolean} True if the element is currently visible, else false
7305          */
7306         isVisible : function(deep) {
7307             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7308             if(deep !== true || !vis){
7309                 return vis;
7310             }
7311             var p = this.dom.parentNode;
7312             while(p && p.tagName.toLowerCase() != "body"){
7313                 if(!Roo.fly(p, '_isVisible').isVisible()){
7314                     return false;
7315                 }
7316                 p = p.parentNode;
7317             }
7318             return true;
7319         },
7320
7321         /**
7322          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7323          * @param {String} selector The CSS selector
7324          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7325          * @return {CompositeElement/CompositeElementLite} The composite element
7326          */
7327         select : function(selector, unique){
7328             return El.select(selector, unique, this.dom);
7329         },
7330
7331         /**
7332          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7333          * @param {String} selector The CSS selector
7334          * @return {Array} An array of the matched nodes
7335          */
7336         query : function(selector, unique){
7337             return Roo.DomQuery.select(selector, this.dom);
7338         },
7339
7340         /**
7341          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7342          * @param {String} selector The CSS selector
7343          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7344          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7345          */
7346         child : function(selector, returnDom){
7347             var n = Roo.DomQuery.selectNode(selector, this.dom);
7348             return returnDom ? n : Roo.get(n);
7349         },
7350
7351         /**
7352          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7353          * @param {String} selector The CSS selector
7354          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7355          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7356          */
7357         down : function(selector, returnDom){
7358             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7359             return returnDom ? n : Roo.get(n);
7360         },
7361
7362         /**
7363          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7364          * @param {String} group The group the DD object is member of
7365          * @param {Object} config The DD config object
7366          * @param {Object} overrides An object containing methods to override/implement on the DD object
7367          * @return {Roo.dd.DD} The DD object
7368          */
7369         initDD : function(group, config, overrides){
7370             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7371             return Roo.apply(dd, overrides);
7372         },
7373
7374         /**
7375          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7376          * @param {String} group The group the DDProxy object is member of
7377          * @param {Object} config The DDProxy config object
7378          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7379          * @return {Roo.dd.DDProxy} The DDProxy object
7380          */
7381         initDDProxy : function(group, config, overrides){
7382             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7383             return Roo.apply(dd, overrides);
7384         },
7385
7386         /**
7387          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7388          * @param {String} group The group the DDTarget object is member of
7389          * @param {Object} config The DDTarget config object
7390          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7391          * @return {Roo.dd.DDTarget} The DDTarget object
7392          */
7393         initDDTarget : function(group, config, overrides){
7394             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7395             return Roo.apply(dd, overrides);
7396         },
7397
7398         /**
7399          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7400          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7401          * @param {Boolean} visible Whether the element is visible
7402          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7403          * @return {Roo.Element} this
7404          */
7405          setVisible : function(visible, animate){
7406             if(!animate || !A){
7407                 if(this.visibilityMode == El.DISPLAY){
7408                     this.setDisplayed(visible);
7409                 }else{
7410                     this.fixDisplay();
7411                     this.dom.style.visibility = visible ? "visible" : "hidden";
7412                 }
7413             }else{
7414                 // closure for composites
7415                 var dom = this.dom;
7416                 var visMode = this.visibilityMode;
7417                 if(visible){
7418                     this.setOpacity(.01);
7419                     this.setVisible(true);
7420                 }
7421                 this.anim({opacity: { to: (visible?1:0) }},
7422                       this.preanim(arguments, 1),
7423                       null, .35, 'easeIn', function(){
7424                          if(!visible){
7425                              if(visMode == El.DISPLAY){
7426                                  dom.style.display = "none";
7427                              }else{
7428                                  dom.style.visibility = "hidden";
7429                              }
7430                              Roo.get(dom).setOpacity(1);
7431                          }
7432                      });
7433             }
7434             return this;
7435         },
7436
7437         /**
7438          * Returns true if display is not "none"
7439          * @return {Boolean}
7440          */
7441         isDisplayed : function() {
7442             return this.getStyle("display") != "none";
7443         },
7444
7445         /**
7446          * Toggles the element's visibility or display, depending on visibility mode.
7447          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7448          * @return {Roo.Element} this
7449          */
7450         toggle : function(animate){
7451             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7452             return this;
7453         },
7454
7455         /**
7456          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7457          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7458          * @return {Roo.Element} this
7459          */
7460         setDisplayed : function(value) {
7461             if(typeof value == "boolean"){
7462                value = value ? this.originalDisplay : "none";
7463             }
7464             this.setStyle("display", value);
7465             return this;
7466         },
7467
7468         /**
7469          * Tries to focus the element. Any exceptions are caught and ignored.
7470          * @return {Roo.Element} this
7471          */
7472         focus : function() {
7473             try{
7474                 this.dom.focus();
7475             }catch(e){}
7476             return this;
7477         },
7478
7479         /**
7480          * Tries to blur the element. Any exceptions are caught and ignored.
7481          * @return {Roo.Element} this
7482          */
7483         blur : function() {
7484             try{
7485                 this.dom.blur();
7486             }catch(e){}
7487             return this;
7488         },
7489
7490         /**
7491          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7492          * @param {String/Array} className The CSS class to add, or an array of classes
7493          * @return {Roo.Element} this
7494          */
7495         addClass : function(className){
7496             if(className instanceof Array){
7497                 for(var i = 0, len = className.length; i < len; i++) {
7498                     this.addClass(className[i]);
7499                 }
7500             }else{
7501                 if(className && !this.hasClass(className)){
7502                     this.dom.className = this.dom.className + " " + className;
7503                 }
7504             }
7505             return this;
7506         },
7507
7508         /**
7509          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7510          * @param {String/Array} className The CSS class to add, or an array of classes
7511          * @return {Roo.Element} this
7512          */
7513         radioClass : function(className){
7514             var siblings = this.dom.parentNode.childNodes;
7515             for(var i = 0; i < siblings.length; i++) {
7516                 var s = siblings[i];
7517                 if(s.nodeType == 1){
7518                     Roo.get(s).removeClass(className);
7519                 }
7520             }
7521             this.addClass(className);
7522             return this;
7523         },
7524
7525         /**
7526          * Removes one or more CSS classes from the element.
7527          * @param {String/Array} className The CSS class to remove, or an array of classes
7528          * @return {Roo.Element} this
7529          */
7530         removeClass : function(className){
7531             if(!className || !this.dom.className){
7532                 return this;
7533             }
7534             if(className instanceof Array){
7535                 for(var i = 0, len = className.length; i < len; i++) {
7536                     this.removeClass(className[i]);
7537                 }
7538             }else{
7539                 if(this.hasClass(className)){
7540                     var re = this.classReCache[className];
7541                     if (!re) {
7542                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7543                        this.classReCache[className] = re;
7544                     }
7545                     this.dom.className =
7546                         this.dom.className.replace(re, " ");
7547                 }
7548             }
7549             return this;
7550         },
7551
7552         // private
7553         classReCache: {},
7554
7555         /**
7556          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7557          * @param {String} className The CSS class to toggle
7558          * @return {Roo.Element} this
7559          */
7560         toggleClass : function(className){
7561             if(this.hasClass(className)){
7562                 this.removeClass(className);
7563             }else{
7564                 this.addClass(className);
7565             }
7566             return this;
7567         },
7568
7569         /**
7570          * Checks if the specified CSS class exists on this element's DOM node.
7571          * @param {String} className The CSS class to check for
7572          * @return {Boolean} True if the class exists, else false
7573          */
7574         hasClass : function(className){
7575             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7576         },
7577
7578         /**
7579          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7580          * @param {String} oldClassName The CSS class to replace
7581          * @param {String} newClassName The replacement CSS class
7582          * @return {Roo.Element} this
7583          */
7584         replaceClass : function(oldClassName, newClassName){
7585             this.removeClass(oldClassName);
7586             this.addClass(newClassName);
7587             return this;
7588         },
7589
7590         /**
7591          * Returns an object with properties matching the styles requested.
7592          * For example, el.getStyles('color', 'font-size', 'width') might return
7593          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7594          * @param {String} style1 A style name
7595          * @param {String} style2 A style name
7596          * @param {String} etc.
7597          * @return {Object} The style object
7598          */
7599         getStyles : function(){
7600             var a = arguments, len = a.length, r = {};
7601             for(var i = 0; i < len; i++){
7602                 r[a[i]] = this.getStyle(a[i]);
7603             }
7604             return r;
7605         },
7606
7607         /**
7608          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7609          * @param {String} property The style property whose value is returned.
7610          * @return {String} The current value of the style property for this element.
7611          */
7612         getStyle : function(){
7613             return view && view.getComputedStyle ?
7614                 function(prop){
7615                     var el = this.dom, v, cs, camel;
7616                     if(prop == 'float'){
7617                         prop = "cssFloat";
7618                     }
7619                     if(el.style && (v = el.style[prop])){
7620                         return v;
7621                     }
7622                     if(cs = view.getComputedStyle(el, "")){
7623                         if(!(camel = propCache[prop])){
7624                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7625                         }
7626                         return cs[camel];
7627                     }
7628                     return null;
7629                 } :
7630                 function(prop){
7631                     var el = this.dom, v, cs, camel;
7632                     if(prop == 'opacity'){
7633                         if(typeof el.style.filter == 'string'){
7634                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7635                             if(m){
7636                                 var fv = parseFloat(m[1]);
7637                                 if(!isNaN(fv)){
7638                                     return fv ? fv / 100 : 0;
7639                                 }
7640                             }
7641                         }
7642                         return 1;
7643                     }else if(prop == 'float'){
7644                         prop = "styleFloat";
7645                     }
7646                     if(!(camel = propCache[prop])){
7647                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7648                     }
7649                     if(v = el.style[camel]){
7650                         return v;
7651                     }
7652                     if(cs = el.currentStyle){
7653                         return cs[camel];
7654                     }
7655                     return null;
7656                 };
7657         }(),
7658
7659         /**
7660          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7661          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7662          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7663          * @return {Roo.Element} this
7664          */
7665         setStyle : function(prop, value){
7666             if(typeof prop == "string"){
7667                 
7668                 if (prop == 'float') {
7669                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7670                     return this;
7671                 }
7672                 
7673                 var camel;
7674                 if(!(camel = propCache[prop])){
7675                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7676                 }
7677                 
7678                 if(camel == 'opacity') {
7679                     this.setOpacity(value);
7680                 }else{
7681                     this.dom.style[camel] = value;
7682                 }
7683             }else{
7684                 for(var style in prop){
7685                     if(typeof prop[style] != "function"){
7686                        this.setStyle(style, prop[style]);
7687                     }
7688                 }
7689             }
7690             return this;
7691         },
7692
7693         /**
7694          * More flexible version of {@link #setStyle} for setting style properties.
7695          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7696          * a function which returns such a specification.
7697          * @return {Roo.Element} this
7698          */
7699         applyStyles : function(style){
7700             Roo.DomHelper.applyStyles(this.dom, style);
7701             return this;
7702         },
7703
7704         /**
7705           * 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).
7706           * @return {Number} The X position of the element
7707           */
7708         getX : function(){
7709             return D.getX(this.dom);
7710         },
7711
7712         /**
7713           * 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).
7714           * @return {Number} The Y position of the element
7715           */
7716         getY : function(){
7717             return D.getY(this.dom);
7718         },
7719
7720         /**
7721           * 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).
7722           * @return {Array} The XY position of the element
7723           */
7724         getXY : function(){
7725             return D.getXY(this.dom);
7726         },
7727
7728         /**
7729          * 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).
7730          * @param {Number} The X position of the element
7731          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7732          * @return {Roo.Element} this
7733          */
7734         setX : function(x, animate){
7735             if(!animate || !A){
7736                 D.setX(this.dom, x);
7737             }else{
7738                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7739             }
7740             return this;
7741         },
7742
7743         /**
7744          * 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).
7745          * @param {Number} The Y position of the element
7746          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7747          * @return {Roo.Element} this
7748          */
7749         setY : function(y, animate){
7750             if(!animate || !A){
7751                 D.setY(this.dom, y);
7752             }else{
7753                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7754             }
7755             return this;
7756         },
7757
7758         /**
7759          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7760          * @param {String} left The left CSS property value
7761          * @return {Roo.Element} this
7762          */
7763         setLeft : function(left){
7764             this.setStyle("left", this.addUnits(left));
7765             return this;
7766         },
7767
7768         /**
7769          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7770          * @param {String} top The top CSS property value
7771          * @return {Roo.Element} this
7772          */
7773         setTop : function(top){
7774             this.setStyle("top", this.addUnits(top));
7775             return this;
7776         },
7777
7778         /**
7779          * Sets the element's CSS right style.
7780          * @param {String} right The right CSS property value
7781          * @return {Roo.Element} this
7782          */
7783         setRight : function(right){
7784             this.setStyle("right", this.addUnits(right));
7785             return this;
7786         },
7787
7788         /**
7789          * Sets the element's CSS bottom style.
7790          * @param {String} bottom The bottom CSS property value
7791          * @return {Roo.Element} this
7792          */
7793         setBottom : function(bottom){
7794             this.setStyle("bottom", this.addUnits(bottom));
7795             return this;
7796         },
7797
7798         /**
7799          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7800          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7801          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7802          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7803          * @return {Roo.Element} this
7804          */
7805         setXY : function(pos, animate){
7806             if(!animate || !A){
7807                 D.setXY(this.dom, pos);
7808             }else{
7809                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7810             }
7811             return this;
7812         },
7813
7814         /**
7815          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7816          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7817          * @param {Number} x X value for new position (coordinates are page-based)
7818          * @param {Number} y Y value for new position (coordinates are page-based)
7819          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7820          * @return {Roo.Element} this
7821          */
7822         setLocation : function(x, y, animate){
7823             this.setXY([x, y], this.preanim(arguments, 2));
7824             return this;
7825         },
7826
7827         /**
7828          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7829          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7830          * @param {Number} x X value for new position (coordinates are page-based)
7831          * @param {Number} y Y value for new position (coordinates are page-based)
7832          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7833          * @return {Roo.Element} this
7834          */
7835         moveTo : function(x, y, animate){
7836             this.setXY([x, y], this.preanim(arguments, 2));
7837             return this;
7838         },
7839
7840         /**
7841          * Returns the region of the given element.
7842          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7843          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7844          */
7845         getRegion : function(){
7846             return D.getRegion(this.dom);
7847         },
7848
7849         /**
7850          * Returns the offset height of the element
7851          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7852          * @return {Number} The element's height
7853          */
7854         getHeight : function(contentHeight){
7855             var h = this.dom.offsetHeight || 0;
7856             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7857         },
7858
7859         /**
7860          * Returns the offset width of the element
7861          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7862          * @return {Number} The element's width
7863          */
7864         getWidth : function(contentWidth){
7865             var w = this.dom.offsetWidth || 0;
7866             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7867         },
7868
7869         /**
7870          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7871          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7872          * if a height has not been set using CSS.
7873          * @return {Number}
7874          */
7875         getComputedHeight : function(){
7876             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7877             if(!h){
7878                 h = parseInt(this.getStyle('height'), 10) || 0;
7879                 if(!this.isBorderBox()){
7880                     h += this.getFrameWidth('tb');
7881                 }
7882             }
7883             return h;
7884         },
7885
7886         /**
7887          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7888          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7889          * if a width has not been set using CSS.
7890          * @return {Number}
7891          */
7892         getComputedWidth : function(){
7893             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7894             if(!w){
7895                 w = parseInt(this.getStyle('width'), 10) || 0;
7896                 if(!this.isBorderBox()){
7897                     w += this.getFrameWidth('lr');
7898                 }
7899             }
7900             return w;
7901         },
7902
7903         /**
7904          * Returns the size of the element.
7905          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7906          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7907          */
7908         getSize : function(contentSize){
7909             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7910         },
7911
7912         /**
7913          * Returns the width and height of the viewport.
7914          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7915          */
7916         getViewSize : function(){
7917             var d = this.dom, doc = document, aw = 0, ah = 0;
7918             if(d == doc || d == doc.body){
7919                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7920             }else{
7921                 return {
7922                     width : d.clientWidth,
7923                     height: d.clientHeight
7924                 };
7925             }
7926         },
7927
7928         /**
7929          * Returns the value of the "value" attribute
7930          * @param {Boolean} asNumber true to parse the value as a number
7931          * @return {String/Number}
7932          */
7933         getValue : function(asNumber){
7934             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7935         },
7936
7937         // private
7938         adjustWidth : function(width){
7939             if(typeof width == "number"){
7940                 if(this.autoBoxAdjust && !this.isBorderBox()){
7941                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7942                 }
7943                 if(width < 0){
7944                     width = 0;
7945                 }
7946             }
7947             return width;
7948         },
7949
7950         // private
7951         adjustHeight : function(height){
7952             if(typeof height == "number"){
7953                if(this.autoBoxAdjust && !this.isBorderBox()){
7954                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7955                }
7956                if(height < 0){
7957                    height = 0;
7958                }
7959             }
7960             return height;
7961         },
7962
7963         /**
7964          * Set the width of the element
7965          * @param {Number} width The new width
7966          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7967          * @return {Roo.Element} this
7968          */
7969         setWidth : function(width, animate){
7970             width = this.adjustWidth(width);
7971             if(!animate || !A){
7972                 this.dom.style.width = this.addUnits(width);
7973             }else{
7974                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7975             }
7976             return this;
7977         },
7978
7979         /**
7980          * Set the height of the element
7981          * @param {Number} height The new height
7982          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7983          * @return {Roo.Element} this
7984          */
7985          setHeight : function(height, animate){
7986             height = this.adjustHeight(height);
7987             if(!animate || !A){
7988                 this.dom.style.height = this.addUnits(height);
7989             }else{
7990                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7991             }
7992             return this;
7993         },
7994
7995         /**
7996          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7997          * @param {Number} width The new width
7998          * @param {Number} height The new height
7999          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8000          * @return {Roo.Element} this
8001          */
8002          setSize : function(width, height, animate){
8003             if(typeof width == "object"){ // in case of object from getSize()
8004                 height = width.height; width = width.width;
8005             }
8006             width = this.adjustWidth(width); height = this.adjustHeight(height);
8007             if(!animate || !A){
8008                 this.dom.style.width = this.addUnits(width);
8009                 this.dom.style.height = this.addUnits(height);
8010             }else{
8011                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8012             }
8013             return this;
8014         },
8015
8016         /**
8017          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8018          * @param {Number} x X value for new position (coordinates are page-based)
8019          * @param {Number} y Y value for new position (coordinates are page-based)
8020          * @param {Number} width The new width
8021          * @param {Number} height The new height
8022          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8023          * @return {Roo.Element} this
8024          */
8025         setBounds : function(x, y, width, height, animate){
8026             if(!animate || !A){
8027                 this.setSize(width, height);
8028                 this.setLocation(x, y);
8029             }else{
8030                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8031                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8032                               this.preanim(arguments, 4), 'motion');
8033             }
8034             return this;
8035         },
8036
8037         /**
8038          * 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.
8039          * @param {Roo.lib.Region} region The region to fill
8040          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8041          * @return {Roo.Element} this
8042          */
8043         setRegion : function(region, animate){
8044             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8045             return this;
8046         },
8047
8048         /**
8049          * Appends an event handler
8050          *
8051          * @param {String}   eventName     The type of event to append
8052          * @param {Function} fn        The method the event invokes
8053          * @param {Object} scope       (optional) The scope (this object) of the fn
8054          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8055          */
8056         addListener : function(eventName, fn, scope, options){
8057             if (this.dom) {
8058                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8059             }
8060         },
8061
8062         /**
8063          * Removes an event handler from this element
8064          * @param {String} eventName the type of event to remove
8065          * @param {Function} fn the method the event invokes
8066          * @return {Roo.Element} this
8067          */
8068         removeListener : function(eventName, fn){
8069             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8070             return this;
8071         },
8072
8073         /**
8074          * Removes all previous added listeners from this element
8075          * @return {Roo.Element} this
8076          */
8077         removeAllListeners : function(){
8078             E.purgeElement(this.dom);
8079             return this;
8080         },
8081
8082         relayEvent : function(eventName, observable){
8083             this.on(eventName, function(e){
8084                 observable.fireEvent(eventName, e);
8085             });
8086         },
8087
8088         /**
8089          * Set the opacity of the element
8090          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8091          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8092          * @return {Roo.Element} this
8093          */
8094          setOpacity : function(opacity, animate){
8095             if(!animate || !A){
8096                 var s = this.dom.style;
8097                 if(Roo.isIE){
8098                     s.zoom = 1;
8099                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8100                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8101                 }else{
8102                     s.opacity = opacity;
8103                 }
8104             }else{
8105                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8106             }
8107             return this;
8108         },
8109
8110         /**
8111          * Gets the left X coordinate
8112          * @param {Boolean} local True to get the local css position instead of page coordinate
8113          * @return {Number}
8114          */
8115         getLeft : function(local){
8116             if(!local){
8117                 return this.getX();
8118             }else{
8119                 return parseInt(this.getStyle("left"), 10) || 0;
8120             }
8121         },
8122
8123         /**
8124          * Gets the right X coordinate of the element (element X position + element width)
8125          * @param {Boolean} local True to get the local css position instead of page coordinate
8126          * @return {Number}
8127          */
8128         getRight : function(local){
8129             if(!local){
8130                 return this.getX() + this.getWidth();
8131             }else{
8132                 return (this.getLeft(true) + this.getWidth()) || 0;
8133             }
8134         },
8135
8136         /**
8137          * Gets the top Y coordinate
8138          * @param {Boolean} local True to get the local css position instead of page coordinate
8139          * @return {Number}
8140          */
8141         getTop : function(local) {
8142             if(!local){
8143                 return this.getY();
8144             }else{
8145                 return parseInt(this.getStyle("top"), 10) || 0;
8146             }
8147         },
8148
8149         /**
8150          * Gets the bottom Y coordinate of the element (element Y position + element height)
8151          * @param {Boolean} local True to get the local css position instead of page coordinate
8152          * @return {Number}
8153          */
8154         getBottom : function(local){
8155             if(!local){
8156                 return this.getY() + this.getHeight();
8157             }else{
8158                 return (this.getTop(true) + this.getHeight()) || 0;
8159             }
8160         },
8161
8162         /**
8163         * Initializes positioning on this element. If a desired position is not passed, it will make the
8164         * the element positioned relative IF it is not already positioned.
8165         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8166         * @param {Number} zIndex (optional) The zIndex to apply
8167         * @param {Number} x (optional) Set the page X position
8168         * @param {Number} y (optional) Set the page Y position
8169         */
8170         position : function(pos, zIndex, x, y){
8171             if(!pos){
8172                if(this.getStyle('position') == 'static'){
8173                    this.setStyle('position', 'relative');
8174                }
8175             }else{
8176                 this.setStyle("position", pos);
8177             }
8178             if(zIndex){
8179                 this.setStyle("z-index", zIndex);
8180             }
8181             if(x !== undefined && y !== undefined){
8182                 this.setXY([x, y]);
8183             }else if(x !== undefined){
8184                 this.setX(x);
8185             }else if(y !== undefined){
8186                 this.setY(y);
8187             }
8188         },
8189
8190         /**
8191         * Clear positioning back to the default when the document was loaded
8192         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8193         * @return {Roo.Element} this
8194          */
8195         clearPositioning : function(value){
8196             value = value ||'';
8197             this.setStyle({
8198                 "left": value,
8199                 "right": value,
8200                 "top": value,
8201                 "bottom": value,
8202                 "z-index": "",
8203                 "position" : "static"
8204             });
8205             return this;
8206         },
8207
8208         /**
8209         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8210         * snapshot before performing an update and then restoring the element.
8211         * @return {Object}
8212         */
8213         getPositioning : function(){
8214             var l = this.getStyle("left");
8215             var t = this.getStyle("top");
8216             return {
8217                 "position" : this.getStyle("position"),
8218                 "left" : l,
8219                 "right" : l ? "" : this.getStyle("right"),
8220                 "top" : t,
8221                 "bottom" : t ? "" : this.getStyle("bottom"),
8222                 "z-index" : this.getStyle("z-index")
8223             };
8224         },
8225
8226         /**
8227          * Gets the width of the border(s) for the specified side(s)
8228          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8229          * passing lr would get the border (l)eft width + the border (r)ight width.
8230          * @return {Number} The width of the sides passed added together
8231          */
8232         getBorderWidth : function(side){
8233             return this.addStyles(side, El.borders);
8234         },
8235
8236         /**
8237          * Gets the width of the padding(s) for the specified side(s)
8238          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8239          * passing lr would get the padding (l)eft + the padding (r)ight.
8240          * @return {Number} The padding of the sides passed added together
8241          */
8242         getPadding : function(side){
8243             return this.addStyles(side, El.paddings);
8244         },
8245
8246         /**
8247         * Set positioning with an object returned by getPositioning().
8248         * @param {Object} posCfg
8249         * @return {Roo.Element} this
8250          */
8251         setPositioning : function(pc){
8252             this.applyStyles(pc);
8253             if(pc.right == "auto"){
8254                 this.dom.style.right = "";
8255             }
8256             if(pc.bottom == "auto"){
8257                 this.dom.style.bottom = "";
8258             }
8259             return this;
8260         },
8261
8262         // private
8263         fixDisplay : function(){
8264             if(this.getStyle("display") == "none"){
8265                 this.setStyle("visibility", "hidden");
8266                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8267                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8268                     this.setStyle("display", "block");
8269                 }
8270             }
8271         },
8272
8273         /**
8274          * Quick set left and top adding default units
8275          * @param {String} left The left CSS property value
8276          * @param {String} top The top CSS property value
8277          * @return {Roo.Element} this
8278          */
8279          setLeftTop : function(left, top){
8280             this.dom.style.left = this.addUnits(left);
8281             this.dom.style.top = this.addUnits(top);
8282             return this;
8283         },
8284
8285         /**
8286          * Move this element relative to its current position.
8287          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8288          * @param {Number} distance How far to move the element in pixels
8289          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8290          * @return {Roo.Element} this
8291          */
8292          move : function(direction, distance, animate){
8293             var xy = this.getXY();
8294             direction = direction.toLowerCase();
8295             switch(direction){
8296                 case "l":
8297                 case "left":
8298                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8299                     break;
8300                case "r":
8301                case "right":
8302                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8303                     break;
8304                case "t":
8305                case "top":
8306                case "up":
8307                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8308                     break;
8309                case "b":
8310                case "bottom":
8311                case "down":
8312                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8313                     break;
8314             }
8315             return this;
8316         },
8317
8318         /**
8319          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8320          * @return {Roo.Element} this
8321          */
8322         clip : function(){
8323             if(!this.isClipped){
8324                this.isClipped = true;
8325                this.originalClip = {
8326                    "o": this.getStyle("overflow"),
8327                    "x": this.getStyle("overflow-x"),
8328                    "y": this.getStyle("overflow-y")
8329                };
8330                this.setStyle("overflow", "hidden");
8331                this.setStyle("overflow-x", "hidden");
8332                this.setStyle("overflow-y", "hidden");
8333             }
8334             return this;
8335         },
8336
8337         /**
8338          *  Return clipping (overflow) to original clipping before clip() was called
8339          * @return {Roo.Element} this
8340          */
8341         unclip : function(){
8342             if(this.isClipped){
8343                 this.isClipped = false;
8344                 var o = this.originalClip;
8345                 if(o.o){this.setStyle("overflow", o.o);}
8346                 if(o.x){this.setStyle("overflow-x", o.x);}
8347                 if(o.y){this.setStyle("overflow-y", o.y);}
8348             }
8349             return this;
8350         },
8351
8352
8353         /**
8354          * Gets the x,y coordinates specified by the anchor position on the element.
8355          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8356          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8357          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8358          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8359          * @return {Array} [x, y] An array containing the element's x and y coordinates
8360          */
8361         getAnchorXY : function(anchor, local, s){
8362             //Passing a different size is useful for pre-calculating anchors,
8363             //especially for anchored animations that change the el size.
8364
8365             var w, h, vp = false;
8366             if(!s){
8367                 var d = this.dom;
8368                 if(d == document.body || d == document){
8369                     vp = true;
8370                     w = D.getViewWidth(); h = D.getViewHeight();
8371                 }else{
8372                     w = this.getWidth(); h = this.getHeight();
8373                 }
8374             }else{
8375                 w = s.width;  h = s.height;
8376             }
8377             var x = 0, y = 0, r = Math.round;
8378             switch((anchor || "tl").toLowerCase()){
8379                 case "c":
8380                     x = r(w*.5);
8381                     y = r(h*.5);
8382                 break;
8383                 case "t":
8384                     x = r(w*.5);
8385                     y = 0;
8386                 break;
8387                 case "l":
8388                     x = 0;
8389                     y = r(h*.5);
8390                 break;
8391                 case "r":
8392                     x = w;
8393                     y = r(h*.5);
8394                 break;
8395                 case "b":
8396                     x = r(w*.5);
8397                     y = h;
8398                 break;
8399                 case "tl":
8400                     x = 0;
8401                     y = 0;
8402                 break;
8403                 case "bl":
8404                     x = 0;
8405                     y = h;
8406                 break;
8407                 case "br":
8408                     x = w;
8409                     y = h;
8410                 break;
8411                 case "tr":
8412                     x = w;
8413                     y = 0;
8414                 break;
8415             }
8416             if(local === true){
8417                 return [x, y];
8418             }
8419             if(vp){
8420                 var sc = this.getScroll();
8421                 return [x + sc.left, y + sc.top];
8422             }
8423             //Add the element's offset xy
8424             var o = this.getXY();
8425             return [x+o[0], y+o[1]];
8426         },
8427
8428         /**
8429          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8430          * supported position values.
8431          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8432          * @param {String} position The position to align to.
8433          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8434          * @return {Array} [x, y]
8435          */
8436         getAlignToXY : function(el, p, o){
8437             el = Roo.get(el);
8438             var d = this.dom;
8439             if(!el.dom){
8440                 throw "Element.alignTo with an element that doesn't exist";
8441             }
8442             var c = false; //constrain to viewport
8443             var p1 = "", p2 = "";
8444             o = o || [0,0];
8445
8446             if(!p){
8447                 p = "tl-bl";
8448             }else if(p == "?"){
8449                 p = "tl-bl?";
8450             }else if(p.indexOf("-") == -1){
8451                 p = "tl-" + p;
8452             }
8453             p = p.toLowerCase();
8454             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8455             if(!m){
8456                throw "Element.alignTo with an invalid alignment " + p;
8457             }
8458             p1 = m[1]; p2 = m[2]; c = !!m[3];
8459
8460             //Subtract the aligned el's internal xy from the target's offset xy
8461             //plus custom offset to get the aligned el's new offset xy
8462             var a1 = this.getAnchorXY(p1, true);
8463             var a2 = el.getAnchorXY(p2, false);
8464             var x = a2[0] - a1[0] + o[0];
8465             var y = a2[1] - a1[1] + o[1];
8466             if(c){
8467                 //constrain the aligned el to viewport if necessary
8468                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8469                 // 5px of margin for ie
8470                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8471
8472                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8473                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8474                 //otherwise swap the aligned el to the opposite border of the target.
8475                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8476                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8477                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8478                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8479
8480                var doc = document;
8481                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8482                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8483
8484                if((x+w) > dw + scrollX){
8485                     x = swapX ? r.left-w : dw+scrollX-w;
8486                 }
8487                if(x < scrollX){
8488                    x = swapX ? r.right : scrollX;
8489                }
8490                if((y+h) > dh + scrollY){
8491                     y = swapY ? r.top-h : dh+scrollY-h;
8492                 }
8493                if (y < scrollY){
8494                    y = swapY ? r.bottom : scrollY;
8495                }
8496             }
8497             return [x,y];
8498         },
8499
8500         // private
8501         getConstrainToXY : function(){
8502             var os = {top:0, left:0, bottom:0, right: 0};
8503
8504             return function(el, local, offsets, proposedXY){
8505                 el = Roo.get(el);
8506                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8507
8508                 var vw, vh, vx = 0, vy = 0;
8509                 if(el.dom == document.body || el.dom == document){
8510                     vw = Roo.lib.Dom.getViewWidth();
8511                     vh = Roo.lib.Dom.getViewHeight();
8512                 }else{
8513                     vw = el.dom.clientWidth;
8514                     vh = el.dom.clientHeight;
8515                     if(!local){
8516                         var vxy = el.getXY();
8517                         vx = vxy[0];
8518                         vy = vxy[1];
8519                     }
8520                 }
8521
8522                 var s = el.getScroll();
8523
8524                 vx += offsets.left + s.left;
8525                 vy += offsets.top + s.top;
8526
8527                 vw -= offsets.right;
8528                 vh -= offsets.bottom;
8529
8530                 var vr = vx+vw;
8531                 var vb = vy+vh;
8532
8533                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8534                 var x = xy[0], y = xy[1];
8535                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8536
8537                 // only move it if it needs it
8538                 var moved = false;
8539
8540                 // first validate right/bottom
8541                 if((x + w) > vr){
8542                     x = vr - w;
8543                     moved = true;
8544                 }
8545                 if((y + h) > vb){
8546                     y = vb - h;
8547                     moved = true;
8548                 }
8549                 // then make sure top/left isn't negative
8550                 if(x < vx){
8551                     x = vx;
8552                     moved = true;
8553                 }
8554                 if(y < vy){
8555                     y = vy;
8556                     moved = true;
8557                 }
8558                 return moved ? [x, y] : false;
8559             };
8560         }(),
8561
8562         // private
8563         adjustForConstraints : function(xy, parent, offsets){
8564             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8565         },
8566
8567         /**
8568          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8569          * document it aligns it to the viewport.
8570          * The position parameter is optional, and can be specified in any one of the following formats:
8571          * <ul>
8572          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8573          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8574          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8575          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8576          *   <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
8577          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8578          * </ul>
8579          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8580          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8581          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8582          * that specified in order to enforce the viewport constraints.
8583          * Following are all of the supported anchor positions:
8584     <pre>
8585     Value  Description
8586     -----  -----------------------------
8587     tl     The top left corner (default)
8588     t      The center of the top edge
8589     tr     The top right corner
8590     l      The center of the left edge
8591     c      In the center of the element
8592     r      The center of the right edge
8593     bl     The bottom left corner
8594     b      The center of the bottom edge
8595     br     The bottom right corner
8596     </pre>
8597     Example Usage:
8598     <pre><code>
8599     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8600     el.alignTo("other-el");
8601
8602     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8603     el.alignTo("other-el", "tr?");
8604
8605     // align the bottom right corner of el with the center left edge of other-el
8606     el.alignTo("other-el", "br-l?");
8607
8608     // align the center of el with the bottom left corner of other-el and
8609     // adjust the x position by -6 pixels (and the y position by 0)
8610     el.alignTo("other-el", "c-bl", [-6, 0]);
8611     </code></pre>
8612          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8613          * @param {String} position The position to align to.
8614          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8615          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8616          * @return {Roo.Element} this
8617          */
8618         alignTo : function(element, position, offsets, animate){
8619             var xy = this.getAlignToXY(element, position, offsets);
8620             this.setXY(xy, this.preanim(arguments, 3));
8621             return this;
8622         },
8623
8624         /**
8625          * Anchors an element to another element and realigns it when the window is resized.
8626          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8627          * @param {String} position The position to align to.
8628          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8629          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8630          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8631          * is a number, it is used as the buffer delay (defaults to 50ms).
8632          * @param {Function} callback The function to call after the animation finishes
8633          * @return {Roo.Element} this
8634          */
8635         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8636             var action = function(){
8637                 this.alignTo(el, alignment, offsets, animate);
8638                 Roo.callback(callback, this);
8639             };
8640             Roo.EventManager.onWindowResize(action, this);
8641             var tm = typeof monitorScroll;
8642             if(tm != 'undefined'){
8643                 Roo.EventManager.on(window, 'scroll', action, this,
8644                     {buffer: tm == 'number' ? monitorScroll : 50});
8645             }
8646             action.call(this); // align immediately
8647             return this;
8648         },
8649         /**
8650          * Clears any opacity settings from this element. Required in some cases for IE.
8651          * @return {Roo.Element} this
8652          */
8653         clearOpacity : function(){
8654             if (window.ActiveXObject) {
8655                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8656                     this.dom.style.filter = "";
8657                 }
8658             } else {
8659                 this.dom.style.opacity = "";
8660                 this.dom.style["-moz-opacity"] = "";
8661                 this.dom.style["-khtml-opacity"] = "";
8662             }
8663             return this;
8664         },
8665
8666         /**
8667          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8668          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8669          * @return {Roo.Element} this
8670          */
8671         hide : function(animate){
8672             this.setVisible(false, this.preanim(arguments, 0));
8673             return this;
8674         },
8675
8676         /**
8677         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8678         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8679          * @return {Roo.Element} this
8680          */
8681         show : function(animate){
8682             this.setVisible(true, this.preanim(arguments, 0));
8683             return this;
8684         },
8685
8686         /**
8687          * @private Test if size has a unit, otherwise appends the default
8688          */
8689         addUnits : function(size){
8690             return Roo.Element.addUnits(size, this.defaultUnit);
8691         },
8692
8693         /**
8694          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8695          * @return {Roo.Element} this
8696          */
8697         beginMeasure : function(){
8698             var el = this.dom;
8699             if(el.offsetWidth || el.offsetHeight){
8700                 return this; // offsets work already
8701             }
8702             var changed = [];
8703             var p = this.dom, b = document.body; // start with this element
8704             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8705                 var pe = Roo.get(p);
8706                 if(pe.getStyle('display') == 'none'){
8707                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8708                     p.style.visibility = "hidden";
8709                     p.style.display = "block";
8710                 }
8711                 p = p.parentNode;
8712             }
8713             this._measureChanged = changed;
8714             return this;
8715
8716         },
8717
8718         /**
8719          * Restores displays to before beginMeasure was called
8720          * @return {Roo.Element} this
8721          */
8722         endMeasure : function(){
8723             var changed = this._measureChanged;
8724             if(changed){
8725                 for(var i = 0, len = changed.length; i < len; i++) {
8726                     var r = changed[i];
8727                     r.el.style.visibility = r.visibility;
8728                     r.el.style.display = "none";
8729                 }
8730                 this._measureChanged = null;
8731             }
8732             return this;
8733         },
8734
8735         /**
8736         * Update the innerHTML of this element, optionally searching for and processing scripts
8737         * @param {String} html The new HTML
8738         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8739         * @param {Function} callback For async script loading you can be noticed when the update completes
8740         * @return {Roo.Element} this
8741          */
8742         update : function(html, loadScripts, callback){
8743             if(typeof html == "undefined"){
8744                 html = "";
8745             }
8746             if(loadScripts !== true){
8747                 this.dom.innerHTML = html;
8748                 if(typeof callback == "function"){
8749                     callback();
8750                 }
8751                 return this;
8752             }
8753             var id = Roo.id();
8754             var dom = this.dom;
8755
8756             html += '<span id="' + id + '"></span>';
8757
8758             E.onAvailable(id, function(){
8759                 var hd = document.getElementsByTagName("head")[0];
8760                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8761                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8762                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8763
8764                 var match;
8765                 while(match = re.exec(html)){
8766                     var attrs = match[1];
8767                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8768                     if(srcMatch && srcMatch[2]){
8769                        var s = document.createElement("script");
8770                        s.src = srcMatch[2];
8771                        var typeMatch = attrs.match(typeRe);
8772                        if(typeMatch && typeMatch[2]){
8773                            s.type = typeMatch[2];
8774                        }
8775                        hd.appendChild(s);
8776                     }else if(match[2] && match[2].length > 0){
8777                         if(window.execScript) {
8778                            window.execScript(match[2]);
8779                         } else {
8780                             /**
8781                              * eval:var:id
8782                              * eval:var:dom
8783                              * eval:var:html
8784                              * 
8785                              */
8786                            window.eval(match[2]);
8787                         }
8788                     }
8789                 }
8790                 var el = document.getElementById(id);
8791                 if(el){el.parentNode.removeChild(el);}
8792                 if(typeof callback == "function"){
8793                     callback();
8794                 }
8795             });
8796             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8797             return this;
8798         },
8799
8800         /**
8801          * Direct access to the UpdateManager update() method (takes the same parameters).
8802          * @param {String/Function} url The url for this request or a function to call to get the url
8803          * @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}
8804          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8805          * @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.
8806          * @return {Roo.Element} this
8807          */
8808         load : function(){
8809             var um = this.getUpdateManager();
8810             um.update.apply(um, arguments);
8811             return this;
8812         },
8813
8814         /**
8815         * Gets this element's UpdateManager
8816         * @return {Roo.UpdateManager} The UpdateManager
8817         */
8818         getUpdateManager : function(){
8819             if(!this.updateManager){
8820                 this.updateManager = new Roo.UpdateManager(this);
8821             }
8822             return this.updateManager;
8823         },
8824
8825         /**
8826          * Disables text selection for this element (normalized across browsers)
8827          * @return {Roo.Element} this
8828          */
8829         unselectable : function(){
8830             this.dom.unselectable = "on";
8831             this.swallowEvent("selectstart", true);
8832             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8833             this.addClass("x-unselectable");
8834             return this;
8835         },
8836
8837         /**
8838         * Calculates the x, y to center this element on the screen
8839         * @return {Array} The x, y values [x, y]
8840         */
8841         getCenterXY : function(){
8842             return this.getAlignToXY(document, 'c-c');
8843         },
8844
8845         /**
8846         * Centers the Element in either the viewport, or another Element.
8847         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8848         */
8849         center : function(centerIn){
8850             this.alignTo(centerIn || document, 'c-c');
8851             return this;
8852         },
8853
8854         /**
8855          * Tests various css rules/browsers to determine if this element uses a border box
8856          * @return {Boolean}
8857          */
8858         isBorderBox : function(){
8859             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8860         },
8861
8862         /**
8863          * Return a box {x, y, width, height} that can be used to set another elements
8864          * size/location to match this element.
8865          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8866          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8867          * @return {Object} box An object in the format {x, y, width, height}
8868          */
8869         getBox : function(contentBox, local){
8870             var xy;
8871             if(!local){
8872                 xy = this.getXY();
8873             }else{
8874                 var left = parseInt(this.getStyle("left"), 10) || 0;
8875                 var top = parseInt(this.getStyle("top"), 10) || 0;
8876                 xy = [left, top];
8877             }
8878             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8879             if(!contentBox){
8880                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8881             }else{
8882                 var l = this.getBorderWidth("l")+this.getPadding("l");
8883                 var r = this.getBorderWidth("r")+this.getPadding("r");
8884                 var t = this.getBorderWidth("t")+this.getPadding("t");
8885                 var b = this.getBorderWidth("b")+this.getPadding("b");
8886                 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)};
8887             }
8888             bx.right = bx.x + bx.width;
8889             bx.bottom = bx.y + bx.height;
8890             return bx;
8891         },
8892
8893         /**
8894          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8895          for more information about the sides.
8896          * @param {String} sides
8897          * @return {Number}
8898          */
8899         getFrameWidth : function(sides, onlyContentBox){
8900             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8901         },
8902
8903         /**
8904          * 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.
8905          * @param {Object} box The box to fill {x, y, width, height}
8906          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8907          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8908          * @return {Roo.Element} this
8909          */
8910         setBox : function(box, adjust, animate){
8911             var w = box.width, h = box.height;
8912             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8913                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8914                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8915             }
8916             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8917             return this;
8918         },
8919
8920         /**
8921          * Forces the browser to repaint this element
8922          * @return {Roo.Element} this
8923          */
8924          repaint : function(){
8925             var dom = this.dom;
8926             this.addClass("x-repaint");
8927             setTimeout(function(){
8928                 Roo.get(dom).removeClass("x-repaint");
8929             }, 1);
8930             return this;
8931         },
8932
8933         /**
8934          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8935          * then it returns the calculated width of the sides (see getPadding)
8936          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8937          * @return {Object/Number}
8938          */
8939         getMargins : function(side){
8940             if(!side){
8941                 return {
8942                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8943                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8944                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8945                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8946                 };
8947             }else{
8948                 return this.addStyles(side, El.margins);
8949              }
8950         },
8951
8952         // private
8953         addStyles : function(sides, styles){
8954             var val = 0, v, w;
8955             for(var i = 0, len = sides.length; i < len; i++){
8956                 v = this.getStyle(styles[sides.charAt(i)]);
8957                 if(v){
8958                      w = parseInt(v, 10);
8959                      if(w){ val += w; }
8960                 }
8961             }
8962             return val;
8963         },
8964
8965         /**
8966          * Creates a proxy element of this element
8967          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8968          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8969          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8970          * @return {Roo.Element} The new proxy element
8971          */
8972         createProxy : function(config, renderTo, matchBox){
8973             if(renderTo){
8974                 renderTo = Roo.getDom(renderTo);
8975             }else{
8976                 renderTo = document.body;
8977             }
8978             config = typeof config == "object" ?
8979                 config : {tag : "div", cls: config};
8980             var proxy = Roo.DomHelper.append(renderTo, config, true);
8981             if(matchBox){
8982                proxy.setBox(this.getBox());
8983             }
8984             return proxy;
8985         },
8986
8987         /**
8988          * Puts a mask over this element to disable user interaction. Requires core.css.
8989          * This method can only be applied to elements which accept child nodes.
8990          * @param {String} msg (optional) A message to display in the mask
8991          * @param {String} msgCls (optional) A css class to apply to the msg element
8992          * @return {Element} The mask  element
8993          */
8994         mask : function(msg, msgCls)
8995         {
8996             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
8997                 this.setStyle("position", "relative");
8998             }
8999             if(!this._mask){
9000                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9001             }
9002             this.addClass("x-masked");
9003             this._mask.setDisplayed(true);
9004             
9005             // we wander
9006             var z = 0;
9007             var dom = this.dom
9008             while (dom && dom.style) {
9009                 if (!isNaN(parseInt(dom.style.zIndex))) {
9010                     z = Math.max(z, parseInt(dom.style.zIndex));
9011                 }
9012                 dom = dom.parentNode;
9013             }
9014             // if we are masking the body - then it hides everything..
9015             if (this.dom == document.body) {
9016                 z = 1000000;
9017                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9018                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9019             }
9020            
9021             if(typeof msg == 'string'){
9022                 if(!this._maskMsg){
9023                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9024                 }
9025                 var mm = this._maskMsg;
9026                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9027                 if (mm.dom.firstChild) { // weird IE issue?
9028                     mm.dom.firstChild.innerHTML = msg;
9029                 }
9030                 mm.setDisplayed(true);
9031                 mm.center(this);
9032                 mm.setStyle('z-index', z + 102);
9033             }
9034             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9035                 this._mask.setHeight(this.getHeight());
9036             }
9037             this._mask.setStyle('z-index', z + 100);
9038             
9039             return this._mask;
9040         },
9041
9042         /**
9043          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9044          * it is cached for reuse.
9045          */
9046         unmask : function(removeEl){
9047             if(this._mask){
9048                 if(removeEl === true){
9049                     this._mask.remove();
9050                     delete this._mask;
9051                     if(this._maskMsg){
9052                         this._maskMsg.remove();
9053                         delete this._maskMsg;
9054                     }
9055                 }else{
9056                     this._mask.setDisplayed(false);
9057                     if(this._maskMsg){
9058                         this._maskMsg.setDisplayed(false);
9059                     }
9060                 }
9061             }
9062             this.removeClass("x-masked");
9063         },
9064
9065         /**
9066          * Returns true if this element is masked
9067          * @return {Boolean}
9068          */
9069         isMasked : function(){
9070             return this._mask && this._mask.isVisible();
9071         },
9072
9073         /**
9074          * Creates an iframe shim for this element to keep selects and other windowed objects from
9075          * showing through.
9076          * @return {Roo.Element} The new shim element
9077          */
9078         createShim : function(){
9079             var el = document.createElement('iframe');
9080             el.frameBorder = 'no';
9081             el.className = 'roo-shim';
9082             if(Roo.isIE && Roo.isSecure){
9083                 el.src = Roo.SSL_SECURE_URL;
9084             }
9085             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9086             shim.autoBoxAdjust = false;
9087             return shim;
9088         },
9089
9090         /**
9091          * Removes this element from the DOM and deletes it from the cache
9092          */
9093         remove : function(){
9094             if(this.dom.parentNode){
9095                 this.dom.parentNode.removeChild(this.dom);
9096             }
9097             delete El.cache[this.dom.id];
9098         },
9099
9100         /**
9101          * Sets up event handlers to add and remove a css class when the mouse is over this element
9102          * @param {String} className
9103          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9104          * mouseout events for children elements
9105          * @return {Roo.Element} this
9106          */
9107         addClassOnOver : function(className, preventFlicker){
9108             this.on("mouseover", function(){
9109                 Roo.fly(this, '_internal').addClass(className);
9110             }, this.dom);
9111             var removeFn = function(e){
9112                 if(preventFlicker !== true || !e.within(this, true)){
9113                     Roo.fly(this, '_internal').removeClass(className);
9114                 }
9115             };
9116             this.on("mouseout", removeFn, this.dom);
9117             return this;
9118         },
9119
9120         /**
9121          * Sets up event handlers to add and remove a css class when this element has the focus
9122          * @param {String} className
9123          * @return {Roo.Element} this
9124          */
9125         addClassOnFocus : function(className){
9126             this.on("focus", function(){
9127                 Roo.fly(this, '_internal').addClass(className);
9128             }, this.dom);
9129             this.on("blur", function(){
9130                 Roo.fly(this, '_internal').removeClass(className);
9131             }, this.dom);
9132             return this;
9133         },
9134         /**
9135          * 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)
9136          * @param {String} className
9137          * @return {Roo.Element} this
9138          */
9139         addClassOnClick : function(className){
9140             var dom = this.dom;
9141             this.on("mousedown", function(){
9142                 Roo.fly(dom, '_internal').addClass(className);
9143                 var d = Roo.get(document);
9144                 var fn = function(){
9145                     Roo.fly(dom, '_internal').removeClass(className);
9146                     d.removeListener("mouseup", fn);
9147                 };
9148                 d.on("mouseup", fn);
9149             });
9150             return this;
9151         },
9152
9153         /**
9154          * Stops the specified event from bubbling and optionally prevents the default action
9155          * @param {String} eventName
9156          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9157          * @return {Roo.Element} this
9158          */
9159         swallowEvent : function(eventName, preventDefault){
9160             var fn = function(e){
9161                 e.stopPropagation();
9162                 if(preventDefault){
9163                     e.preventDefault();
9164                 }
9165             };
9166             if(eventName instanceof Array){
9167                 for(var i = 0, len = eventName.length; i < len; i++){
9168                      this.on(eventName[i], fn);
9169                 }
9170                 return this;
9171             }
9172             this.on(eventName, fn);
9173             return this;
9174         },
9175
9176         /**
9177          * @private
9178          */
9179       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9180
9181         /**
9182          * Sizes this element to its parent element's dimensions performing
9183          * neccessary box adjustments.
9184          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9185          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9186          * @return {Roo.Element} this
9187          */
9188         fitToParent : function(monitorResize, targetParent) {
9189           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9190           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9191           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9192             return;
9193           }
9194           var p = Roo.get(targetParent || this.dom.parentNode);
9195           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9196           if (monitorResize === true) {
9197             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9198             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9199           }
9200           return this;
9201         },
9202
9203         /**
9204          * Gets the next sibling, skipping text nodes
9205          * @return {HTMLElement} The next sibling or null
9206          */
9207         getNextSibling : function(){
9208             var n = this.dom.nextSibling;
9209             while(n && n.nodeType != 1){
9210                 n = n.nextSibling;
9211             }
9212             return n;
9213         },
9214
9215         /**
9216          * Gets the previous sibling, skipping text nodes
9217          * @return {HTMLElement} The previous sibling or null
9218          */
9219         getPrevSibling : function(){
9220             var n = this.dom.previousSibling;
9221             while(n && n.nodeType != 1){
9222                 n = n.previousSibling;
9223             }
9224             return n;
9225         },
9226
9227
9228         /**
9229          * Appends the passed element(s) to this element
9230          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9231          * @return {Roo.Element} this
9232          */
9233         appendChild: function(el){
9234             el = Roo.get(el);
9235             el.appendTo(this);
9236             return this;
9237         },
9238
9239         /**
9240          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9241          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9242          * automatically generated with the specified attributes.
9243          * @param {HTMLElement} insertBefore (optional) a child element of this element
9244          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9245          * @return {Roo.Element} The new child element
9246          */
9247         createChild: function(config, insertBefore, returnDom){
9248             config = config || {tag:'div'};
9249             if(insertBefore){
9250                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9251             }
9252             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9253         },
9254
9255         /**
9256          * Appends this element to the passed element
9257          * @param {String/HTMLElement/Element} el The new parent element
9258          * @return {Roo.Element} this
9259          */
9260         appendTo: function(el){
9261             el = Roo.getDom(el);
9262             el.appendChild(this.dom);
9263             return this;
9264         },
9265
9266         /**
9267          * Inserts this element before the passed element in the DOM
9268          * @param {String/HTMLElement/Element} el The element to insert before
9269          * @return {Roo.Element} this
9270          */
9271         insertBefore: function(el){
9272             el = Roo.getDom(el);
9273             el.parentNode.insertBefore(this.dom, el);
9274             return this;
9275         },
9276
9277         /**
9278          * Inserts this element after the passed element in the DOM
9279          * @param {String/HTMLElement/Element} el The element to insert after
9280          * @return {Roo.Element} this
9281          */
9282         insertAfter: function(el){
9283             el = Roo.getDom(el);
9284             el.parentNode.insertBefore(this.dom, el.nextSibling);
9285             return this;
9286         },
9287
9288         /**
9289          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9290          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9291          * @return {Roo.Element} The new child
9292          */
9293         insertFirst: function(el, returnDom){
9294             el = el || {};
9295             if(typeof el == 'object' && !el.nodeType){ // dh config
9296                 return this.createChild(el, this.dom.firstChild, returnDom);
9297             }else{
9298                 el = Roo.getDom(el);
9299                 this.dom.insertBefore(el, this.dom.firstChild);
9300                 return !returnDom ? Roo.get(el) : el;
9301             }
9302         },
9303
9304         /**
9305          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9306          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9307          * @param {String} where (optional) 'before' or 'after' defaults to before
9308          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9309          * @return {Roo.Element} the inserted Element
9310          */
9311         insertSibling: function(el, where, returnDom){
9312             where = where ? where.toLowerCase() : 'before';
9313             el = el || {};
9314             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9315
9316             if(typeof el == 'object' && !el.nodeType){ // dh config
9317                 if(where == 'after' && !this.dom.nextSibling){
9318                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9319                 }else{
9320                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9321                 }
9322
9323             }else{
9324                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9325                             where == 'before' ? this.dom : this.dom.nextSibling);
9326                 if(!returnDom){
9327                     rt = Roo.get(rt);
9328                 }
9329             }
9330             return rt;
9331         },
9332
9333         /**
9334          * Creates and wraps this element with another element
9335          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9336          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9337          * @return {HTMLElement/Element} The newly created wrapper element
9338          */
9339         wrap: function(config, returnDom){
9340             if(!config){
9341                 config = {tag: "div"};
9342             }
9343             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9344             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9345             return newEl;
9346         },
9347
9348         /**
9349          * Replaces the passed element with this element
9350          * @param {String/HTMLElement/Element} el The element to replace
9351          * @return {Roo.Element} this
9352          */
9353         replace: function(el){
9354             el = Roo.get(el);
9355             this.insertBefore(el);
9356             el.remove();
9357             return this;
9358         },
9359
9360         /**
9361          * Inserts an html fragment into this element
9362          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9363          * @param {String} html The HTML fragment
9364          * @param {Boolean} returnEl True to return an Roo.Element
9365          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9366          */
9367         insertHtml : function(where, html, returnEl){
9368             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9369             return returnEl ? Roo.get(el) : el;
9370         },
9371
9372         /**
9373          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9374          * @param {Object} o The object with the attributes
9375          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9376          * @return {Roo.Element} this
9377          */
9378         set : function(o, useSet){
9379             var el = this.dom;
9380             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9381             for(var attr in o){
9382                 if(attr == "style" || typeof o[attr] == "function") continue;
9383                 if(attr=="cls"){
9384                     el.className = o["cls"];
9385                 }else{
9386                     if(useSet) el.setAttribute(attr, o[attr]);
9387                     else el[attr] = o[attr];
9388                 }
9389             }
9390             if(o.style){
9391                 Roo.DomHelper.applyStyles(el, o.style);
9392             }
9393             return this;
9394         },
9395
9396         /**
9397          * Convenience method for constructing a KeyMap
9398          * @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:
9399          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9400          * @param {Function} fn The function to call
9401          * @param {Object} scope (optional) The scope of the function
9402          * @return {Roo.KeyMap} The KeyMap created
9403          */
9404         addKeyListener : function(key, fn, scope){
9405             var config;
9406             if(typeof key != "object" || key instanceof Array){
9407                 config = {
9408                     key: key,
9409                     fn: fn,
9410                     scope: scope
9411                 };
9412             }else{
9413                 config = {
9414                     key : key.key,
9415                     shift : key.shift,
9416                     ctrl : key.ctrl,
9417                     alt : key.alt,
9418                     fn: fn,
9419                     scope: scope
9420                 };
9421             }
9422             return new Roo.KeyMap(this, config);
9423         },
9424
9425         /**
9426          * Creates a KeyMap for this element
9427          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9428          * @return {Roo.KeyMap} The KeyMap created
9429          */
9430         addKeyMap : function(config){
9431             return new Roo.KeyMap(this, config);
9432         },
9433
9434         /**
9435          * Returns true if this element is scrollable.
9436          * @return {Boolean}
9437          */
9438          isScrollable : function(){
9439             var dom = this.dom;
9440             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9441         },
9442
9443         /**
9444          * 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().
9445          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9446          * @param {Number} value The new scroll value
9447          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9448          * @return {Element} this
9449          */
9450
9451         scrollTo : function(side, value, animate){
9452             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9453             if(!animate || !A){
9454                 this.dom[prop] = value;
9455             }else{
9456                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9457                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9458             }
9459             return this;
9460         },
9461
9462         /**
9463          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9464          * within this element's scrollable range.
9465          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9466          * @param {Number} distance How far to scroll the element in pixels
9467          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9468          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9469          * was scrolled as far as it could go.
9470          */
9471          scroll : function(direction, distance, animate){
9472              if(!this.isScrollable()){
9473                  return;
9474              }
9475              var el = this.dom;
9476              var l = el.scrollLeft, t = el.scrollTop;
9477              var w = el.scrollWidth, h = el.scrollHeight;
9478              var cw = el.clientWidth, ch = el.clientHeight;
9479              direction = direction.toLowerCase();
9480              var scrolled = false;
9481              var a = this.preanim(arguments, 2);
9482              switch(direction){
9483                  case "l":
9484                  case "left":
9485                      if(w - l > cw){
9486                          var v = Math.min(l + distance, w-cw);
9487                          this.scrollTo("left", v, a);
9488                          scrolled = true;
9489                      }
9490                      break;
9491                 case "r":
9492                 case "right":
9493                      if(l > 0){
9494                          var v = Math.max(l - distance, 0);
9495                          this.scrollTo("left", v, a);
9496                          scrolled = true;
9497                      }
9498                      break;
9499                 case "t":
9500                 case "top":
9501                 case "up":
9502                      if(t > 0){
9503                          var v = Math.max(t - distance, 0);
9504                          this.scrollTo("top", v, a);
9505                          scrolled = true;
9506                      }
9507                      break;
9508                 case "b":
9509                 case "bottom":
9510                 case "down":
9511                      if(h - t > ch){
9512                          var v = Math.min(t + distance, h-ch);
9513                          this.scrollTo("top", v, a);
9514                          scrolled = true;
9515                      }
9516                      break;
9517              }
9518              return scrolled;
9519         },
9520
9521         /**
9522          * Translates the passed page coordinates into left/top css values for this element
9523          * @param {Number/Array} x The page x or an array containing [x, y]
9524          * @param {Number} y The page y
9525          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9526          */
9527         translatePoints : function(x, y){
9528             if(typeof x == 'object' || x instanceof Array){
9529                 y = x[1]; x = x[0];
9530             }
9531             var p = this.getStyle('position');
9532             var o = this.getXY();
9533
9534             var l = parseInt(this.getStyle('left'), 10);
9535             var t = parseInt(this.getStyle('top'), 10);
9536
9537             if(isNaN(l)){
9538                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9539             }
9540             if(isNaN(t)){
9541                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9542             }
9543
9544             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9545         },
9546
9547         /**
9548          * Returns the current scroll position of the element.
9549          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9550          */
9551         getScroll : function(){
9552             var d = this.dom, doc = document;
9553             if(d == doc || d == doc.body){
9554                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9555                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9556                 return {left: l, top: t};
9557             }else{
9558                 return {left: d.scrollLeft, top: d.scrollTop};
9559             }
9560         },
9561
9562         /**
9563          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9564          * are convert to standard 6 digit hex color.
9565          * @param {String} attr The css attribute
9566          * @param {String} defaultValue The default value to use when a valid color isn't found
9567          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9568          * YUI color anims.
9569          */
9570         getColor : function(attr, defaultValue, prefix){
9571             var v = this.getStyle(attr);
9572             if(!v || v == "transparent" || v == "inherit") {
9573                 return defaultValue;
9574             }
9575             var color = typeof prefix == "undefined" ? "#" : prefix;
9576             if(v.substr(0, 4) == "rgb("){
9577                 var rvs = v.slice(4, v.length -1).split(",");
9578                 for(var i = 0; i < 3; i++){
9579                     var h = parseInt(rvs[i]).toString(16);
9580                     if(h < 16){
9581                         h = "0" + h;
9582                     }
9583                     color += h;
9584                 }
9585             } else {
9586                 if(v.substr(0, 1) == "#"){
9587                     if(v.length == 4) {
9588                         for(var i = 1; i < 4; i++){
9589                             var c = v.charAt(i);
9590                             color +=  c + c;
9591                         }
9592                     }else if(v.length == 7){
9593                         color += v.substr(1);
9594                     }
9595                 }
9596             }
9597             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9598         },
9599
9600         /**
9601          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9602          * gradient background, rounded corners and a 4-way shadow.
9603          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9604          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9605          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9606          * @return {Roo.Element} this
9607          */
9608         boxWrap : function(cls){
9609             cls = cls || 'x-box';
9610             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9611             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9612             return el;
9613         },
9614
9615         /**
9616          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9617          * @param {String} namespace The namespace in which to look for the attribute
9618          * @param {String} name The attribute name
9619          * @return {String} The attribute value
9620          */
9621         getAttributeNS : Roo.isIE ? function(ns, name){
9622             var d = this.dom;
9623             var type = typeof d[ns+":"+name];
9624             if(type != 'undefined' && type != 'unknown'){
9625                 return d[ns+":"+name];
9626             }
9627             return d[name];
9628         } : function(ns, name){
9629             var d = this.dom;
9630             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9631         },
9632         
9633         
9634         /**
9635          * Sets or Returns the value the dom attribute value
9636          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9637          * @param {String} value (optional) The value to set the attribute to
9638          * @return {String} The attribute value
9639          */
9640         attr : function(name){
9641             if (arguments.length > 1) {
9642                 this.dom.setAttribute(name, arguments[1]);
9643                 return arguments[1];
9644             }
9645             if (typeof(name) == 'object') {
9646                 for(var i in name) {
9647                     this.attr(i, name[i]);
9648                 }
9649                 return name;
9650             }
9651             
9652             
9653             if (!this.dom.hasAttribute(name)) {
9654                 return undefined;
9655             }
9656             return this.dom.getAttribute(name);
9657         }
9658         
9659         
9660         
9661     };
9662
9663     var ep = El.prototype;
9664
9665     /**
9666      * Appends an event handler (Shorthand for addListener)
9667      * @param {String}   eventName     The type of event to append
9668      * @param {Function} fn        The method the event invokes
9669      * @param {Object} scope       (optional) The scope (this object) of the fn
9670      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9671      * @method
9672      */
9673     ep.on = ep.addListener;
9674         // backwards compat
9675     ep.mon = ep.addListener;
9676
9677     /**
9678      * Removes an event handler from this element (shorthand for removeListener)
9679      * @param {String} eventName the type of event to remove
9680      * @param {Function} fn the method the event invokes
9681      * @return {Roo.Element} this
9682      * @method
9683      */
9684     ep.un = ep.removeListener;
9685
9686     /**
9687      * true to automatically adjust width and height settings for box-model issues (default to true)
9688      */
9689     ep.autoBoxAdjust = true;
9690
9691     // private
9692     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9693
9694     // private
9695     El.addUnits = function(v, defaultUnit){
9696         if(v === "" || v == "auto"){
9697             return v;
9698         }
9699         if(v === undefined){
9700             return '';
9701         }
9702         if(typeof v == "number" || !El.unitPattern.test(v)){
9703             return v + (defaultUnit || 'px');
9704         }
9705         return v;
9706     };
9707
9708     // special markup used throughout Roo when box wrapping elements
9709     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>';
9710     /**
9711      * Visibility mode constant - Use visibility to hide element
9712      * @static
9713      * @type Number
9714      */
9715     El.VISIBILITY = 1;
9716     /**
9717      * Visibility mode constant - Use display to hide element
9718      * @static
9719      * @type Number
9720      */
9721     El.DISPLAY = 2;
9722
9723     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9724     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9725     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9726
9727
9728
9729     /**
9730      * @private
9731      */
9732     El.cache = {};
9733
9734     var docEl;
9735
9736     /**
9737      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9738      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9739      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9740      * @return {Element} The Element object
9741      * @static
9742      */
9743     El.get = function(el){
9744         var ex, elm, id;
9745         if(!el){ return null; }
9746         if(typeof el == "string"){ // element id
9747             if(!(elm = document.getElementById(el))){
9748                 return null;
9749             }
9750             if(ex = El.cache[el]){
9751                 ex.dom = elm;
9752             }else{
9753                 ex = El.cache[el] = new El(elm);
9754             }
9755             return ex;
9756         }else if(el.tagName){ // dom element
9757             if(!(id = el.id)){
9758                 id = Roo.id(el);
9759             }
9760             if(ex = El.cache[id]){
9761                 ex.dom = el;
9762             }else{
9763                 ex = El.cache[id] = new El(el);
9764             }
9765             return ex;
9766         }else if(el instanceof El){
9767             if(el != docEl){
9768                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9769                                                               // catch case where it hasn't been appended
9770                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9771             }
9772             return el;
9773         }else if(el.isComposite){
9774             return el;
9775         }else if(el instanceof Array){
9776             return El.select(el);
9777         }else if(el == document){
9778             // create a bogus element object representing the document object
9779             if(!docEl){
9780                 var f = function(){};
9781                 f.prototype = El.prototype;
9782                 docEl = new f();
9783                 docEl.dom = document;
9784             }
9785             return docEl;
9786         }
9787         return null;
9788     };
9789
9790     // private
9791     El.uncache = function(el){
9792         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9793             if(a[i]){
9794                 delete El.cache[a[i].id || a[i]];
9795             }
9796         }
9797     };
9798
9799     // private
9800     // Garbage collection - uncache elements/purge listeners on orphaned elements
9801     // so we don't hold a reference and cause the browser to retain them
9802     El.garbageCollect = function(){
9803         if(!Roo.enableGarbageCollector){
9804             clearInterval(El.collectorThread);
9805             return;
9806         }
9807         for(var eid in El.cache){
9808             var el = El.cache[eid], d = el.dom;
9809             // -------------------------------------------------------
9810             // Determining what is garbage:
9811             // -------------------------------------------------------
9812             // !d
9813             // dom node is null, definitely garbage
9814             // -------------------------------------------------------
9815             // !d.parentNode
9816             // no parentNode == direct orphan, definitely garbage
9817             // -------------------------------------------------------
9818             // !d.offsetParent && !document.getElementById(eid)
9819             // display none elements have no offsetParent so we will
9820             // also try to look it up by it's id. However, check
9821             // offsetParent first so we don't do unneeded lookups.
9822             // This enables collection of elements that are not orphans
9823             // directly, but somewhere up the line they have an orphan
9824             // parent.
9825             // -------------------------------------------------------
9826             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9827                 delete El.cache[eid];
9828                 if(d && Roo.enableListenerCollection){
9829                     E.purgeElement(d);
9830                 }
9831             }
9832         }
9833     }
9834     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9835
9836
9837     // dom is optional
9838     El.Flyweight = function(dom){
9839         this.dom = dom;
9840     };
9841     El.Flyweight.prototype = El.prototype;
9842
9843     El._flyweights = {};
9844     /**
9845      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9846      * the dom node can be overwritten by other code.
9847      * @param {String/HTMLElement} el The dom node or id
9848      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9849      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9850      * @static
9851      * @return {Element} The shared Element object
9852      */
9853     El.fly = function(el, named){
9854         named = named || '_global';
9855         el = Roo.getDom(el);
9856         if(!el){
9857             return null;
9858         }
9859         if(!El._flyweights[named]){
9860             El._flyweights[named] = new El.Flyweight();
9861         }
9862         El._flyweights[named].dom = el;
9863         return El._flyweights[named];
9864     };
9865
9866     /**
9867      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9868      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9869      * Shorthand of {@link Roo.Element#get}
9870      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9871      * @return {Element} The Element object
9872      * @member Roo
9873      * @method get
9874      */
9875     Roo.get = El.get;
9876     /**
9877      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9878      * the dom node can be overwritten by other code.
9879      * Shorthand of {@link Roo.Element#fly}
9880      * @param {String/HTMLElement} el The dom node or id
9881      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9882      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9883      * @static
9884      * @return {Element} The shared Element object
9885      * @member Roo
9886      * @method fly
9887      */
9888     Roo.fly = El.fly;
9889
9890     // speedy lookup for elements never to box adjust
9891     var noBoxAdjust = Roo.isStrict ? {
9892         select:1
9893     } : {
9894         input:1, select:1, textarea:1
9895     };
9896     if(Roo.isIE || Roo.isGecko){
9897         noBoxAdjust['button'] = 1;
9898     }
9899
9900
9901     Roo.EventManager.on(window, 'unload', function(){
9902         delete El.cache;
9903         delete El._flyweights;
9904     });
9905 })();
9906
9907
9908
9909
9910 if(Roo.DomQuery){
9911     Roo.Element.selectorFunction = Roo.DomQuery.select;
9912 }
9913
9914 Roo.Element.select = function(selector, unique, root){
9915     var els;
9916     if(typeof selector == "string"){
9917         els = Roo.Element.selectorFunction(selector, root);
9918     }else if(selector.length !== undefined){
9919         els = selector;
9920     }else{
9921         throw "Invalid selector";
9922     }
9923     if(unique === true){
9924         return new Roo.CompositeElement(els);
9925     }else{
9926         return new Roo.CompositeElementLite(els);
9927     }
9928 };
9929 /**
9930  * Selects elements based on the passed CSS selector to enable working on them as 1.
9931  * @param {String/Array} selector The CSS selector or an array of elements
9932  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9933  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9934  * @return {CompositeElementLite/CompositeElement}
9935  * @member Roo
9936  * @method select
9937  */
9938 Roo.select = Roo.Element.select;
9939
9940
9941
9942
9943
9944
9945
9946
9947
9948
9949
9950
9951
9952
9953 /*
9954  * Based on:
9955  * Ext JS Library 1.1.1
9956  * Copyright(c) 2006-2007, Ext JS, LLC.
9957  *
9958  * Originally Released Under LGPL - original licence link has changed is not relivant.
9959  *
9960  * Fork - LGPL
9961  * <script type="text/javascript">
9962  */
9963
9964
9965
9966 //Notifies Element that fx methods are available
9967 Roo.enableFx = true;
9968
9969 /**
9970  * @class Roo.Fx
9971  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9972  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9973  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9974  * Element effects to work.</p><br/>
9975  *
9976  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9977  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9978  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9979  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9980  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9981  * expected results and should be done with care.</p><br/>
9982  *
9983  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9984  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9985 <pre>
9986 Value  Description
9987 -----  -----------------------------
9988 tl     The top left corner
9989 t      The center of the top edge
9990 tr     The top right corner
9991 l      The center of the left edge
9992 r      The center of the right edge
9993 bl     The bottom left corner
9994 b      The center of the bottom edge
9995 br     The bottom right corner
9996 </pre>
9997  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9998  * below are common options that can be passed to any Fx method.</b>
9999  * @cfg {Function} callback A function called when the effect is finished
10000  * @cfg {Object} scope The scope of the effect function
10001  * @cfg {String} easing A valid Easing value for the effect
10002  * @cfg {String} afterCls A css class to apply after the effect
10003  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10004  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10005  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10006  * effects that end with the element being visually hidden, ignored otherwise)
10007  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10008  * a function which returns such a specification that will be applied to the Element after the effect finishes
10009  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10010  * @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
10011  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10012  */
10013 Roo.Fx = {
10014         /**
10015          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10016          * origin for the slide effect.  This function automatically handles wrapping the element with
10017          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10018          * Usage:
10019          *<pre><code>
10020 // default: slide the element in from the top
10021 el.slideIn();
10022
10023 // custom: slide the element in from the right with a 2-second duration
10024 el.slideIn('r', { duration: 2 });
10025
10026 // common config options shown with default values
10027 el.slideIn('t', {
10028     easing: 'easeOut',
10029     duration: .5
10030 });
10031 </code></pre>
10032          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10033          * @param {Object} options (optional) Object literal with any of the Fx config options
10034          * @return {Roo.Element} The Element
10035          */
10036     slideIn : function(anchor, o){
10037         var el = this.getFxEl();
10038         o = o || {};
10039
10040         el.queueFx(o, function(){
10041
10042             anchor = anchor || "t";
10043
10044             // fix display to visibility
10045             this.fixDisplay();
10046
10047             // restore values after effect
10048             var r = this.getFxRestore();
10049             var b = this.getBox();
10050             // fixed size for slide
10051             this.setSize(b);
10052
10053             // wrap if needed
10054             var wrap = this.fxWrap(r.pos, o, "hidden");
10055
10056             var st = this.dom.style;
10057             st.visibility = "visible";
10058             st.position = "absolute";
10059
10060             // clear out temp styles after slide and unwrap
10061             var after = function(){
10062                 el.fxUnwrap(wrap, r.pos, o);
10063                 st.width = r.width;
10064                 st.height = r.height;
10065                 el.afterFx(o);
10066             };
10067             // time to calc the positions
10068             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10069
10070             switch(anchor.toLowerCase()){
10071                 case "t":
10072                     wrap.setSize(b.width, 0);
10073                     st.left = st.bottom = "0";
10074                     a = {height: bh};
10075                 break;
10076                 case "l":
10077                     wrap.setSize(0, b.height);
10078                     st.right = st.top = "0";
10079                     a = {width: bw};
10080                 break;
10081                 case "r":
10082                     wrap.setSize(0, b.height);
10083                     wrap.setX(b.right);
10084                     st.left = st.top = "0";
10085                     a = {width: bw, points: pt};
10086                 break;
10087                 case "b":
10088                     wrap.setSize(b.width, 0);
10089                     wrap.setY(b.bottom);
10090                     st.left = st.top = "0";
10091                     a = {height: bh, points: pt};
10092                 break;
10093                 case "tl":
10094                     wrap.setSize(0, 0);
10095                     st.right = st.bottom = "0";
10096                     a = {width: bw, height: bh};
10097                 break;
10098                 case "bl":
10099                     wrap.setSize(0, 0);
10100                     wrap.setY(b.y+b.height);
10101                     st.right = st.top = "0";
10102                     a = {width: bw, height: bh, points: pt};
10103                 break;
10104                 case "br":
10105                     wrap.setSize(0, 0);
10106                     wrap.setXY([b.right, b.bottom]);
10107                     st.left = st.top = "0";
10108                     a = {width: bw, height: bh, points: pt};
10109                 break;
10110                 case "tr":
10111                     wrap.setSize(0, 0);
10112                     wrap.setX(b.x+b.width);
10113                     st.left = st.bottom = "0";
10114                     a = {width: bw, height: bh, points: pt};
10115                 break;
10116             }
10117             this.dom.style.visibility = "visible";
10118             wrap.show();
10119
10120             arguments.callee.anim = wrap.fxanim(a,
10121                 o,
10122                 'motion',
10123                 .5,
10124                 'easeOut', after);
10125         });
10126         return this;
10127     },
10128     
10129         /**
10130          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10131          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10132          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10133          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10134          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10135          * Usage:
10136          *<pre><code>
10137 // default: slide the element out to the top
10138 el.slideOut();
10139
10140 // custom: slide the element out to the right with a 2-second duration
10141 el.slideOut('r', { duration: 2 });
10142
10143 // common config options shown with default values
10144 el.slideOut('t', {
10145     easing: 'easeOut',
10146     duration: .5,
10147     remove: false,
10148     useDisplay: false
10149 });
10150 </code></pre>
10151          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10152          * @param {Object} options (optional) Object literal with any of the Fx config options
10153          * @return {Roo.Element} The Element
10154          */
10155     slideOut : function(anchor, o){
10156         var el = this.getFxEl();
10157         o = o || {};
10158
10159         el.queueFx(o, function(){
10160
10161             anchor = anchor || "t";
10162
10163             // restore values after effect
10164             var r = this.getFxRestore();
10165             
10166             var b = this.getBox();
10167             // fixed size for slide
10168             this.setSize(b);
10169
10170             // wrap if needed
10171             var wrap = this.fxWrap(r.pos, o, "visible");
10172
10173             var st = this.dom.style;
10174             st.visibility = "visible";
10175             st.position = "absolute";
10176
10177             wrap.setSize(b);
10178
10179             var after = function(){
10180                 if(o.useDisplay){
10181                     el.setDisplayed(false);
10182                 }else{
10183                     el.hide();
10184                 }
10185
10186                 el.fxUnwrap(wrap, r.pos, o);
10187
10188                 st.width = r.width;
10189                 st.height = r.height;
10190
10191                 el.afterFx(o);
10192             };
10193
10194             var a, zero = {to: 0};
10195             switch(anchor.toLowerCase()){
10196                 case "t":
10197                     st.left = st.bottom = "0";
10198                     a = {height: zero};
10199                 break;
10200                 case "l":
10201                     st.right = st.top = "0";
10202                     a = {width: zero};
10203                 break;
10204                 case "r":
10205                     st.left = st.top = "0";
10206                     a = {width: zero, points: {to:[b.right, b.y]}};
10207                 break;
10208                 case "b":
10209                     st.left = st.top = "0";
10210                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10211                 break;
10212                 case "tl":
10213                     st.right = st.bottom = "0";
10214                     a = {width: zero, height: zero};
10215                 break;
10216                 case "bl":
10217                     st.right = st.top = "0";
10218                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10219                 break;
10220                 case "br":
10221                     st.left = st.top = "0";
10222                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10223                 break;
10224                 case "tr":
10225                     st.left = st.bottom = "0";
10226                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10227                 break;
10228             }
10229
10230             arguments.callee.anim = wrap.fxanim(a,
10231                 o,
10232                 'motion',
10233                 .5,
10234                 "easeOut", after);
10235         });
10236         return this;
10237     },
10238
10239         /**
10240          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10241          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10242          * The element must be removed from the DOM using the 'remove' config option if desired.
10243          * Usage:
10244          *<pre><code>
10245 // default
10246 el.puff();
10247
10248 // common config options shown with default values
10249 el.puff({
10250     easing: 'easeOut',
10251     duration: .5,
10252     remove: false,
10253     useDisplay: false
10254 });
10255 </code></pre>
10256          * @param {Object} options (optional) Object literal with any of the Fx config options
10257          * @return {Roo.Element} The Element
10258          */
10259     puff : function(o){
10260         var el = this.getFxEl();
10261         o = o || {};
10262
10263         el.queueFx(o, function(){
10264             this.clearOpacity();
10265             this.show();
10266
10267             // restore values after effect
10268             var r = this.getFxRestore();
10269             var st = this.dom.style;
10270
10271             var after = function(){
10272                 if(o.useDisplay){
10273                     el.setDisplayed(false);
10274                 }else{
10275                     el.hide();
10276                 }
10277
10278                 el.clearOpacity();
10279
10280                 el.setPositioning(r.pos);
10281                 st.width = r.width;
10282                 st.height = r.height;
10283                 st.fontSize = '';
10284                 el.afterFx(o);
10285             };
10286
10287             var width = this.getWidth();
10288             var height = this.getHeight();
10289
10290             arguments.callee.anim = this.fxanim({
10291                     width : {to: this.adjustWidth(width * 2)},
10292                     height : {to: this.adjustHeight(height * 2)},
10293                     points : {by: [-(width * .5), -(height * .5)]},
10294                     opacity : {to: 0},
10295                     fontSize: {to:200, unit: "%"}
10296                 },
10297                 o,
10298                 'motion',
10299                 .5,
10300                 "easeOut", after);
10301         });
10302         return this;
10303     },
10304
10305         /**
10306          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10307          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10308          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10309          * Usage:
10310          *<pre><code>
10311 // default
10312 el.switchOff();
10313
10314 // all config options shown with default values
10315 el.switchOff({
10316     easing: 'easeIn',
10317     duration: .3,
10318     remove: false,
10319     useDisplay: false
10320 });
10321 </code></pre>
10322          * @param {Object} options (optional) Object literal with any of the Fx config options
10323          * @return {Roo.Element} The Element
10324          */
10325     switchOff : function(o){
10326         var el = this.getFxEl();
10327         o = o || {};
10328
10329         el.queueFx(o, function(){
10330             this.clearOpacity();
10331             this.clip();
10332
10333             // restore values after effect
10334             var r = this.getFxRestore();
10335             var st = this.dom.style;
10336
10337             var after = function(){
10338                 if(o.useDisplay){
10339                     el.setDisplayed(false);
10340                 }else{
10341                     el.hide();
10342                 }
10343
10344                 el.clearOpacity();
10345                 el.setPositioning(r.pos);
10346                 st.width = r.width;
10347                 st.height = r.height;
10348
10349                 el.afterFx(o);
10350             };
10351
10352             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10353                 this.clearOpacity();
10354                 (function(){
10355                     this.fxanim({
10356                         height:{to:1},
10357                         points:{by:[0, this.getHeight() * .5]}
10358                     }, o, 'motion', 0.3, 'easeIn', after);
10359                 }).defer(100, this);
10360             });
10361         });
10362         return this;
10363     },
10364
10365     /**
10366      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10367      * changed using the "attr" config option) and then fading back to the original color. If no original
10368      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10369      * Usage:
10370 <pre><code>
10371 // default: highlight background to yellow
10372 el.highlight();
10373
10374 // custom: highlight foreground text to blue for 2 seconds
10375 el.highlight("0000ff", { attr: 'color', duration: 2 });
10376
10377 // common config options shown with default values
10378 el.highlight("ffff9c", {
10379     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10380     endColor: (current color) or "ffffff",
10381     easing: 'easeIn',
10382     duration: 1
10383 });
10384 </code></pre>
10385      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10386      * @param {Object} options (optional) Object literal with any of the Fx config options
10387      * @return {Roo.Element} The Element
10388      */ 
10389     highlight : function(color, o){
10390         var el = this.getFxEl();
10391         o = o || {};
10392
10393         el.queueFx(o, function(){
10394             color = color || "ffff9c";
10395             attr = o.attr || "backgroundColor";
10396
10397             this.clearOpacity();
10398             this.show();
10399
10400             var origColor = this.getColor(attr);
10401             var restoreColor = this.dom.style[attr];
10402             endColor = (o.endColor || origColor) || "ffffff";
10403
10404             var after = function(){
10405                 el.dom.style[attr] = restoreColor;
10406                 el.afterFx(o);
10407             };
10408
10409             var a = {};
10410             a[attr] = {from: color, to: endColor};
10411             arguments.callee.anim = this.fxanim(a,
10412                 o,
10413                 'color',
10414                 1,
10415                 'easeIn', after);
10416         });
10417         return this;
10418     },
10419
10420    /**
10421     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10422     * Usage:
10423 <pre><code>
10424 // default: a single light blue ripple
10425 el.frame();
10426
10427 // custom: 3 red ripples lasting 3 seconds total
10428 el.frame("ff0000", 3, { duration: 3 });
10429
10430 // common config options shown with default values
10431 el.frame("C3DAF9", 1, {
10432     duration: 1 //duration of entire animation (not each individual ripple)
10433     // Note: Easing is not configurable and will be ignored if included
10434 });
10435 </code></pre>
10436     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10437     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10438     * @param {Object} options (optional) Object literal with any of the Fx config options
10439     * @return {Roo.Element} The Element
10440     */
10441     frame : function(color, count, o){
10442         var el = this.getFxEl();
10443         o = o || {};
10444
10445         el.queueFx(o, function(){
10446             color = color || "#C3DAF9";
10447             if(color.length == 6){
10448                 color = "#" + color;
10449             }
10450             count = count || 1;
10451             duration = o.duration || 1;
10452             this.show();
10453
10454             var b = this.getBox();
10455             var animFn = function(){
10456                 var proxy = this.createProxy({
10457
10458                      style:{
10459                         visbility:"hidden",
10460                         position:"absolute",
10461                         "z-index":"35000", // yee haw
10462                         border:"0px solid " + color
10463                      }
10464                   });
10465                 var scale = Roo.isBorderBox ? 2 : 1;
10466                 proxy.animate({
10467                     top:{from:b.y, to:b.y - 20},
10468                     left:{from:b.x, to:b.x - 20},
10469                     borderWidth:{from:0, to:10},
10470                     opacity:{from:1, to:0},
10471                     height:{from:b.height, to:(b.height + (20*scale))},
10472                     width:{from:b.width, to:(b.width + (20*scale))}
10473                 }, duration, function(){
10474                     proxy.remove();
10475                 });
10476                 if(--count > 0){
10477                      animFn.defer((duration/2)*1000, this);
10478                 }else{
10479                     el.afterFx(o);
10480                 }
10481             };
10482             animFn.call(this);
10483         });
10484         return this;
10485     },
10486
10487    /**
10488     * Creates a pause before any subsequent queued effects begin.  If there are
10489     * no effects queued after the pause it will have no effect.
10490     * Usage:
10491 <pre><code>
10492 el.pause(1);
10493 </code></pre>
10494     * @param {Number} seconds The length of time to pause (in seconds)
10495     * @return {Roo.Element} The Element
10496     */
10497     pause : function(seconds){
10498         var el = this.getFxEl();
10499         var o = {};
10500
10501         el.queueFx(o, function(){
10502             setTimeout(function(){
10503                 el.afterFx(o);
10504             }, seconds * 1000);
10505         });
10506         return this;
10507     },
10508
10509    /**
10510     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10511     * using the "endOpacity" config option.
10512     * Usage:
10513 <pre><code>
10514 // default: fade in from opacity 0 to 100%
10515 el.fadeIn();
10516
10517 // custom: fade in from opacity 0 to 75% over 2 seconds
10518 el.fadeIn({ endOpacity: .75, duration: 2});
10519
10520 // common config options shown with default values
10521 el.fadeIn({
10522     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10523     easing: 'easeOut',
10524     duration: .5
10525 });
10526 </code></pre>
10527     * @param {Object} options (optional) Object literal with any of the Fx config options
10528     * @return {Roo.Element} The Element
10529     */
10530     fadeIn : function(o){
10531         var el = this.getFxEl();
10532         o = o || {};
10533         el.queueFx(o, function(){
10534             this.setOpacity(0);
10535             this.fixDisplay();
10536             this.dom.style.visibility = 'visible';
10537             var to = o.endOpacity || 1;
10538             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10539                 o, null, .5, "easeOut", function(){
10540                 if(to == 1){
10541                     this.clearOpacity();
10542                 }
10543                 el.afterFx(o);
10544             });
10545         });
10546         return this;
10547     },
10548
10549    /**
10550     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10551     * using the "endOpacity" config option.
10552     * Usage:
10553 <pre><code>
10554 // default: fade out from the element's current opacity to 0
10555 el.fadeOut();
10556
10557 // custom: fade out from the element's current opacity to 25% over 2 seconds
10558 el.fadeOut({ endOpacity: .25, duration: 2});
10559
10560 // common config options shown with default values
10561 el.fadeOut({
10562     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10563     easing: 'easeOut',
10564     duration: .5
10565     remove: false,
10566     useDisplay: false
10567 });
10568 </code></pre>
10569     * @param {Object} options (optional) Object literal with any of the Fx config options
10570     * @return {Roo.Element} The Element
10571     */
10572     fadeOut : function(o){
10573         var el = this.getFxEl();
10574         o = o || {};
10575         el.queueFx(o, function(){
10576             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10577                 o, null, .5, "easeOut", function(){
10578                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10579                      this.dom.style.display = "none";
10580                 }else{
10581                      this.dom.style.visibility = "hidden";
10582                 }
10583                 this.clearOpacity();
10584                 el.afterFx(o);
10585             });
10586         });
10587         return this;
10588     },
10589
10590    /**
10591     * Animates the transition of an element's dimensions from a starting height/width
10592     * to an ending height/width.
10593     * Usage:
10594 <pre><code>
10595 // change height and width to 100x100 pixels
10596 el.scale(100, 100);
10597
10598 // common config options shown with default values.  The height and width will default to
10599 // the element's existing values if passed as null.
10600 el.scale(
10601     [element's width],
10602     [element's height], {
10603     easing: 'easeOut',
10604     duration: .35
10605 });
10606 </code></pre>
10607     * @param {Number} width  The new width (pass undefined to keep the original width)
10608     * @param {Number} height  The new height (pass undefined to keep the original height)
10609     * @param {Object} options (optional) Object literal with any of the Fx config options
10610     * @return {Roo.Element} The Element
10611     */
10612     scale : function(w, h, o){
10613         this.shift(Roo.apply({}, o, {
10614             width: w,
10615             height: h
10616         }));
10617         return this;
10618     },
10619
10620    /**
10621     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10622     * Any of these properties not specified in the config object will not be changed.  This effect 
10623     * requires that at least one new dimension, position or opacity setting must be passed in on
10624     * the config object in order for the function to have any effect.
10625     * Usage:
10626 <pre><code>
10627 // slide the element horizontally to x position 200 while changing the height and opacity
10628 el.shift({ x: 200, height: 50, opacity: .8 });
10629
10630 // common config options shown with default values.
10631 el.shift({
10632     width: [element's width],
10633     height: [element's height],
10634     x: [element's x position],
10635     y: [element's y position],
10636     opacity: [element's opacity],
10637     easing: 'easeOut',
10638     duration: .35
10639 });
10640 </code></pre>
10641     * @param {Object} options  Object literal with any of the Fx config options
10642     * @return {Roo.Element} The Element
10643     */
10644     shift : function(o){
10645         var el = this.getFxEl();
10646         o = o || {};
10647         el.queueFx(o, function(){
10648             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10649             if(w !== undefined){
10650                 a.width = {to: this.adjustWidth(w)};
10651             }
10652             if(h !== undefined){
10653                 a.height = {to: this.adjustHeight(h)};
10654             }
10655             if(x !== undefined || y !== undefined){
10656                 a.points = {to: [
10657                     x !== undefined ? x : this.getX(),
10658                     y !== undefined ? y : this.getY()
10659                 ]};
10660             }
10661             if(op !== undefined){
10662                 a.opacity = {to: op};
10663             }
10664             if(o.xy !== undefined){
10665                 a.points = {to: o.xy};
10666             }
10667             arguments.callee.anim = this.fxanim(a,
10668                 o, 'motion', .35, "easeOut", function(){
10669                 el.afterFx(o);
10670             });
10671         });
10672         return this;
10673     },
10674
10675         /**
10676          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10677          * ending point of the effect.
10678          * Usage:
10679          *<pre><code>
10680 // default: slide the element downward while fading out
10681 el.ghost();
10682
10683 // custom: slide the element out to the right with a 2-second duration
10684 el.ghost('r', { duration: 2 });
10685
10686 // common config options shown with default values
10687 el.ghost('b', {
10688     easing: 'easeOut',
10689     duration: .5
10690     remove: false,
10691     useDisplay: false
10692 });
10693 </code></pre>
10694          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10695          * @param {Object} options (optional) Object literal with any of the Fx config options
10696          * @return {Roo.Element} The Element
10697          */
10698     ghost : function(anchor, o){
10699         var el = this.getFxEl();
10700         o = o || {};
10701
10702         el.queueFx(o, function(){
10703             anchor = anchor || "b";
10704
10705             // restore values after effect
10706             var r = this.getFxRestore();
10707             var w = this.getWidth(),
10708                 h = this.getHeight();
10709
10710             var st = this.dom.style;
10711
10712             var after = function(){
10713                 if(o.useDisplay){
10714                     el.setDisplayed(false);
10715                 }else{
10716                     el.hide();
10717                 }
10718
10719                 el.clearOpacity();
10720                 el.setPositioning(r.pos);
10721                 st.width = r.width;
10722                 st.height = r.height;
10723
10724                 el.afterFx(o);
10725             };
10726
10727             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10728             switch(anchor.toLowerCase()){
10729                 case "t":
10730                     pt.by = [0, -h];
10731                 break;
10732                 case "l":
10733                     pt.by = [-w, 0];
10734                 break;
10735                 case "r":
10736                     pt.by = [w, 0];
10737                 break;
10738                 case "b":
10739                     pt.by = [0, h];
10740                 break;
10741                 case "tl":
10742                     pt.by = [-w, -h];
10743                 break;
10744                 case "bl":
10745                     pt.by = [-w, h];
10746                 break;
10747                 case "br":
10748                     pt.by = [w, h];
10749                 break;
10750                 case "tr":
10751                     pt.by = [w, -h];
10752                 break;
10753             }
10754
10755             arguments.callee.anim = this.fxanim(a,
10756                 o,
10757                 'motion',
10758                 .5,
10759                 "easeOut", after);
10760         });
10761         return this;
10762     },
10763
10764         /**
10765          * Ensures that all effects queued after syncFx is called on the element are
10766          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10767          * @return {Roo.Element} The Element
10768          */
10769     syncFx : function(){
10770         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10771             block : false,
10772             concurrent : true,
10773             stopFx : false
10774         });
10775         return this;
10776     },
10777
10778         /**
10779          * Ensures that all effects queued after sequenceFx is called on the element are
10780          * run in sequence.  This is the opposite of {@link #syncFx}.
10781          * @return {Roo.Element} The Element
10782          */
10783     sequenceFx : function(){
10784         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10785             block : false,
10786             concurrent : false,
10787             stopFx : false
10788         });
10789         return this;
10790     },
10791
10792         /* @private */
10793     nextFx : function(){
10794         var ef = this.fxQueue[0];
10795         if(ef){
10796             ef.call(this);
10797         }
10798     },
10799
10800         /**
10801          * Returns true if the element has any effects actively running or queued, else returns false.
10802          * @return {Boolean} True if element has active effects, else false
10803          */
10804     hasActiveFx : function(){
10805         return this.fxQueue && this.fxQueue[0];
10806     },
10807
10808         /**
10809          * Stops any running effects and clears the element's internal effects queue if it contains
10810          * any additional effects that haven't started yet.
10811          * @return {Roo.Element} The Element
10812          */
10813     stopFx : function(){
10814         if(this.hasActiveFx()){
10815             var cur = this.fxQueue[0];
10816             if(cur && cur.anim && cur.anim.isAnimated()){
10817                 this.fxQueue = [cur]; // clear out others
10818                 cur.anim.stop(true);
10819             }
10820         }
10821         return this;
10822     },
10823
10824         /* @private */
10825     beforeFx : function(o){
10826         if(this.hasActiveFx() && !o.concurrent){
10827            if(o.stopFx){
10828                this.stopFx();
10829                return true;
10830            }
10831            return false;
10832         }
10833         return true;
10834     },
10835
10836         /**
10837          * Returns true if the element is currently blocking so that no other effect can be queued
10838          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10839          * used to ensure that an effect initiated by a user action runs to completion prior to the
10840          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10841          * @return {Boolean} True if blocking, else false
10842          */
10843     hasFxBlock : function(){
10844         var q = this.fxQueue;
10845         return q && q[0] && q[0].block;
10846     },
10847
10848         /* @private */
10849     queueFx : function(o, fn){
10850         if(!this.fxQueue){
10851             this.fxQueue = [];
10852         }
10853         if(!this.hasFxBlock()){
10854             Roo.applyIf(o, this.fxDefaults);
10855             if(!o.concurrent){
10856                 var run = this.beforeFx(o);
10857                 fn.block = o.block;
10858                 this.fxQueue.push(fn);
10859                 if(run){
10860                     this.nextFx();
10861                 }
10862             }else{
10863                 fn.call(this);
10864             }
10865         }
10866         return this;
10867     },
10868
10869         /* @private */
10870     fxWrap : function(pos, o, vis){
10871         var wrap;
10872         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10873             var wrapXY;
10874             if(o.fixPosition){
10875                 wrapXY = this.getXY();
10876             }
10877             var div = document.createElement("div");
10878             div.style.visibility = vis;
10879             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10880             wrap.setPositioning(pos);
10881             if(wrap.getStyle("position") == "static"){
10882                 wrap.position("relative");
10883             }
10884             this.clearPositioning('auto');
10885             wrap.clip();
10886             wrap.dom.appendChild(this.dom);
10887             if(wrapXY){
10888                 wrap.setXY(wrapXY);
10889             }
10890         }
10891         return wrap;
10892     },
10893
10894         /* @private */
10895     fxUnwrap : function(wrap, pos, o){
10896         this.clearPositioning();
10897         this.setPositioning(pos);
10898         if(!o.wrap){
10899             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10900             wrap.remove();
10901         }
10902     },
10903
10904         /* @private */
10905     getFxRestore : function(){
10906         var st = this.dom.style;
10907         return {pos: this.getPositioning(), width: st.width, height : st.height};
10908     },
10909
10910         /* @private */
10911     afterFx : function(o){
10912         if(o.afterStyle){
10913             this.applyStyles(o.afterStyle);
10914         }
10915         if(o.afterCls){
10916             this.addClass(o.afterCls);
10917         }
10918         if(o.remove === true){
10919             this.remove();
10920         }
10921         Roo.callback(o.callback, o.scope, [this]);
10922         if(!o.concurrent){
10923             this.fxQueue.shift();
10924             this.nextFx();
10925         }
10926     },
10927
10928         /* @private */
10929     getFxEl : function(){ // support for composite element fx
10930         return Roo.get(this.dom);
10931     },
10932
10933         /* @private */
10934     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10935         animType = animType || 'run';
10936         opt = opt || {};
10937         var anim = Roo.lib.Anim[animType](
10938             this.dom, args,
10939             (opt.duration || defaultDur) || .35,
10940             (opt.easing || defaultEase) || 'easeOut',
10941             function(){
10942                 Roo.callback(cb, this);
10943             },
10944             this
10945         );
10946         opt.anim = anim;
10947         return anim;
10948     }
10949 };
10950
10951 // backwords compat
10952 Roo.Fx.resize = Roo.Fx.scale;
10953
10954 //When included, Roo.Fx is automatically applied to Element so that all basic
10955 //effects are available directly via the Element API
10956 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10957  * Based on:
10958  * Ext JS Library 1.1.1
10959  * Copyright(c) 2006-2007, Ext JS, LLC.
10960  *
10961  * Originally Released Under LGPL - original licence link has changed is not relivant.
10962  *
10963  * Fork - LGPL
10964  * <script type="text/javascript">
10965  */
10966
10967
10968 /**
10969  * @class Roo.CompositeElement
10970  * Standard composite class. Creates a Roo.Element for every element in the collection.
10971  * <br><br>
10972  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10973  * actions will be performed on all the elements in this collection.</b>
10974  * <br><br>
10975  * All methods return <i>this</i> and can be chained.
10976  <pre><code>
10977  var els = Roo.select("#some-el div.some-class", true);
10978  // or select directly from an existing element
10979  var el = Roo.get('some-el');
10980  el.select('div.some-class', true);
10981
10982  els.setWidth(100); // all elements become 100 width
10983  els.hide(true); // all elements fade out and hide
10984  // or
10985  els.setWidth(100).hide(true);
10986  </code></pre>
10987  */
10988 Roo.CompositeElement = function(els){
10989     this.elements = [];
10990     this.addElements(els);
10991 };
10992 Roo.CompositeElement.prototype = {
10993     isComposite: true,
10994     addElements : function(els){
10995         if(!els) return this;
10996         if(typeof els == "string"){
10997             els = Roo.Element.selectorFunction(els);
10998         }
10999         var yels = this.elements;
11000         var index = yels.length-1;
11001         for(var i = 0, len = els.length; i < len; i++) {
11002                 yels[++index] = Roo.get(els[i]);
11003         }
11004         return this;
11005     },
11006
11007     /**
11008     * Clears this composite and adds the elements returned by the passed selector.
11009     * @param {String/Array} els A string CSS selector, an array of elements or an element
11010     * @return {CompositeElement} this
11011     */
11012     fill : function(els){
11013         this.elements = [];
11014         this.add(els);
11015         return this;
11016     },
11017
11018     /**
11019     * Filters this composite to only elements that match the passed selector.
11020     * @param {String} selector A string CSS selector
11021     * @param {Boolean} inverse return inverse filter (not matches)
11022     * @return {CompositeElement} this
11023     */
11024     filter : function(selector, inverse){
11025         var els = [];
11026         inverse = inverse || false;
11027         this.each(function(el){
11028             var match = inverse ? !el.is(selector) : el.is(selector);
11029             if(match){
11030                 els[els.length] = el.dom;
11031             }
11032         });
11033         this.fill(els);
11034         return this;
11035     },
11036
11037     invoke : function(fn, args){
11038         var els = this.elements;
11039         for(var i = 0, len = els.length; i < len; i++) {
11040                 Roo.Element.prototype[fn].apply(els[i], args);
11041         }
11042         return this;
11043     },
11044     /**
11045     * Adds elements to this composite.
11046     * @param {String/Array} els A string CSS selector, an array of elements or an element
11047     * @return {CompositeElement} this
11048     */
11049     add : function(els){
11050         if(typeof els == "string"){
11051             this.addElements(Roo.Element.selectorFunction(els));
11052         }else if(els.length !== undefined){
11053             this.addElements(els);
11054         }else{
11055             this.addElements([els]);
11056         }
11057         return this;
11058     },
11059     /**
11060     * Calls the passed function passing (el, this, index) for each element in this composite.
11061     * @param {Function} fn The function to call
11062     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11063     * @return {CompositeElement} this
11064     */
11065     each : function(fn, scope){
11066         var els = this.elements;
11067         for(var i = 0, len = els.length; i < len; i++){
11068             if(fn.call(scope || els[i], els[i], this, i) === false) {
11069                 break;
11070             }
11071         }
11072         return this;
11073     },
11074
11075     /**
11076      * Returns the Element object at the specified index
11077      * @param {Number} index
11078      * @return {Roo.Element}
11079      */
11080     item : function(index){
11081         return this.elements[index] || null;
11082     },
11083
11084     /**
11085      * Returns the first Element
11086      * @return {Roo.Element}
11087      */
11088     first : function(){
11089         return this.item(0);
11090     },
11091
11092     /**
11093      * Returns the last Element
11094      * @return {Roo.Element}
11095      */
11096     last : function(){
11097         return this.item(this.elements.length-1);
11098     },
11099
11100     /**
11101      * Returns the number of elements in this composite
11102      * @return Number
11103      */
11104     getCount : function(){
11105         return this.elements.length;
11106     },
11107
11108     /**
11109      * Returns true if this composite contains the passed element
11110      * @return Boolean
11111      */
11112     contains : function(el){
11113         return this.indexOf(el) !== -1;
11114     },
11115
11116     /**
11117      * Returns true if this composite contains the passed element
11118      * @return Boolean
11119      */
11120     indexOf : function(el){
11121         return this.elements.indexOf(Roo.get(el));
11122     },
11123
11124
11125     /**
11126     * Removes the specified element(s).
11127     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11128     * or an array of any of those.
11129     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11130     * @return {CompositeElement} this
11131     */
11132     removeElement : function(el, removeDom){
11133         if(el instanceof Array){
11134             for(var i = 0, len = el.length; i < len; i++){
11135                 this.removeElement(el[i]);
11136             }
11137             return this;
11138         }
11139         var index = typeof el == 'number' ? el : this.indexOf(el);
11140         if(index !== -1){
11141             if(removeDom){
11142                 var d = this.elements[index];
11143                 if(d.dom){
11144                     d.remove();
11145                 }else{
11146                     d.parentNode.removeChild(d);
11147                 }
11148             }
11149             this.elements.splice(index, 1);
11150         }
11151         return this;
11152     },
11153
11154     /**
11155     * Replaces the specified element with the passed element.
11156     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11157     * to replace.
11158     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11159     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11160     * @return {CompositeElement} this
11161     */
11162     replaceElement : function(el, replacement, domReplace){
11163         var index = typeof el == 'number' ? el : this.indexOf(el);
11164         if(index !== -1){
11165             if(domReplace){
11166                 this.elements[index].replaceWith(replacement);
11167             }else{
11168                 this.elements.splice(index, 1, Roo.get(replacement))
11169             }
11170         }
11171         return this;
11172     },
11173
11174     /**
11175      * Removes all elements.
11176      */
11177     clear : function(){
11178         this.elements = [];
11179     }
11180 };
11181 (function(){
11182     Roo.CompositeElement.createCall = function(proto, fnName){
11183         if(!proto[fnName]){
11184             proto[fnName] = function(){
11185                 return this.invoke(fnName, arguments);
11186             };
11187         }
11188     };
11189     for(var fnName in Roo.Element.prototype){
11190         if(typeof Roo.Element.prototype[fnName] == "function"){
11191             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11192         }
11193     };
11194 })();
11195 /*
11196  * Based on:
11197  * Ext JS Library 1.1.1
11198  * Copyright(c) 2006-2007, Ext JS, LLC.
11199  *
11200  * Originally Released Under LGPL - original licence link has changed is not relivant.
11201  *
11202  * Fork - LGPL
11203  * <script type="text/javascript">
11204  */
11205
11206 /**
11207  * @class Roo.CompositeElementLite
11208  * @extends Roo.CompositeElement
11209  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11210  <pre><code>
11211  var els = Roo.select("#some-el div.some-class");
11212  // or select directly from an existing element
11213  var el = Roo.get('some-el');
11214  el.select('div.some-class');
11215
11216  els.setWidth(100); // all elements become 100 width
11217  els.hide(true); // all elements fade out and hide
11218  // or
11219  els.setWidth(100).hide(true);
11220  </code></pre><br><br>
11221  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11222  * actions will be performed on all the elements in this collection.</b>
11223  */
11224 Roo.CompositeElementLite = function(els){
11225     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11226     this.el = new Roo.Element.Flyweight();
11227 };
11228 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11229     addElements : function(els){
11230         if(els){
11231             if(els instanceof Array){
11232                 this.elements = this.elements.concat(els);
11233             }else{
11234                 var yels = this.elements;
11235                 var index = yels.length-1;
11236                 for(var i = 0, len = els.length; i < len; i++) {
11237                     yels[++index] = els[i];
11238                 }
11239             }
11240         }
11241         return this;
11242     },
11243     invoke : function(fn, args){
11244         var els = this.elements;
11245         var el = this.el;
11246         for(var i = 0, len = els.length; i < len; i++) {
11247             el.dom = els[i];
11248                 Roo.Element.prototype[fn].apply(el, args);
11249         }
11250         return this;
11251     },
11252     /**
11253      * Returns a flyweight Element of the dom element object at the specified index
11254      * @param {Number} index
11255      * @return {Roo.Element}
11256      */
11257     item : function(index){
11258         if(!this.elements[index]){
11259             return null;
11260         }
11261         this.el.dom = this.elements[index];
11262         return this.el;
11263     },
11264
11265     // fixes scope with flyweight
11266     addListener : function(eventName, handler, scope, opt){
11267         var els = this.elements;
11268         for(var i = 0, len = els.length; i < len; i++) {
11269             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11270         }
11271         return this;
11272     },
11273
11274     /**
11275     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11276     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11277     * a reference to the dom node, use el.dom.</b>
11278     * @param {Function} fn The function to call
11279     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11280     * @return {CompositeElement} this
11281     */
11282     each : function(fn, scope){
11283         var els = this.elements;
11284         var el = this.el;
11285         for(var i = 0, len = els.length; i < len; i++){
11286             el.dom = els[i];
11287                 if(fn.call(scope || el, el, this, i) === false){
11288                 break;
11289             }
11290         }
11291         return this;
11292     },
11293
11294     indexOf : function(el){
11295         return this.elements.indexOf(Roo.getDom(el));
11296     },
11297
11298     replaceElement : function(el, replacement, domReplace){
11299         var index = typeof el == 'number' ? el : this.indexOf(el);
11300         if(index !== -1){
11301             replacement = Roo.getDom(replacement);
11302             if(domReplace){
11303                 var d = this.elements[index];
11304                 d.parentNode.insertBefore(replacement, d);
11305                 d.parentNode.removeChild(d);
11306             }
11307             this.elements.splice(index, 1, replacement);
11308         }
11309         return this;
11310     }
11311 });
11312 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11313
11314 /*
11315  * Based on:
11316  * Ext JS Library 1.1.1
11317  * Copyright(c) 2006-2007, Ext JS, LLC.
11318  *
11319  * Originally Released Under LGPL - original licence link has changed is not relivant.
11320  *
11321  * Fork - LGPL
11322  * <script type="text/javascript">
11323  */
11324
11325  
11326
11327 /**
11328  * @class Roo.data.Connection
11329  * @extends Roo.util.Observable
11330  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11331  * either to a configured URL, or to a URL specified at request time.<br><br>
11332  * <p>
11333  * Requests made by this class are asynchronous, and will return immediately. No data from
11334  * the server will be available to the statement immediately following the {@link #request} call.
11335  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11336  * <p>
11337  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11338  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11339  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11340  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11341  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11342  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11343  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11344  * standard DOM methods.
11345  * @constructor
11346  * @param {Object} config a configuration object.
11347  */
11348 Roo.data.Connection = function(config){
11349     Roo.apply(this, config);
11350     this.addEvents({
11351         /**
11352          * @event beforerequest
11353          * Fires before a network request is made to retrieve a data object.
11354          * @param {Connection} conn This Connection object.
11355          * @param {Object} options The options config object passed to the {@link #request} method.
11356          */
11357         "beforerequest" : true,
11358         /**
11359          * @event requestcomplete
11360          * Fires if the request was successfully completed.
11361          * @param {Connection} conn This Connection object.
11362          * @param {Object} response The XHR object containing the response data.
11363          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11364          * @param {Object} options The options config object passed to the {@link #request} method.
11365          */
11366         "requestcomplete" : true,
11367         /**
11368          * @event requestexception
11369          * Fires if an error HTTP status was returned from the server.
11370          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11371          * @param {Connection} conn This Connection object.
11372          * @param {Object} response The XHR object containing the response data.
11373          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11374          * @param {Object} options The options config object passed to the {@link #request} method.
11375          */
11376         "requestexception" : true
11377     });
11378     Roo.data.Connection.superclass.constructor.call(this);
11379 };
11380
11381 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11382     /**
11383      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11384      */
11385     /**
11386      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11387      * extra parameters to each request made by this object. (defaults to undefined)
11388      */
11389     /**
11390      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11391      *  to each request made by this object. (defaults to undefined)
11392      */
11393     /**
11394      * @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)
11395      */
11396     /**
11397      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11398      */
11399     timeout : 30000,
11400     /**
11401      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11402      * @type Boolean
11403      */
11404     autoAbort:false,
11405
11406     /**
11407      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11408      * @type Boolean
11409      */
11410     disableCaching: true,
11411
11412     /**
11413      * Sends an HTTP request to a remote server.
11414      * @param {Object} options An object which may contain the following properties:<ul>
11415      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11416      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11417      * request, a url encoded string or a function to call to get either.</li>
11418      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11419      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11420      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11421      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11422      * <li>options {Object} The parameter to the request call.</li>
11423      * <li>success {Boolean} True if the request succeeded.</li>
11424      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11425      * </ul></li>
11426      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11427      * The callback is passed the following parameters:<ul>
11428      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11429      * <li>options {Object} The parameter to the request call.</li>
11430      * </ul></li>
11431      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11432      * The callback is passed the following parameters:<ul>
11433      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11434      * <li>options {Object} The parameter to the request call.</li>
11435      * </ul></li>
11436      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11437      * for the callback function. Defaults to the browser window.</li>
11438      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11439      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11440      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11441      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11442      * params for the post data. Any params will be appended to the URL.</li>
11443      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11444      * </ul>
11445      * @return {Number} transactionId
11446      */
11447     request : function(o){
11448         if(this.fireEvent("beforerequest", this, o) !== false){
11449             var p = o.params;
11450
11451             if(typeof p == "function"){
11452                 p = p.call(o.scope||window, o);
11453             }
11454             if(typeof p == "object"){
11455                 p = Roo.urlEncode(o.params);
11456             }
11457             if(this.extraParams){
11458                 var extras = Roo.urlEncode(this.extraParams);
11459                 p = p ? (p + '&' + extras) : extras;
11460             }
11461
11462             var url = o.url || this.url;
11463             if(typeof url == 'function'){
11464                 url = url.call(o.scope||window, o);
11465             }
11466
11467             if(o.form){
11468                 var form = Roo.getDom(o.form);
11469                 url = url || form.action;
11470
11471                 var enctype = form.getAttribute("enctype");
11472                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11473                     return this.doFormUpload(o, p, url);
11474                 }
11475                 var f = Roo.lib.Ajax.serializeForm(form);
11476                 p = p ? (p + '&' + f) : f;
11477             }
11478
11479             var hs = o.headers;
11480             if(this.defaultHeaders){
11481                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11482                 if(!o.headers){
11483                     o.headers = hs;
11484                 }
11485             }
11486
11487             var cb = {
11488                 success: this.handleResponse,
11489                 failure: this.handleFailure,
11490                 scope: this,
11491                 argument: {options: o},
11492                 timeout : o.timeout || this.timeout
11493             };
11494
11495             var method = o.method||this.method||(p ? "POST" : "GET");
11496
11497             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11498                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11499             }
11500
11501             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11502                 if(o.autoAbort){
11503                     this.abort();
11504                 }
11505             }else if(this.autoAbort !== false){
11506                 this.abort();
11507             }
11508
11509             if((method == 'GET' && p) || o.xmlData){
11510                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11511                 p = '';
11512             }
11513             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11514             return this.transId;
11515         }else{
11516             Roo.callback(o.callback, o.scope, [o, null, null]);
11517             return null;
11518         }
11519     },
11520
11521     /**
11522      * Determine whether this object has a request outstanding.
11523      * @param {Number} transactionId (Optional) defaults to the last transaction
11524      * @return {Boolean} True if there is an outstanding request.
11525      */
11526     isLoading : function(transId){
11527         if(transId){
11528             return Roo.lib.Ajax.isCallInProgress(transId);
11529         }else{
11530             return this.transId ? true : false;
11531         }
11532     },
11533
11534     /**
11535      * Aborts any outstanding request.
11536      * @param {Number} transactionId (Optional) defaults to the last transaction
11537      */
11538     abort : function(transId){
11539         if(transId || this.isLoading()){
11540             Roo.lib.Ajax.abort(transId || this.transId);
11541         }
11542     },
11543
11544     // private
11545     handleResponse : function(response){
11546         this.transId = false;
11547         var options = response.argument.options;
11548         response.argument = options ? options.argument : null;
11549         this.fireEvent("requestcomplete", this, response, options);
11550         Roo.callback(options.success, options.scope, [response, options]);
11551         Roo.callback(options.callback, options.scope, [options, true, response]);
11552     },
11553
11554     // private
11555     handleFailure : function(response, e){
11556         this.transId = false;
11557         var options = response.argument.options;
11558         response.argument = options ? options.argument : null;
11559         this.fireEvent("requestexception", this, response, options, e);
11560         Roo.callback(options.failure, options.scope, [response, options]);
11561         Roo.callback(options.callback, options.scope, [options, false, response]);
11562     },
11563
11564     // private
11565     doFormUpload : function(o, ps, url){
11566         var id = Roo.id();
11567         var frame = document.createElement('iframe');
11568         frame.id = id;
11569         frame.name = id;
11570         frame.className = 'x-hidden';
11571         if(Roo.isIE){
11572             frame.src = Roo.SSL_SECURE_URL;
11573         }
11574         document.body.appendChild(frame);
11575
11576         if(Roo.isIE){
11577            document.frames[id].name = id;
11578         }
11579
11580         var form = Roo.getDom(o.form);
11581         form.target = id;
11582         form.method = 'POST';
11583         form.enctype = form.encoding = 'multipart/form-data';
11584         if(url){
11585             form.action = url;
11586         }
11587
11588         var hiddens, hd;
11589         if(ps){ // add dynamic params
11590             hiddens = [];
11591             ps = Roo.urlDecode(ps, false);
11592             for(var k in ps){
11593                 if(ps.hasOwnProperty(k)){
11594                     hd = document.createElement('input');
11595                     hd.type = 'hidden';
11596                     hd.name = k;
11597                     hd.value = ps[k];
11598                     form.appendChild(hd);
11599                     hiddens.push(hd);
11600                 }
11601             }
11602         }
11603
11604         function cb(){
11605             var r = {  // bogus response object
11606                 responseText : '',
11607                 responseXML : null
11608             };
11609
11610             r.argument = o ? o.argument : null;
11611
11612             try { //
11613                 var doc;
11614                 if(Roo.isIE){
11615                     doc = frame.contentWindow.document;
11616                 }else {
11617                     doc = (frame.contentDocument || window.frames[id].document);
11618                 }
11619                 if(doc && doc.body){
11620                     r.responseText = doc.body.innerHTML;
11621                 }
11622                 if(doc && doc.XMLDocument){
11623                     r.responseXML = doc.XMLDocument;
11624                 }else {
11625                     r.responseXML = doc;
11626                 }
11627             }
11628             catch(e) {
11629                 // ignore
11630             }
11631
11632             Roo.EventManager.removeListener(frame, 'load', cb, this);
11633
11634             this.fireEvent("requestcomplete", this, r, o);
11635             Roo.callback(o.success, o.scope, [r, o]);
11636             Roo.callback(o.callback, o.scope, [o, true, r]);
11637
11638             setTimeout(function(){document.body.removeChild(frame);}, 100);
11639         }
11640
11641         Roo.EventManager.on(frame, 'load', cb, this);
11642         form.submit();
11643
11644         if(hiddens){ // remove dynamic params
11645             for(var i = 0, len = hiddens.length; i < len; i++){
11646                 form.removeChild(hiddens[i]);
11647             }
11648         }
11649     }
11650 });
11651 /*
11652  * Based on:
11653  * Ext JS Library 1.1.1
11654  * Copyright(c) 2006-2007, Ext JS, LLC.
11655  *
11656  * Originally Released Under LGPL - original licence link has changed is not relivant.
11657  *
11658  * Fork - LGPL
11659  * <script type="text/javascript">
11660  */
11661  
11662 /**
11663  * Global Ajax request class.
11664  * 
11665  * @class Roo.Ajax
11666  * @extends Roo.data.Connection
11667  * @static
11668  * 
11669  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11670  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11671  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11672  * @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)
11673  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11674  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11675  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11676  */
11677 Roo.Ajax = new Roo.data.Connection({
11678     // fix up the docs
11679     /**
11680      * @scope Roo.Ajax
11681      * @type {Boolear} 
11682      */
11683     autoAbort : false,
11684
11685     /**
11686      * Serialize the passed form into a url encoded string
11687      * @scope Roo.Ajax
11688      * @param {String/HTMLElement} form
11689      * @return {String}
11690      */
11691     serializeForm : function(form){
11692         return Roo.lib.Ajax.serializeForm(form);
11693     }
11694 });/*
11695  * Based on:
11696  * Ext JS Library 1.1.1
11697  * Copyright(c) 2006-2007, Ext JS, LLC.
11698  *
11699  * Originally Released Under LGPL - original licence link has changed is not relivant.
11700  *
11701  * Fork - LGPL
11702  * <script type="text/javascript">
11703  */
11704
11705  
11706 /**
11707  * @class Roo.UpdateManager
11708  * @extends Roo.util.Observable
11709  * Provides AJAX-style update for Element object.<br><br>
11710  * Usage:<br>
11711  * <pre><code>
11712  * // Get it from a Roo.Element object
11713  * var el = Roo.get("foo");
11714  * var mgr = el.getUpdateManager();
11715  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11716  * ...
11717  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11718  * <br>
11719  * // or directly (returns the same UpdateManager instance)
11720  * var mgr = new Roo.UpdateManager("myElementId");
11721  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11722  * mgr.on("update", myFcnNeedsToKnow);
11723  * <br>
11724    // short handed call directly from the element object
11725    Roo.get("foo").load({
11726         url: "bar.php",
11727         scripts:true,
11728         params: "for=bar",
11729         text: "Loading Foo..."
11730    });
11731  * </code></pre>
11732  * @constructor
11733  * Create new UpdateManager directly.
11734  * @param {String/HTMLElement/Roo.Element} el The element to update
11735  * @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).
11736  */
11737 Roo.UpdateManager = function(el, forceNew){
11738     el = Roo.get(el);
11739     if(!forceNew && el.updateManager){
11740         return el.updateManager;
11741     }
11742     /**
11743      * The Element object
11744      * @type Roo.Element
11745      */
11746     this.el = el;
11747     /**
11748      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11749      * @type String
11750      */
11751     this.defaultUrl = null;
11752
11753     this.addEvents({
11754         /**
11755          * @event beforeupdate
11756          * Fired before an update is made, return false from your handler and the update is cancelled.
11757          * @param {Roo.Element} el
11758          * @param {String/Object/Function} url
11759          * @param {String/Object} params
11760          */
11761         "beforeupdate": true,
11762         /**
11763          * @event update
11764          * Fired after successful update is made.
11765          * @param {Roo.Element} el
11766          * @param {Object} oResponseObject The response Object
11767          */
11768         "update": true,
11769         /**
11770          * @event failure
11771          * Fired on update failure.
11772          * @param {Roo.Element} el
11773          * @param {Object} oResponseObject The response Object
11774          */
11775         "failure": true
11776     });
11777     var d = Roo.UpdateManager.defaults;
11778     /**
11779      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11780      * @type String
11781      */
11782     this.sslBlankUrl = d.sslBlankUrl;
11783     /**
11784      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11785      * @type Boolean
11786      */
11787     this.disableCaching = d.disableCaching;
11788     /**
11789      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11790      * @type String
11791      */
11792     this.indicatorText = d.indicatorText;
11793     /**
11794      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11795      * @type String
11796      */
11797     this.showLoadIndicator = d.showLoadIndicator;
11798     /**
11799      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11800      * @type Number
11801      */
11802     this.timeout = d.timeout;
11803
11804     /**
11805      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11806      * @type Boolean
11807      */
11808     this.loadScripts = d.loadScripts;
11809
11810     /**
11811      * Transaction object of current executing transaction
11812      */
11813     this.transaction = null;
11814
11815     /**
11816      * @private
11817      */
11818     this.autoRefreshProcId = null;
11819     /**
11820      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11821      * @type Function
11822      */
11823     this.refreshDelegate = this.refresh.createDelegate(this);
11824     /**
11825      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11826      * @type Function
11827      */
11828     this.updateDelegate = this.update.createDelegate(this);
11829     /**
11830      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11831      * @type Function
11832      */
11833     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11834     /**
11835      * @private
11836      */
11837     this.successDelegate = this.processSuccess.createDelegate(this);
11838     /**
11839      * @private
11840      */
11841     this.failureDelegate = this.processFailure.createDelegate(this);
11842
11843     if(!this.renderer){
11844      /**
11845       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11846       */
11847     this.renderer = new Roo.UpdateManager.BasicRenderer();
11848     }
11849     
11850     Roo.UpdateManager.superclass.constructor.call(this);
11851 };
11852
11853 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11854     /**
11855      * Get the Element this UpdateManager is bound to
11856      * @return {Roo.Element} The element
11857      */
11858     getEl : function(){
11859         return this.el;
11860     },
11861     /**
11862      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11863      * @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:
11864 <pre><code>
11865 um.update({<br/>
11866     url: "your-url.php",<br/>
11867     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11868     callback: yourFunction,<br/>
11869     scope: yourObject, //(optional scope)  <br/>
11870     discardUrl: false, <br/>
11871     nocache: false,<br/>
11872     text: "Loading...",<br/>
11873     timeout: 30,<br/>
11874     scripts: false<br/>
11875 });
11876 </code></pre>
11877      * The only required property is url. The optional properties nocache, text and scripts
11878      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11879      * @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}
11880      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11881      * @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.
11882      */
11883     update : function(url, params, callback, discardUrl){
11884         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11885             var method = this.method,
11886                 cfg;
11887             if(typeof url == "object"){ // must be config object
11888                 cfg = url;
11889                 url = cfg.url;
11890                 params = params || cfg.params;
11891                 callback = callback || cfg.callback;
11892                 discardUrl = discardUrl || cfg.discardUrl;
11893                 if(callback && cfg.scope){
11894                     callback = callback.createDelegate(cfg.scope);
11895                 }
11896                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11897                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11898                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11899                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11900                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11901             }
11902             this.showLoading();
11903             if(!discardUrl){
11904                 this.defaultUrl = url;
11905             }
11906             if(typeof url == "function"){
11907                 url = url.call(this);
11908             }
11909
11910             method = method || (params ? "POST" : "GET");
11911             if(method == "GET"){
11912                 url = this.prepareUrl(url);
11913             }
11914
11915             var o = Roo.apply(cfg ||{}, {
11916                 url : url,
11917                 params: params,
11918                 success: this.successDelegate,
11919                 failure: this.failureDelegate,
11920                 callback: undefined,
11921                 timeout: (this.timeout*1000),
11922                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11923             });
11924             Roo.log("updated manager called with timeout of " + o.timeout);
11925             this.transaction = Roo.Ajax.request(o);
11926         }
11927     },
11928
11929     /**
11930      * 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.
11931      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11932      * @param {String/HTMLElement} form The form Id or form element
11933      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11934      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11935      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11936      */
11937     formUpdate : function(form, url, reset, callback){
11938         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11939             if(typeof url == "function"){
11940                 url = url.call(this);
11941             }
11942             form = Roo.getDom(form);
11943             this.transaction = Roo.Ajax.request({
11944                 form: form,
11945                 url:url,
11946                 success: this.successDelegate,
11947                 failure: this.failureDelegate,
11948                 timeout: (this.timeout*1000),
11949                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11950             });
11951             this.showLoading.defer(1, this);
11952         }
11953     },
11954
11955     /**
11956      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11957      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11958      */
11959     refresh : function(callback){
11960         if(this.defaultUrl == null){
11961             return;
11962         }
11963         this.update(this.defaultUrl, null, callback, true);
11964     },
11965
11966     /**
11967      * Set this element to auto refresh.
11968      * @param {Number} interval How often to update (in seconds).
11969      * @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)
11970      * @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}
11971      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11972      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11973      */
11974     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11975         if(refreshNow){
11976             this.update(url || this.defaultUrl, params, callback, true);
11977         }
11978         if(this.autoRefreshProcId){
11979             clearInterval(this.autoRefreshProcId);
11980         }
11981         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11982     },
11983
11984     /**
11985      * Stop auto refresh on this element.
11986      */
11987      stopAutoRefresh : function(){
11988         if(this.autoRefreshProcId){
11989             clearInterval(this.autoRefreshProcId);
11990             delete this.autoRefreshProcId;
11991         }
11992     },
11993
11994     isAutoRefreshing : function(){
11995        return this.autoRefreshProcId ? true : false;
11996     },
11997     /**
11998      * Called to update the element to "Loading" state. Override to perform custom action.
11999      */
12000     showLoading : function(){
12001         if(this.showLoadIndicator){
12002             this.el.update(this.indicatorText);
12003         }
12004     },
12005
12006     /**
12007      * Adds unique parameter to query string if disableCaching = true
12008      * @private
12009      */
12010     prepareUrl : function(url){
12011         if(this.disableCaching){
12012             var append = "_dc=" + (new Date().getTime());
12013             if(url.indexOf("?") !== -1){
12014                 url += "&" + append;
12015             }else{
12016                 url += "?" + append;
12017             }
12018         }
12019         return url;
12020     },
12021
12022     /**
12023      * @private
12024      */
12025     processSuccess : function(response){
12026         this.transaction = null;
12027         if(response.argument.form && response.argument.reset){
12028             try{ // put in try/catch since some older FF releases had problems with this
12029                 response.argument.form.reset();
12030             }catch(e){}
12031         }
12032         if(this.loadScripts){
12033             this.renderer.render(this.el, response, this,
12034                 this.updateComplete.createDelegate(this, [response]));
12035         }else{
12036             this.renderer.render(this.el, response, this);
12037             this.updateComplete(response);
12038         }
12039     },
12040
12041     updateComplete : function(response){
12042         this.fireEvent("update", this.el, response);
12043         if(typeof response.argument.callback == "function"){
12044             response.argument.callback(this.el, true, response);
12045         }
12046     },
12047
12048     /**
12049      * @private
12050      */
12051     processFailure : function(response){
12052         this.transaction = null;
12053         this.fireEvent("failure", this.el, response);
12054         if(typeof response.argument.callback == "function"){
12055             response.argument.callback(this.el, false, response);
12056         }
12057     },
12058
12059     /**
12060      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12061      * @param {Object} renderer The object implementing the render() method
12062      */
12063     setRenderer : function(renderer){
12064         this.renderer = renderer;
12065     },
12066
12067     getRenderer : function(){
12068        return this.renderer;
12069     },
12070
12071     /**
12072      * Set the defaultUrl used for updates
12073      * @param {String/Function} defaultUrl The url or a function to call to get the url
12074      */
12075     setDefaultUrl : function(defaultUrl){
12076         this.defaultUrl = defaultUrl;
12077     },
12078
12079     /**
12080      * Aborts the executing transaction
12081      */
12082     abort : function(){
12083         if(this.transaction){
12084             Roo.Ajax.abort(this.transaction);
12085         }
12086     },
12087
12088     /**
12089      * Returns true if an update is in progress
12090      * @return {Boolean}
12091      */
12092     isUpdating : function(){
12093         if(this.transaction){
12094             return Roo.Ajax.isLoading(this.transaction);
12095         }
12096         return false;
12097     }
12098 });
12099
12100 /**
12101  * @class Roo.UpdateManager.defaults
12102  * @static (not really - but it helps the doc tool)
12103  * The defaults collection enables customizing the default properties of UpdateManager
12104  */
12105    Roo.UpdateManager.defaults = {
12106        /**
12107          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12108          * @type Number
12109          */
12110          timeout : 30,
12111
12112          /**
12113          * True to process scripts by default (Defaults to false).
12114          * @type Boolean
12115          */
12116         loadScripts : false,
12117
12118         /**
12119         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12120         * @type String
12121         */
12122         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12123         /**
12124          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12125          * @type Boolean
12126          */
12127         disableCaching : false,
12128         /**
12129          * Whether to show indicatorText when loading (Defaults to true).
12130          * @type Boolean
12131          */
12132         showLoadIndicator : true,
12133         /**
12134          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12135          * @type String
12136          */
12137         indicatorText : '<div class="loading-indicator">Loading...</div>'
12138    };
12139
12140 /**
12141  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12142  *Usage:
12143  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12144  * @param {String/HTMLElement/Roo.Element} el The element to update
12145  * @param {String} url The url
12146  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12147  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12148  * @static
12149  * @deprecated
12150  * @member Roo.UpdateManager
12151  */
12152 Roo.UpdateManager.updateElement = function(el, url, params, options){
12153     var um = Roo.get(el, true).getUpdateManager();
12154     Roo.apply(um, options);
12155     um.update(url, params, options ? options.callback : null);
12156 };
12157 // alias for backwards compat
12158 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12159 /**
12160  * @class Roo.UpdateManager.BasicRenderer
12161  * Default Content renderer. Updates the elements innerHTML with the responseText.
12162  */
12163 Roo.UpdateManager.BasicRenderer = function(){};
12164
12165 Roo.UpdateManager.BasicRenderer.prototype = {
12166     /**
12167      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12168      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12169      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12170      * @param {Roo.Element} el The element being rendered
12171      * @param {Object} response The YUI Connect response object
12172      * @param {UpdateManager} updateManager The calling update manager
12173      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12174      */
12175      render : function(el, response, updateManager, callback){
12176         el.update(response.responseText, updateManager.loadScripts, callback);
12177     }
12178 };
12179 /*
12180  * Based on:
12181  * Roo JS
12182  * (c)) Alan Knowles
12183  * Licence : LGPL
12184  */
12185
12186
12187 /**
12188  * @class Roo.DomTemplate
12189  * @extends Roo.Template
12190  * An effort at a dom based template engine..
12191  *
12192  * Similar to XTemplate, except it uses dom parsing to create the template..
12193  *
12194  * Supported features:
12195  *
12196  *  Tags:
12197
12198 <pre><code>
12199       {a_variable} - output encoded.
12200       {a_variable.format:("Y-m-d")} - call a method on the variable
12201       {a_variable:raw} - unencoded output
12202       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12203       {a_variable:this.method_on_template(...)} - call a method on the template object.
12204  
12205 </code></pre>
12206  *  The tpl tag:
12207 <pre><code>
12208         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12209         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12210         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12211         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12212   
12213 </code></pre>
12214  *      
12215  */
12216 Roo.DomTemplate = function()
12217 {
12218      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12219      if (this.html) {
12220         this.compile();
12221      }
12222 };
12223
12224
12225 Roo.extend(Roo.DomTemplate, Roo.Template, {
12226     /**
12227      * id counter for sub templates.
12228      */
12229     id : 0,
12230     /**
12231      * flag to indicate if dom parser is inside a pre,
12232      * it will strip whitespace if not.
12233      */
12234     inPre : false,
12235     
12236     /**
12237      * The various sub templates
12238      */
12239     tpls : false,
12240     
12241     
12242     
12243     /**
12244      *
12245      * basic tag replacing syntax
12246      * WORD:WORD()
12247      *
12248      * // you can fake an object call by doing this
12249      *  x.t:(test,tesT) 
12250      * 
12251      */
12252     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12253     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12254     
12255     iterChild : function (node, method) {
12256         
12257         var oldPre = this.inPre;
12258         if (node.tagName == 'PRE') {
12259             this.inPre = true;
12260         }
12261         for( var i = 0; i < node.childNodes.length; i++) {
12262             method.call(this, node.childNodes[i]);
12263         }
12264         this.inPre = oldPre;
12265     },
12266     
12267     
12268     
12269     /**
12270      * compile the template
12271      *
12272      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12273      *
12274      */
12275     compile: function()
12276     {
12277         var s = this.html;
12278         
12279         // covert the html into DOM...
12280         var doc = false;
12281         var div =false;
12282         try {
12283             doc = document.implementation.createHTMLDocument("");
12284             doc.documentElement.innerHTML =   this.html  ;
12285             div = doc.documentElement;
12286         } catch (e) {
12287             // old IE... - nasty -- it causes all sorts of issues.. with
12288             // images getting pulled from server..
12289             div = document.createElement('div');
12290             div.innerHTML = this.html;
12291         }
12292         //doc.documentElement.innerHTML = htmlBody
12293          
12294         
12295         
12296         this.tpls = [];
12297         var _t = this;
12298         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12299         
12300         var tpls = this.tpls;
12301         
12302         // create a top level template from the snippet..
12303         
12304         //Roo.log(div.innerHTML);
12305         
12306         var tpl = {
12307             uid : 'master',
12308             id : this.id++,
12309             attr : false,
12310             value : false,
12311             body : div.innerHTML,
12312             
12313             forCall : false,
12314             execCall : false,
12315             dom : div,
12316             isTop : true
12317             
12318         };
12319         tpls.unshift(tpl);
12320         
12321         
12322         // compile them...
12323         this.tpls = [];
12324         Roo.each(tpls, function(tp){
12325             this.compileTpl(tp);
12326             this.tpls[tp.id] = tp;
12327         }, this);
12328         
12329         this.master = tpls[0];
12330         return this;
12331         
12332         
12333     },
12334     
12335     compileNode : function(node, istop) {
12336         // test for
12337         //Roo.log(node);
12338         
12339         
12340         // skip anything not a tag..
12341         if (node.nodeType != 1) {
12342             if (node.nodeType == 3 && !this.inPre) {
12343                 // reduce white space..
12344                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12345                 
12346             }
12347             return;
12348         }
12349         
12350         var tpl = {
12351             uid : false,
12352             id : false,
12353             attr : false,
12354             value : false,
12355             body : '',
12356             
12357             forCall : false,
12358             execCall : false,
12359             dom : false,
12360             isTop : istop
12361             
12362             
12363         };
12364         
12365         
12366         switch(true) {
12367             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12368             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12369             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12370             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12371             // no default..
12372         }
12373         
12374         
12375         if (!tpl.attr) {
12376             // just itterate children..
12377             this.iterChild(node,this.compileNode);
12378             return;
12379         }
12380         tpl.uid = this.id++;
12381         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12382         node.removeAttribute('roo-'+ tpl.attr);
12383         if (tpl.attr != 'name') {
12384             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12385             node.parentNode.replaceChild(placeholder,  node);
12386         } else {
12387             
12388             var placeholder =  document.createElement('span');
12389             placeholder.className = 'roo-tpl-' + tpl.value;
12390             node.parentNode.replaceChild(placeholder,  node);
12391         }
12392         
12393         // parent now sees '{domtplXXXX}
12394         this.iterChild(node,this.compileNode);
12395         
12396         // we should now have node body...
12397         var div = document.createElement('div');
12398         div.appendChild(node);
12399         tpl.dom = node;
12400         // this has the unfortunate side effect of converting tagged attributes
12401         // eg. href="{...}" into %7C...%7D
12402         // this has been fixed by searching for those combo's although it's a bit hacky..
12403         
12404         
12405         tpl.body = div.innerHTML;
12406         
12407         
12408          
12409         tpl.id = tpl.uid;
12410         switch(tpl.attr) {
12411             case 'for' :
12412                 switch (tpl.value) {
12413                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12414                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12415                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12416                 }
12417                 break;
12418             
12419             case 'exec':
12420                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12421                 break;
12422             
12423             case 'if':     
12424                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12425                 break;
12426             
12427             case 'name':
12428                 tpl.id  = tpl.value; // replace non characters???
12429                 break;
12430             
12431         }
12432         
12433         
12434         this.tpls.push(tpl);
12435         
12436         
12437         
12438     },
12439     
12440     
12441     
12442     
12443     /**
12444      * Compile a segment of the template into a 'sub-template'
12445      *
12446      * 
12447      * 
12448      *
12449      */
12450     compileTpl : function(tpl)
12451     {
12452         var fm = Roo.util.Format;
12453         var useF = this.disableFormats !== true;
12454         
12455         var sep = Roo.isGecko ? "+\n" : ",\n";
12456         
12457         var undef = function(str) {
12458             Roo.debug && Roo.log("Property not found :"  + str);
12459             return '';
12460         };
12461           
12462         //Roo.log(tpl.body);
12463         
12464         
12465         
12466         var fn = function(m, lbrace, name, format, args)
12467         {
12468             //Roo.log("ARGS");
12469             //Roo.log(arguments);
12470             args = args ? args.replace(/\\'/g,"'") : args;
12471             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12472             if (typeof(format) == 'undefined') {
12473                 format =  'htmlEncode'; 
12474             }
12475             if (format == 'raw' ) {
12476                 format = false;
12477             }
12478             
12479             if(name.substr(0, 6) == 'domtpl'){
12480                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12481             }
12482             
12483             // build an array of options to determine if value is undefined..
12484             
12485             // basically get 'xxxx.yyyy' then do
12486             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12487             //    (function () { Roo.log("Property not found"); return ''; })() :
12488             //    ......
12489             
12490             var udef_ar = [];
12491             var lookfor = '';
12492             Roo.each(name.split('.'), function(st) {
12493                 lookfor += (lookfor.length ? '.': '') + st;
12494                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12495             });
12496             
12497             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12498             
12499             
12500             if(format && useF){
12501                 
12502                 args = args ? ',' + args : "";
12503                  
12504                 if(format.substr(0, 5) != "this."){
12505                     format = "fm." + format + '(';
12506                 }else{
12507                     format = 'this.call("'+ format.substr(5) + '", ';
12508                     args = ", values";
12509                 }
12510                 
12511                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12512             }
12513              
12514             if (args && args.length) {
12515                 // called with xxyx.yuu:(test,test)
12516                 // change to ()
12517                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12518             }
12519             // raw.. - :raw modifier..
12520             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12521             
12522         };
12523         var body;
12524         // branched to use + in gecko and [].join() in others
12525         if(Roo.isGecko){
12526             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12527                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12528                     "';};};";
12529         }else{
12530             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12531             body.push(tpl.body.replace(/(\r\n|\n)/g,
12532                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12533             body.push("'].join('');};};");
12534             body = body.join('');
12535         }
12536         
12537         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12538        
12539         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12540         eval(body);
12541         
12542         return this;
12543     },
12544      
12545     /**
12546      * same as applyTemplate, except it's done to one of the subTemplates
12547      * when using named templates, you can do:
12548      *
12549      * var str = pl.applySubTemplate('your-name', values);
12550      *
12551      * 
12552      * @param {Number} id of the template
12553      * @param {Object} values to apply to template
12554      * @param {Object} parent (normaly the instance of this object)
12555      */
12556     applySubTemplate : function(id, values, parent)
12557     {
12558         
12559         
12560         var t = this.tpls[id];
12561         
12562         
12563         try { 
12564             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12565                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12566                 return '';
12567             }
12568         } catch(e) {
12569             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12570             Roo.log(values);
12571           
12572             return '';
12573         }
12574         try { 
12575             
12576             if(t.execCall && t.execCall.call(this, values, parent)){
12577                 return '';
12578             }
12579         } catch(e) {
12580             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12581             Roo.log(values);
12582             return '';
12583         }
12584         
12585         try {
12586             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12587             parent = t.target ? values : parent;
12588             if(t.forCall && vs instanceof Array){
12589                 var buf = [];
12590                 for(var i = 0, len = vs.length; i < len; i++){
12591                     try {
12592                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12593                     } catch (e) {
12594                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12595                         Roo.log(e.body);
12596                         //Roo.log(t.compiled);
12597                         Roo.log(vs[i]);
12598                     }   
12599                 }
12600                 return buf.join('');
12601             }
12602         } catch (e) {
12603             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12604             Roo.log(values);
12605             return '';
12606         }
12607         try {
12608             return t.compiled.call(this, vs, parent);
12609         } catch (e) {
12610             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12611             Roo.log(e.body);
12612             //Roo.log(t.compiled);
12613             Roo.log(values);
12614             return '';
12615         }
12616     },
12617
12618    
12619
12620     applyTemplate : function(values){
12621         return this.master.compiled.call(this, values, {});
12622         //var s = this.subs;
12623     },
12624
12625     apply : function(){
12626         return this.applyTemplate.apply(this, arguments);
12627     }
12628
12629  });
12630
12631 Roo.DomTemplate.from = function(el){
12632     el = Roo.getDom(el);
12633     return new Roo.Domtemplate(el.value || el.innerHTML);
12634 };/*
12635  * Based on:
12636  * Ext JS Library 1.1.1
12637  * Copyright(c) 2006-2007, Ext JS, LLC.
12638  *
12639  * Originally Released Under LGPL - original licence link has changed is not relivant.
12640  *
12641  * Fork - LGPL
12642  * <script type="text/javascript">
12643  */
12644
12645 /**
12646  * @class Roo.util.DelayedTask
12647  * Provides a convenient method of performing setTimeout where a new
12648  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12649  * You can use this class to buffer
12650  * the keypress events for a certain number of milliseconds, and perform only if they stop
12651  * for that amount of time.
12652  * @constructor The parameters to this constructor serve as defaults and are not required.
12653  * @param {Function} fn (optional) The default function to timeout
12654  * @param {Object} scope (optional) The default scope of that timeout
12655  * @param {Array} args (optional) The default Array of arguments
12656  */
12657 Roo.util.DelayedTask = function(fn, scope, args){
12658     var id = null, d, t;
12659
12660     var call = function(){
12661         var now = new Date().getTime();
12662         if(now - t >= d){
12663             clearInterval(id);
12664             id = null;
12665             fn.apply(scope, args || []);
12666         }
12667     };
12668     /**
12669      * Cancels any pending timeout and queues a new one
12670      * @param {Number} delay The milliseconds to delay
12671      * @param {Function} newFn (optional) Overrides function passed to constructor
12672      * @param {Object} newScope (optional) Overrides scope passed to constructor
12673      * @param {Array} newArgs (optional) Overrides args passed to constructor
12674      */
12675     this.delay = function(delay, newFn, newScope, newArgs){
12676         if(id && delay != d){
12677             this.cancel();
12678         }
12679         d = delay;
12680         t = new Date().getTime();
12681         fn = newFn || fn;
12682         scope = newScope || scope;
12683         args = newArgs || args;
12684         if(!id){
12685             id = setInterval(call, d);
12686         }
12687     };
12688
12689     /**
12690      * Cancel the last queued timeout
12691      */
12692     this.cancel = function(){
12693         if(id){
12694             clearInterval(id);
12695             id = null;
12696         }
12697     };
12698 };/*
12699  * Based on:
12700  * Ext JS Library 1.1.1
12701  * Copyright(c) 2006-2007, Ext JS, LLC.
12702  *
12703  * Originally Released Under LGPL - original licence link has changed is not relivant.
12704  *
12705  * Fork - LGPL
12706  * <script type="text/javascript">
12707  */
12708  
12709  
12710 Roo.util.TaskRunner = function(interval){
12711     interval = interval || 10;
12712     var tasks = [], removeQueue = [];
12713     var id = 0;
12714     var running = false;
12715
12716     var stopThread = function(){
12717         running = false;
12718         clearInterval(id);
12719         id = 0;
12720     };
12721
12722     var startThread = function(){
12723         if(!running){
12724             running = true;
12725             id = setInterval(runTasks, interval);
12726         }
12727     };
12728
12729     var removeTask = function(task){
12730         removeQueue.push(task);
12731         if(task.onStop){
12732             task.onStop();
12733         }
12734     };
12735
12736     var runTasks = function(){
12737         if(removeQueue.length > 0){
12738             for(var i = 0, len = removeQueue.length; i < len; i++){
12739                 tasks.remove(removeQueue[i]);
12740             }
12741             removeQueue = [];
12742             if(tasks.length < 1){
12743                 stopThread();
12744                 return;
12745             }
12746         }
12747         var now = new Date().getTime();
12748         for(var i = 0, len = tasks.length; i < len; ++i){
12749             var t = tasks[i];
12750             var itime = now - t.taskRunTime;
12751             if(t.interval <= itime){
12752                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12753                 t.taskRunTime = now;
12754                 if(rt === false || t.taskRunCount === t.repeat){
12755                     removeTask(t);
12756                     return;
12757                 }
12758             }
12759             if(t.duration && t.duration <= (now - t.taskStartTime)){
12760                 removeTask(t);
12761             }
12762         }
12763     };
12764
12765     /**
12766      * Queues a new task.
12767      * @param {Object} task
12768      */
12769     this.start = function(task){
12770         tasks.push(task);
12771         task.taskStartTime = new Date().getTime();
12772         task.taskRunTime = 0;
12773         task.taskRunCount = 0;
12774         startThread();
12775         return task;
12776     };
12777
12778     this.stop = function(task){
12779         removeTask(task);
12780         return task;
12781     };
12782
12783     this.stopAll = function(){
12784         stopThread();
12785         for(var i = 0, len = tasks.length; i < len; i++){
12786             if(tasks[i].onStop){
12787                 tasks[i].onStop();
12788             }
12789         }
12790         tasks = [];
12791         removeQueue = [];
12792     };
12793 };
12794
12795 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12796  * Based on:
12797  * Ext JS Library 1.1.1
12798  * Copyright(c) 2006-2007, Ext JS, LLC.
12799  *
12800  * Originally Released Under LGPL - original licence link has changed is not relivant.
12801  *
12802  * Fork - LGPL
12803  * <script type="text/javascript">
12804  */
12805
12806  
12807 /**
12808  * @class Roo.util.MixedCollection
12809  * @extends Roo.util.Observable
12810  * A Collection class that maintains both numeric indexes and keys and exposes events.
12811  * @constructor
12812  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12813  * collection (defaults to false)
12814  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12815  * and return the key value for that item.  This is used when available to look up the key on items that
12816  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12817  * equivalent to providing an implementation for the {@link #getKey} method.
12818  */
12819 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12820     this.items = [];
12821     this.map = {};
12822     this.keys = [];
12823     this.length = 0;
12824     this.addEvents({
12825         /**
12826          * @event clear
12827          * Fires when the collection is cleared.
12828          */
12829         "clear" : true,
12830         /**
12831          * @event add
12832          * Fires when an item is added to the collection.
12833          * @param {Number} index The index at which the item was added.
12834          * @param {Object} o The item added.
12835          * @param {String} key The key associated with the added item.
12836          */
12837         "add" : true,
12838         /**
12839          * @event replace
12840          * Fires when an item is replaced in the collection.
12841          * @param {String} key he key associated with the new added.
12842          * @param {Object} old The item being replaced.
12843          * @param {Object} new The new item.
12844          */
12845         "replace" : true,
12846         /**
12847          * @event remove
12848          * Fires when an item is removed from the collection.
12849          * @param {Object} o The item being removed.
12850          * @param {String} key (optional) The key associated with the removed item.
12851          */
12852         "remove" : true,
12853         "sort" : true
12854     });
12855     this.allowFunctions = allowFunctions === true;
12856     if(keyFn){
12857         this.getKey = keyFn;
12858     }
12859     Roo.util.MixedCollection.superclass.constructor.call(this);
12860 };
12861
12862 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12863     allowFunctions : false,
12864     
12865 /**
12866  * Adds an item to the collection.
12867  * @param {String} key The key to associate with the item
12868  * @param {Object} o The item to add.
12869  * @return {Object} The item added.
12870  */
12871     add : function(key, o){
12872         if(arguments.length == 1){
12873             o = arguments[0];
12874             key = this.getKey(o);
12875         }
12876         if(typeof key == "undefined" || key === null){
12877             this.length++;
12878             this.items.push(o);
12879             this.keys.push(null);
12880         }else{
12881             var old = this.map[key];
12882             if(old){
12883                 return this.replace(key, o);
12884             }
12885             this.length++;
12886             this.items.push(o);
12887             this.map[key] = o;
12888             this.keys.push(key);
12889         }
12890         this.fireEvent("add", this.length-1, o, key);
12891         return o;
12892     },
12893        
12894 /**
12895   * MixedCollection has a generic way to fetch keys if you implement getKey.
12896 <pre><code>
12897 // normal way
12898 var mc = new Roo.util.MixedCollection();
12899 mc.add(someEl.dom.id, someEl);
12900 mc.add(otherEl.dom.id, otherEl);
12901 //and so on
12902
12903 // using getKey
12904 var mc = new Roo.util.MixedCollection();
12905 mc.getKey = function(el){
12906    return el.dom.id;
12907 };
12908 mc.add(someEl);
12909 mc.add(otherEl);
12910
12911 // or via the constructor
12912 var mc = new Roo.util.MixedCollection(false, function(el){
12913    return el.dom.id;
12914 });
12915 mc.add(someEl);
12916 mc.add(otherEl);
12917 </code></pre>
12918  * @param o {Object} The item for which to find the key.
12919  * @return {Object} The key for the passed item.
12920  */
12921     getKey : function(o){
12922          return o.id; 
12923     },
12924    
12925 /**
12926  * Replaces an item in the collection.
12927  * @param {String} key The key associated with the item to replace, or the item to replace.
12928  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12929  * @return {Object}  The new item.
12930  */
12931     replace : function(key, o){
12932         if(arguments.length == 1){
12933             o = arguments[0];
12934             key = this.getKey(o);
12935         }
12936         var old = this.item(key);
12937         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12938              return this.add(key, o);
12939         }
12940         var index = this.indexOfKey(key);
12941         this.items[index] = o;
12942         this.map[key] = o;
12943         this.fireEvent("replace", key, old, o);
12944         return o;
12945     },
12946    
12947 /**
12948  * Adds all elements of an Array or an Object to the collection.
12949  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12950  * an Array of values, each of which are added to the collection.
12951  */
12952     addAll : function(objs){
12953         if(arguments.length > 1 || objs instanceof Array){
12954             var args = arguments.length > 1 ? arguments : objs;
12955             for(var i = 0, len = args.length; i < len; i++){
12956                 this.add(args[i]);
12957             }
12958         }else{
12959             for(var key in objs){
12960                 if(this.allowFunctions || typeof objs[key] != "function"){
12961                     this.add(key, objs[key]);
12962                 }
12963             }
12964         }
12965     },
12966    
12967 /**
12968  * Executes the specified function once for every item in the collection, passing each
12969  * item as the first and only parameter. returning false from the function will stop the iteration.
12970  * @param {Function} fn The function to execute for each item.
12971  * @param {Object} scope (optional) The scope in which to execute the function.
12972  */
12973     each : function(fn, scope){
12974         var items = [].concat(this.items); // each safe for removal
12975         for(var i = 0, len = items.length; i < len; i++){
12976             if(fn.call(scope || items[i], items[i], i, len) === false){
12977                 break;
12978             }
12979         }
12980     },
12981    
12982 /**
12983  * Executes the specified function once for every key in the collection, passing each
12984  * key, and its associated item as the first two parameters.
12985  * @param {Function} fn The function to execute for each item.
12986  * @param {Object} scope (optional) The scope in which to execute the function.
12987  */
12988     eachKey : function(fn, scope){
12989         for(var i = 0, len = this.keys.length; i < len; i++){
12990             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12991         }
12992     },
12993    
12994 /**
12995  * Returns the first item in the collection which elicits a true return value from the
12996  * passed selection function.
12997  * @param {Function} fn The selection function to execute for each item.
12998  * @param {Object} scope (optional) The scope in which to execute the function.
12999  * @return {Object} The first item in the collection which returned true from the selection function.
13000  */
13001     find : function(fn, scope){
13002         for(var i = 0, len = this.items.length; i < len; i++){
13003             if(fn.call(scope || window, this.items[i], this.keys[i])){
13004                 return this.items[i];
13005             }
13006         }
13007         return null;
13008     },
13009    
13010 /**
13011  * Inserts an item at the specified index in the collection.
13012  * @param {Number} index The index to insert the item at.
13013  * @param {String} key The key to associate with the new item, or the item itself.
13014  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13015  * @return {Object} The item inserted.
13016  */
13017     insert : function(index, key, o){
13018         if(arguments.length == 2){
13019             o = arguments[1];
13020             key = this.getKey(o);
13021         }
13022         if(index >= this.length){
13023             return this.add(key, o);
13024         }
13025         this.length++;
13026         this.items.splice(index, 0, o);
13027         if(typeof key != "undefined" && key != null){
13028             this.map[key] = o;
13029         }
13030         this.keys.splice(index, 0, key);
13031         this.fireEvent("add", index, o, key);
13032         return o;
13033     },
13034    
13035 /**
13036  * Removed an item from the collection.
13037  * @param {Object} o The item to remove.
13038  * @return {Object} The item removed.
13039  */
13040     remove : function(o){
13041         return this.removeAt(this.indexOf(o));
13042     },
13043    
13044 /**
13045  * Remove an item from a specified index in the collection.
13046  * @param {Number} index The index within the collection of the item to remove.
13047  */
13048     removeAt : function(index){
13049         if(index < this.length && index >= 0){
13050             this.length--;
13051             var o = this.items[index];
13052             this.items.splice(index, 1);
13053             var key = this.keys[index];
13054             if(typeof key != "undefined"){
13055                 delete this.map[key];
13056             }
13057             this.keys.splice(index, 1);
13058             this.fireEvent("remove", o, key);
13059         }
13060     },
13061    
13062 /**
13063  * Removed an item associated with the passed key fom the collection.
13064  * @param {String} key The key of the item to remove.
13065  */
13066     removeKey : function(key){
13067         return this.removeAt(this.indexOfKey(key));
13068     },
13069    
13070 /**
13071  * Returns the number of items in the collection.
13072  * @return {Number} the number of items in the collection.
13073  */
13074     getCount : function(){
13075         return this.length; 
13076     },
13077    
13078 /**
13079  * Returns index within the collection of the passed Object.
13080  * @param {Object} o The item to find the index of.
13081  * @return {Number} index of the item.
13082  */
13083     indexOf : function(o){
13084         if(!this.items.indexOf){
13085             for(var i = 0, len = this.items.length; i < len; i++){
13086                 if(this.items[i] == o) return i;
13087             }
13088             return -1;
13089         }else{
13090             return this.items.indexOf(o);
13091         }
13092     },
13093    
13094 /**
13095  * Returns index within the collection of the passed key.
13096  * @param {String} key The key to find the index of.
13097  * @return {Number} index of the key.
13098  */
13099     indexOfKey : function(key){
13100         if(!this.keys.indexOf){
13101             for(var i = 0, len = this.keys.length; i < len; i++){
13102                 if(this.keys[i] == key) return i;
13103             }
13104             return -1;
13105         }else{
13106             return this.keys.indexOf(key);
13107         }
13108     },
13109    
13110 /**
13111  * Returns the item associated with the passed key OR index. Key has priority over index.
13112  * @param {String/Number} key The key or index of the item.
13113  * @return {Object} The item associated with the passed key.
13114  */
13115     item : function(key){
13116         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13117         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13118     },
13119     
13120 /**
13121  * Returns the item at the specified index.
13122  * @param {Number} index The index of the item.
13123  * @return {Object}
13124  */
13125     itemAt : function(index){
13126         return this.items[index];
13127     },
13128     
13129 /**
13130  * Returns the item associated with the passed key.
13131  * @param {String/Number} key The key of the item.
13132  * @return {Object} The item associated with the passed key.
13133  */
13134     key : function(key){
13135         return this.map[key];
13136     },
13137    
13138 /**
13139  * Returns true if the collection contains the passed Object as an item.
13140  * @param {Object} o  The Object to look for in the collection.
13141  * @return {Boolean} True if the collection contains the Object as an item.
13142  */
13143     contains : function(o){
13144         return this.indexOf(o) != -1;
13145     },
13146    
13147 /**
13148  * Returns true if the collection contains the passed Object as a key.
13149  * @param {String} key The key to look for in the collection.
13150  * @return {Boolean} True if the collection contains the Object as a key.
13151  */
13152     containsKey : function(key){
13153         return typeof this.map[key] != "undefined";
13154     },
13155    
13156 /**
13157  * Removes all items from the collection.
13158  */
13159     clear : function(){
13160         this.length = 0;
13161         this.items = [];
13162         this.keys = [];
13163         this.map = {};
13164         this.fireEvent("clear");
13165     },
13166    
13167 /**
13168  * Returns the first item in the collection.
13169  * @return {Object} the first item in the collection..
13170  */
13171     first : function(){
13172         return this.items[0]; 
13173     },
13174    
13175 /**
13176  * Returns the last item in the collection.
13177  * @return {Object} the last item in the collection..
13178  */
13179     last : function(){
13180         return this.items[this.length-1];   
13181     },
13182     
13183     _sort : function(property, dir, fn){
13184         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13185         fn = fn || function(a, b){
13186             return a-b;
13187         };
13188         var c = [], k = this.keys, items = this.items;
13189         for(var i = 0, len = items.length; i < len; i++){
13190             c[c.length] = {key: k[i], value: items[i], index: i};
13191         }
13192         c.sort(function(a, b){
13193             var v = fn(a[property], b[property]) * dsc;
13194             if(v == 0){
13195                 v = (a.index < b.index ? -1 : 1);
13196             }
13197             return v;
13198         });
13199         for(var i = 0, len = c.length; i < len; i++){
13200             items[i] = c[i].value;
13201             k[i] = c[i].key;
13202         }
13203         this.fireEvent("sort", this);
13204     },
13205     
13206     /**
13207      * Sorts this collection with the passed comparison function
13208      * @param {String} direction (optional) "ASC" or "DESC"
13209      * @param {Function} fn (optional) comparison function
13210      */
13211     sort : function(dir, fn){
13212         this._sort("value", dir, fn);
13213     },
13214     
13215     /**
13216      * Sorts this collection by keys
13217      * @param {String} direction (optional) "ASC" or "DESC"
13218      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13219      */
13220     keySort : function(dir, fn){
13221         this._sort("key", dir, fn || function(a, b){
13222             return String(a).toUpperCase()-String(b).toUpperCase();
13223         });
13224     },
13225     
13226     /**
13227      * Returns a range of items in this collection
13228      * @param {Number} startIndex (optional) defaults to 0
13229      * @param {Number} endIndex (optional) default to the last item
13230      * @return {Array} An array of items
13231      */
13232     getRange : function(start, end){
13233         var items = this.items;
13234         if(items.length < 1){
13235             return [];
13236         }
13237         start = start || 0;
13238         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13239         var r = [];
13240         if(start <= end){
13241             for(var i = start; i <= end; i++) {
13242                     r[r.length] = items[i];
13243             }
13244         }else{
13245             for(var i = start; i >= end; i--) {
13246                     r[r.length] = items[i];
13247             }
13248         }
13249         return r;
13250     },
13251         
13252     /**
13253      * Filter the <i>objects</i> in this collection by a specific property. 
13254      * Returns a new collection that has been filtered.
13255      * @param {String} property A property on your objects
13256      * @param {String/RegExp} value Either string that the property values 
13257      * should start with or a RegExp to test against the property
13258      * @return {MixedCollection} The new filtered collection
13259      */
13260     filter : function(property, value){
13261         if(!value.exec){ // not a regex
13262             value = String(value);
13263             if(value.length == 0){
13264                 return this.clone();
13265             }
13266             value = new RegExp("^" + Roo.escapeRe(value), "i");
13267         }
13268         return this.filterBy(function(o){
13269             return o && value.test(o[property]);
13270         });
13271         },
13272     
13273     /**
13274      * Filter by a function. * Returns a new collection that has been filtered.
13275      * The passed function will be called with each 
13276      * object in the collection. If the function returns true, the value is included 
13277      * otherwise it is filtered.
13278      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13279      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13280      * @return {MixedCollection} The new filtered collection
13281      */
13282     filterBy : function(fn, scope){
13283         var r = new Roo.util.MixedCollection();
13284         r.getKey = this.getKey;
13285         var k = this.keys, it = this.items;
13286         for(var i = 0, len = it.length; i < len; i++){
13287             if(fn.call(scope||this, it[i], k[i])){
13288                                 r.add(k[i], it[i]);
13289                         }
13290         }
13291         return r;
13292     },
13293     
13294     /**
13295      * Creates a duplicate of this collection
13296      * @return {MixedCollection}
13297      */
13298     clone : function(){
13299         var r = new Roo.util.MixedCollection();
13300         var k = this.keys, it = this.items;
13301         for(var i = 0, len = it.length; i < len; i++){
13302             r.add(k[i], it[i]);
13303         }
13304         r.getKey = this.getKey;
13305         return r;
13306     }
13307 });
13308 /**
13309  * Returns the item associated with the passed key or index.
13310  * @method
13311  * @param {String/Number} key The key or index of the item.
13312  * @return {Object} The item associated with the passed key.
13313  */
13314 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13315  * Based on:
13316  * Ext JS Library 1.1.1
13317  * Copyright(c) 2006-2007, Ext JS, LLC.
13318  *
13319  * Originally Released Under LGPL - original licence link has changed is not relivant.
13320  *
13321  * Fork - LGPL
13322  * <script type="text/javascript">
13323  */
13324 /**
13325  * @class Roo.util.JSON
13326  * Modified version of Douglas Crockford"s json.js that doesn"t
13327  * mess with the Object prototype 
13328  * http://www.json.org/js.html
13329  * @singleton
13330  */
13331 Roo.util.JSON = new (function(){
13332     var useHasOwn = {}.hasOwnProperty ? true : false;
13333     
13334     // crashes Safari in some instances
13335     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13336     
13337     var pad = function(n) {
13338         return n < 10 ? "0" + n : n;
13339     };
13340     
13341     var m = {
13342         "\b": '\\b',
13343         "\t": '\\t',
13344         "\n": '\\n',
13345         "\f": '\\f',
13346         "\r": '\\r',
13347         '"' : '\\"',
13348         "\\": '\\\\'
13349     };
13350
13351     var encodeString = function(s){
13352         if (/["\\\x00-\x1f]/.test(s)) {
13353             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13354                 var c = m[b];
13355                 if(c){
13356                     return c;
13357                 }
13358                 c = b.charCodeAt();
13359                 return "\\u00" +
13360                     Math.floor(c / 16).toString(16) +
13361                     (c % 16).toString(16);
13362             }) + '"';
13363         }
13364         return '"' + s + '"';
13365     };
13366     
13367     var encodeArray = function(o){
13368         var a = ["["], b, i, l = o.length, v;
13369             for (i = 0; i < l; i += 1) {
13370                 v = o[i];
13371                 switch (typeof v) {
13372                     case "undefined":
13373                     case "function":
13374                     case "unknown":
13375                         break;
13376                     default:
13377                         if (b) {
13378                             a.push(',');
13379                         }
13380                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13381                         b = true;
13382                 }
13383             }
13384             a.push("]");
13385             return a.join("");
13386     };
13387     
13388     var encodeDate = function(o){
13389         return '"' + o.getFullYear() + "-" +
13390                 pad(o.getMonth() + 1) + "-" +
13391                 pad(o.getDate()) + "T" +
13392                 pad(o.getHours()) + ":" +
13393                 pad(o.getMinutes()) + ":" +
13394                 pad(o.getSeconds()) + '"';
13395     };
13396     
13397     /**
13398      * Encodes an Object, Array or other value
13399      * @param {Mixed} o The variable to encode
13400      * @return {String} The JSON string
13401      */
13402     this.encode = function(o)
13403     {
13404         // should this be extended to fully wrap stringify..
13405         
13406         if(typeof o == "undefined" || o === null){
13407             return "null";
13408         }else if(o instanceof Array){
13409             return encodeArray(o);
13410         }else if(o instanceof Date){
13411             return encodeDate(o);
13412         }else if(typeof o == "string"){
13413             return encodeString(o);
13414         }else if(typeof o == "number"){
13415             return isFinite(o) ? String(o) : "null";
13416         }else if(typeof o == "boolean"){
13417             return String(o);
13418         }else {
13419             var a = ["{"], b, i, v;
13420             for (i in o) {
13421                 if(!useHasOwn || o.hasOwnProperty(i)) {
13422                     v = o[i];
13423                     switch (typeof v) {
13424                     case "undefined":
13425                     case "function":
13426                     case "unknown":
13427                         break;
13428                     default:
13429                         if(b){
13430                             a.push(',');
13431                         }
13432                         a.push(this.encode(i), ":",
13433                                 v === null ? "null" : this.encode(v));
13434                         b = true;
13435                     }
13436                 }
13437             }
13438             a.push("}");
13439             return a.join("");
13440         }
13441     };
13442     
13443     /**
13444      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13445      * @param {String} json The JSON string
13446      * @return {Object} The resulting object
13447      */
13448     this.decode = function(json){
13449         
13450         return  /** eval:var:json */ eval("(" + json + ')');
13451     };
13452 })();
13453 /** 
13454  * Shorthand for {@link Roo.util.JSON#encode}
13455  * @member Roo encode 
13456  * @method */
13457 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13458 /** 
13459  * Shorthand for {@link Roo.util.JSON#decode}
13460  * @member Roo decode 
13461  * @method */
13462 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13463 /*
13464  * Based on:
13465  * Ext JS Library 1.1.1
13466  * Copyright(c) 2006-2007, Ext JS, LLC.
13467  *
13468  * Originally Released Under LGPL - original licence link has changed is not relivant.
13469  *
13470  * Fork - LGPL
13471  * <script type="text/javascript">
13472  */
13473  
13474 /**
13475  * @class Roo.util.Format
13476  * Reusable data formatting functions
13477  * @singleton
13478  */
13479 Roo.util.Format = function(){
13480     var trimRe = /^\s+|\s+$/g;
13481     return {
13482         /**
13483          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13484          * @param {String} value The string to truncate
13485          * @param {Number} length The maximum length to allow before truncating
13486          * @return {String} The converted text
13487          */
13488         ellipsis : function(value, len){
13489             if(value && value.length > len){
13490                 return value.substr(0, len-3)+"...";
13491             }
13492             return value;
13493         },
13494
13495         /**
13496          * Checks a reference and converts it to empty string if it is undefined
13497          * @param {Mixed} value Reference to check
13498          * @return {Mixed} Empty string if converted, otherwise the original value
13499          */
13500         undef : function(value){
13501             return typeof value != "undefined" ? value : "";
13502         },
13503
13504         /**
13505          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13506          * @param {String} value The string to encode
13507          * @return {String} The encoded text
13508          */
13509         htmlEncode : function(value){
13510             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13511         },
13512
13513         /**
13514          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13515          * @param {String} value The string to decode
13516          * @return {String} The decoded text
13517          */
13518         htmlDecode : function(value){
13519             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13520         },
13521
13522         /**
13523          * Trims any whitespace from either side of a string
13524          * @param {String} value The text to trim
13525          * @return {String} The trimmed text
13526          */
13527         trim : function(value){
13528             return String(value).replace(trimRe, "");
13529         },
13530
13531         /**
13532          * Returns a substring from within an original string
13533          * @param {String} value The original text
13534          * @param {Number} start The start index of the substring
13535          * @param {Number} length The length of the substring
13536          * @return {String} The substring
13537          */
13538         substr : function(value, start, length){
13539             return String(value).substr(start, length);
13540         },
13541
13542         /**
13543          * Converts a string to all lower case letters
13544          * @param {String} value The text to convert
13545          * @return {String} The converted text
13546          */
13547         lowercase : function(value){
13548             return String(value).toLowerCase();
13549         },
13550
13551         /**
13552          * Converts a string to all upper case letters
13553          * @param {String} value The text to convert
13554          * @return {String} The converted text
13555          */
13556         uppercase : function(value){
13557             return String(value).toUpperCase();
13558         },
13559
13560         /**
13561          * Converts the first character only of a string to upper case
13562          * @param {String} value The text to convert
13563          * @return {String} The converted text
13564          */
13565         capitalize : function(value){
13566             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13567         },
13568
13569         // private
13570         call : function(value, fn){
13571             if(arguments.length > 2){
13572                 var args = Array.prototype.slice.call(arguments, 2);
13573                 args.unshift(value);
13574                  
13575                 return /** eval:var:value */  eval(fn).apply(window, args);
13576             }else{
13577                 /** eval:var:value */
13578                 return /** eval:var:value */ eval(fn).call(window, value);
13579             }
13580         },
13581
13582        
13583         /**
13584          * safer version of Math.toFixed..??/
13585          * @param {Number/String} value The numeric value to format
13586          * @param {Number/String} value Decimal places 
13587          * @return {String} The formatted currency string
13588          */
13589         toFixed : function(v, n)
13590         {
13591             // why not use to fixed - precision is buggered???
13592             if (!n) {
13593                 return Math.round(v-0);
13594             }
13595             var fact = Math.pow(10,n+1);
13596             v = (Math.round((v-0)*fact))/fact;
13597             var z = (''+fact).substring(2);
13598             if (v == Math.floor(v)) {
13599                 return Math.floor(v) + '.' + z;
13600             }
13601             
13602             // now just padd decimals..
13603             var ps = String(v).split('.');
13604             var fd = (ps[1] + z);
13605             var r = fd.substring(0,n); 
13606             var rm = fd.substring(n); 
13607             if (rm < 5) {
13608                 return ps[0] + '.' + r;
13609             }
13610             r*=1; // turn it into a number;
13611             r++;
13612             if (String(r).length != n) {
13613                 ps[0]*=1;
13614                 ps[0]++;
13615                 r = String(r).substring(1); // chop the end off.
13616             }
13617             
13618             return ps[0] + '.' + r;
13619              
13620         },
13621         
13622         /**
13623          * Format a number as US currency
13624          * @param {Number/String} value The numeric value to format
13625          * @return {String} The formatted currency string
13626          */
13627         usMoney : function(v){
13628             return '$' + Roo.util.Format.number(v);
13629         },
13630         
13631         /**
13632          * Format a number
13633          * eventually this should probably emulate php's number_format
13634          * @param {Number/String} value The numeric value to format
13635          * @param {Number} decimals number of decimal places
13636          * @return {String} The formatted currency string
13637          */
13638         number : function(v,decimals)
13639         {
13640             // multiply and round.
13641             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13642             var mul = Math.pow(10, decimals);
13643             var zero = String(mul).substring(1);
13644             v = (Math.round((v-0)*mul))/mul;
13645             
13646             // if it's '0' number.. then
13647             
13648             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13649             v = String(v);
13650             var ps = v.split('.');
13651             var whole = ps[0];
13652             
13653             
13654             var r = /(\d+)(\d{3})/;
13655             // add comma's
13656             while (r.test(whole)) {
13657                 whole = whole.replace(r, '$1' + ',' + '$2');
13658             }
13659             
13660             
13661             var sub = ps[1] ?
13662                     // has decimals..
13663                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13664                     // does not have decimals
13665                     (decimals ? ('.' + zero) : '');
13666             
13667             
13668             return whole + sub ;
13669         },
13670         
13671         /**
13672          * Parse a value into a formatted date using the specified format pattern.
13673          * @param {Mixed} value The value to format
13674          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13675          * @return {String} The formatted date string
13676          */
13677         date : function(v, format){
13678             if(!v){
13679                 return "";
13680             }
13681             if(!(v instanceof Date)){
13682                 v = new Date(Date.parse(v));
13683             }
13684             return v.dateFormat(format || Roo.util.Format.defaults.date);
13685         },
13686
13687         /**
13688          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13689          * @param {String} format Any valid date format string
13690          * @return {Function} The date formatting function
13691          */
13692         dateRenderer : function(format){
13693             return function(v){
13694                 return Roo.util.Format.date(v, format);  
13695             };
13696         },
13697
13698         // private
13699         stripTagsRE : /<\/?[^>]+>/gi,
13700         
13701         /**
13702          * Strips all HTML tags
13703          * @param {Mixed} value The text from which to strip tags
13704          * @return {String} The stripped text
13705          */
13706         stripTags : function(v){
13707             return !v ? v : String(v).replace(this.stripTagsRE, "");
13708         }
13709     };
13710 }();
13711 Roo.util.Format.defaults = {
13712     date : 'd/M/Y'
13713 };/*
13714  * Based on:
13715  * Ext JS Library 1.1.1
13716  * Copyright(c) 2006-2007, Ext JS, LLC.
13717  *
13718  * Originally Released Under LGPL - original licence link has changed is not relivant.
13719  *
13720  * Fork - LGPL
13721  * <script type="text/javascript">
13722  */
13723
13724
13725  
13726
13727 /**
13728  * @class Roo.MasterTemplate
13729  * @extends Roo.Template
13730  * Provides a template that can have child templates. The syntax is:
13731 <pre><code>
13732 var t = new Roo.MasterTemplate(
13733         '&lt;select name="{name}"&gt;',
13734                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13735         '&lt;/select&gt;'
13736 );
13737 t.add('options', {value: 'foo', text: 'bar'});
13738 // or you can add multiple child elements in one shot
13739 t.addAll('options', [
13740     {value: 'foo', text: 'bar'},
13741     {value: 'foo2', text: 'bar2'},
13742     {value: 'foo3', text: 'bar3'}
13743 ]);
13744 // then append, applying the master template values
13745 t.append('my-form', {name: 'my-select'});
13746 </code></pre>
13747 * A name attribute for the child template is not required if you have only one child
13748 * template or you want to refer to them by index.
13749  */
13750 Roo.MasterTemplate = function(){
13751     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13752     this.originalHtml = this.html;
13753     var st = {};
13754     var m, re = this.subTemplateRe;
13755     re.lastIndex = 0;
13756     var subIndex = 0;
13757     while(m = re.exec(this.html)){
13758         var name = m[1], content = m[2];
13759         st[subIndex] = {
13760             name: name,
13761             index: subIndex,
13762             buffer: [],
13763             tpl : new Roo.Template(content)
13764         };
13765         if(name){
13766             st[name] = st[subIndex];
13767         }
13768         st[subIndex].tpl.compile();
13769         st[subIndex].tpl.call = this.call.createDelegate(this);
13770         subIndex++;
13771     }
13772     this.subCount = subIndex;
13773     this.subs = st;
13774 };
13775 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13776     /**
13777     * The regular expression used to match sub templates
13778     * @type RegExp
13779     * @property
13780     */
13781     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13782
13783     /**
13784      * Applies the passed values to a child template.
13785      * @param {String/Number} name (optional) The name or index of the child template
13786      * @param {Array/Object} values The values to be applied to the template
13787      * @return {MasterTemplate} this
13788      */
13789      add : function(name, values){
13790         if(arguments.length == 1){
13791             values = arguments[0];
13792             name = 0;
13793         }
13794         var s = this.subs[name];
13795         s.buffer[s.buffer.length] = s.tpl.apply(values);
13796         return this;
13797     },
13798
13799     /**
13800      * Applies all the passed values to a child template.
13801      * @param {String/Number} name (optional) The name or index of the child template
13802      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13803      * @param {Boolean} reset (optional) True to reset the template first
13804      * @return {MasterTemplate} this
13805      */
13806     fill : function(name, values, reset){
13807         var a = arguments;
13808         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13809             values = a[0];
13810             name = 0;
13811             reset = a[1];
13812         }
13813         if(reset){
13814             this.reset();
13815         }
13816         for(var i = 0, len = values.length; i < len; i++){
13817             this.add(name, values[i]);
13818         }
13819         return this;
13820     },
13821
13822     /**
13823      * Resets the template for reuse
13824      * @return {MasterTemplate} this
13825      */
13826      reset : function(){
13827         var s = this.subs;
13828         for(var i = 0; i < this.subCount; i++){
13829             s[i].buffer = [];
13830         }
13831         return this;
13832     },
13833
13834     applyTemplate : function(values){
13835         var s = this.subs;
13836         var replaceIndex = -1;
13837         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13838             return s[++replaceIndex].buffer.join("");
13839         });
13840         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13841     },
13842
13843     apply : function(){
13844         return this.applyTemplate.apply(this, arguments);
13845     },
13846
13847     compile : function(){return this;}
13848 });
13849
13850 /**
13851  * Alias for fill().
13852  * @method
13853  */
13854 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13855  /**
13856  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13857  * var tpl = Roo.MasterTemplate.from('element-id');
13858  * @param {String/HTMLElement} el
13859  * @param {Object} config
13860  * @static
13861  */
13862 Roo.MasterTemplate.from = function(el, config){
13863     el = Roo.getDom(el);
13864     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13865 };/*
13866  * Based on:
13867  * Ext JS Library 1.1.1
13868  * Copyright(c) 2006-2007, Ext JS, LLC.
13869  *
13870  * Originally Released Under LGPL - original licence link has changed is not relivant.
13871  *
13872  * Fork - LGPL
13873  * <script type="text/javascript">
13874  */
13875
13876  
13877 /**
13878  * @class Roo.util.CSS
13879  * Utility class for manipulating CSS rules
13880  * @singleton
13881  */
13882 Roo.util.CSS = function(){
13883         var rules = null;
13884         var doc = document;
13885
13886     var camelRe = /(-[a-z])/gi;
13887     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13888
13889    return {
13890    /**
13891     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13892     * tag and appended to the HEAD of the document.
13893     * @param {String|Object} cssText The text containing the css rules
13894     * @param {String} id An id to add to the stylesheet for later removal
13895     * @return {StyleSheet}
13896     */
13897     createStyleSheet : function(cssText, id){
13898         var ss;
13899         var head = doc.getElementsByTagName("head")[0];
13900         var nrules = doc.createElement("style");
13901         nrules.setAttribute("type", "text/css");
13902         if(id){
13903             nrules.setAttribute("id", id);
13904         }
13905         if (typeof(cssText) != 'string') {
13906             // support object maps..
13907             // not sure if this a good idea.. 
13908             // perhaps it should be merged with the general css handling
13909             // and handle js style props.
13910             var cssTextNew = [];
13911             for(var n in cssText) {
13912                 var citems = [];
13913                 for(var k in cssText[n]) {
13914                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13915                 }
13916                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13917                 
13918             }
13919             cssText = cssTextNew.join("\n");
13920             
13921         }
13922        
13923        
13924        if(Roo.isIE){
13925            head.appendChild(nrules);
13926            ss = nrules.styleSheet;
13927            ss.cssText = cssText;
13928        }else{
13929            try{
13930                 nrules.appendChild(doc.createTextNode(cssText));
13931            }catch(e){
13932                nrules.cssText = cssText; 
13933            }
13934            head.appendChild(nrules);
13935            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13936        }
13937        this.cacheStyleSheet(ss);
13938        return ss;
13939    },
13940
13941    /**
13942     * Removes a style or link tag by id
13943     * @param {String} id The id of the tag
13944     */
13945    removeStyleSheet : function(id){
13946        var existing = doc.getElementById(id);
13947        if(existing){
13948            existing.parentNode.removeChild(existing);
13949        }
13950    },
13951
13952    /**
13953     * Dynamically swaps an existing stylesheet reference for a new one
13954     * @param {String} id The id of an existing link tag to remove
13955     * @param {String} url The href of the new stylesheet to include
13956     */
13957    swapStyleSheet : function(id, url){
13958        this.removeStyleSheet(id);
13959        var ss = doc.createElement("link");
13960        ss.setAttribute("rel", "stylesheet");
13961        ss.setAttribute("type", "text/css");
13962        ss.setAttribute("id", id);
13963        ss.setAttribute("href", url);
13964        doc.getElementsByTagName("head")[0].appendChild(ss);
13965    },
13966    
13967    /**
13968     * Refresh the rule cache if you have dynamically added stylesheets
13969     * @return {Object} An object (hash) of rules indexed by selector
13970     */
13971    refreshCache : function(){
13972        return this.getRules(true);
13973    },
13974
13975    // private
13976    cacheStyleSheet : function(stylesheet){
13977        if(!rules){
13978            rules = {};
13979        }
13980        try{// try catch for cross domain access issue
13981            var ssRules = stylesheet.cssRules || stylesheet.rules;
13982            for(var j = ssRules.length-1; j >= 0; --j){
13983                rules[ssRules[j].selectorText] = ssRules[j];
13984            }
13985        }catch(e){}
13986    },
13987    
13988    /**
13989     * Gets all css rules for the document
13990     * @param {Boolean} refreshCache true to refresh the internal cache
13991     * @return {Object} An object (hash) of rules indexed by selector
13992     */
13993    getRules : function(refreshCache){
13994                 if(rules == null || refreshCache){
13995                         rules = {};
13996                         var ds = doc.styleSheets;
13997                         for(var i =0, len = ds.length; i < len; i++){
13998                             try{
13999                         this.cacheStyleSheet(ds[i]);
14000                     }catch(e){} 
14001                 }
14002                 }
14003                 return rules;
14004         },
14005         
14006         /**
14007     * Gets an an individual CSS rule by selector(s)
14008     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14009     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14010     * @return {CSSRule} The CSS rule or null if one is not found
14011     */
14012    getRule : function(selector, refreshCache){
14013                 var rs = this.getRules(refreshCache);
14014                 if(!(selector instanceof Array)){
14015                     return rs[selector];
14016                 }
14017                 for(var i = 0; i < selector.length; i++){
14018                         if(rs[selector[i]]){
14019                                 return rs[selector[i]];
14020                         }
14021                 }
14022                 return null;
14023         },
14024         
14025         
14026         /**
14027     * Updates a rule property
14028     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14029     * @param {String} property The css property
14030     * @param {String} value The new value for the property
14031     * @return {Boolean} true If a rule was found and updated
14032     */
14033    updateRule : function(selector, property, value){
14034                 if(!(selector instanceof Array)){
14035                         var rule = this.getRule(selector);
14036                         if(rule){
14037                                 rule.style[property.replace(camelRe, camelFn)] = value;
14038                                 return true;
14039                         }
14040                 }else{
14041                         for(var i = 0; i < selector.length; i++){
14042                                 if(this.updateRule(selector[i], property, value)){
14043                                         return true;
14044                                 }
14045                         }
14046                 }
14047                 return false;
14048         }
14049    };   
14050 }();/*
14051  * Based on:
14052  * Ext JS Library 1.1.1
14053  * Copyright(c) 2006-2007, Ext JS, LLC.
14054  *
14055  * Originally Released Under LGPL - original licence link has changed is not relivant.
14056  *
14057  * Fork - LGPL
14058  * <script type="text/javascript">
14059  */
14060
14061  
14062
14063 /**
14064  * @class Roo.util.ClickRepeater
14065  * @extends Roo.util.Observable
14066  * 
14067  * A wrapper class which can be applied to any element. Fires a "click" event while the
14068  * mouse is pressed. The interval between firings may be specified in the config but
14069  * defaults to 10 milliseconds.
14070  * 
14071  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14072  * 
14073  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14074  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14075  * Similar to an autorepeat key delay.
14076  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14077  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14078  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14079  *           "interval" and "delay" are ignored. "immediate" is honored.
14080  * @cfg {Boolean} preventDefault True to prevent the default click event
14081  * @cfg {Boolean} stopDefault True to stop the default click event
14082  * 
14083  * @history
14084  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14085  *     2007-02-02 jvs Renamed to ClickRepeater
14086  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14087  *
14088  *  @constructor
14089  * @param {String/HTMLElement/Element} el The element to listen on
14090  * @param {Object} config
14091  **/
14092 Roo.util.ClickRepeater = function(el, config)
14093 {
14094     this.el = Roo.get(el);
14095     this.el.unselectable();
14096
14097     Roo.apply(this, config);
14098
14099     this.addEvents({
14100     /**
14101      * @event mousedown
14102      * Fires when the mouse button is depressed.
14103      * @param {Roo.util.ClickRepeater} this
14104      */
14105         "mousedown" : true,
14106     /**
14107      * @event click
14108      * Fires on a specified interval during the time the element is pressed.
14109      * @param {Roo.util.ClickRepeater} this
14110      */
14111         "click" : true,
14112     /**
14113      * @event mouseup
14114      * Fires when the mouse key is released.
14115      * @param {Roo.util.ClickRepeater} this
14116      */
14117         "mouseup" : true
14118     });
14119
14120     this.el.on("mousedown", this.handleMouseDown, this);
14121     if(this.preventDefault || this.stopDefault){
14122         this.el.on("click", function(e){
14123             if(this.preventDefault){
14124                 e.preventDefault();
14125             }
14126             if(this.stopDefault){
14127                 e.stopEvent();
14128             }
14129         }, this);
14130     }
14131
14132     // allow inline handler
14133     if(this.handler){
14134         this.on("click", this.handler,  this.scope || this);
14135     }
14136
14137     Roo.util.ClickRepeater.superclass.constructor.call(this);
14138 };
14139
14140 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14141     interval : 20,
14142     delay: 250,
14143     preventDefault : true,
14144     stopDefault : false,
14145     timer : 0,
14146
14147     // private
14148     handleMouseDown : function(){
14149         clearTimeout(this.timer);
14150         this.el.blur();
14151         if(this.pressClass){
14152             this.el.addClass(this.pressClass);
14153         }
14154         this.mousedownTime = new Date();
14155
14156         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14157         this.el.on("mouseout", this.handleMouseOut, this);
14158
14159         this.fireEvent("mousedown", this);
14160         this.fireEvent("click", this);
14161         
14162         this.timer = this.click.defer(this.delay || this.interval, this);
14163     },
14164
14165     // private
14166     click : function(){
14167         this.fireEvent("click", this);
14168         this.timer = this.click.defer(this.getInterval(), this);
14169     },
14170
14171     // private
14172     getInterval: function(){
14173         if(!this.accelerate){
14174             return this.interval;
14175         }
14176         var pressTime = this.mousedownTime.getElapsed();
14177         if(pressTime < 500){
14178             return 400;
14179         }else if(pressTime < 1700){
14180             return 320;
14181         }else if(pressTime < 2600){
14182             return 250;
14183         }else if(pressTime < 3500){
14184             return 180;
14185         }else if(pressTime < 4400){
14186             return 140;
14187         }else if(pressTime < 5300){
14188             return 80;
14189         }else if(pressTime < 6200){
14190             return 50;
14191         }else{
14192             return 10;
14193         }
14194     },
14195
14196     // private
14197     handleMouseOut : function(){
14198         clearTimeout(this.timer);
14199         if(this.pressClass){
14200             this.el.removeClass(this.pressClass);
14201         }
14202         this.el.on("mouseover", this.handleMouseReturn, this);
14203     },
14204
14205     // private
14206     handleMouseReturn : function(){
14207         this.el.un("mouseover", this.handleMouseReturn);
14208         if(this.pressClass){
14209             this.el.addClass(this.pressClass);
14210         }
14211         this.click();
14212     },
14213
14214     // private
14215     handleMouseUp : function(){
14216         clearTimeout(this.timer);
14217         this.el.un("mouseover", this.handleMouseReturn);
14218         this.el.un("mouseout", this.handleMouseOut);
14219         Roo.get(document).un("mouseup", this.handleMouseUp);
14220         this.el.removeClass(this.pressClass);
14221         this.fireEvent("mouseup", this);
14222     }
14223 });/*
14224  * Based on:
14225  * Ext JS Library 1.1.1
14226  * Copyright(c) 2006-2007, Ext JS, LLC.
14227  *
14228  * Originally Released Under LGPL - original licence link has changed is not relivant.
14229  *
14230  * Fork - LGPL
14231  * <script type="text/javascript">
14232  */
14233
14234  
14235 /**
14236  * @class Roo.KeyNav
14237  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14238  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14239  * way to implement custom navigation schemes for any UI component.</p>
14240  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14241  * pageUp, pageDown, del, home, end.  Usage:</p>
14242  <pre><code>
14243 var nav = new Roo.KeyNav("my-element", {
14244     "left" : function(e){
14245         this.moveLeft(e.ctrlKey);
14246     },
14247     "right" : function(e){
14248         this.moveRight(e.ctrlKey);
14249     },
14250     "enter" : function(e){
14251         this.save();
14252     },
14253     scope : this
14254 });
14255 </code></pre>
14256  * @constructor
14257  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14258  * @param {Object} config The config
14259  */
14260 Roo.KeyNav = function(el, config){
14261     this.el = Roo.get(el);
14262     Roo.apply(this, config);
14263     if(!this.disabled){
14264         this.disabled = true;
14265         this.enable();
14266     }
14267 };
14268
14269 Roo.KeyNav.prototype = {
14270     /**
14271      * @cfg {Boolean} disabled
14272      * True to disable this KeyNav instance (defaults to false)
14273      */
14274     disabled : false,
14275     /**
14276      * @cfg {String} defaultEventAction
14277      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14278      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14279      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14280      */
14281     defaultEventAction: "stopEvent",
14282     /**
14283      * @cfg {Boolean} forceKeyDown
14284      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14285      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14286      * handle keydown instead of keypress.
14287      */
14288     forceKeyDown : false,
14289
14290     // private
14291     prepareEvent : function(e){
14292         var k = e.getKey();
14293         var h = this.keyToHandler[k];
14294         //if(h && this[h]){
14295         //    e.stopPropagation();
14296         //}
14297         if(Roo.isSafari && h && k >= 37 && k <= 40){
14298             e.stopEvent();
14299         }
14300     },
14301
14302     // private
14303     relay : function(e){
14304         var k = e.getKey();
14305         var h = this.keyToHandler[k];
14306         if(h && this[h]){
14307             if(this.doRelay(e, this[h], h) !== true){
14308                 e[this.defaultEventAction]();
14309             }
14310         }
14311     },
14312
14313     // private
14314     doRelay : function(e, h, hname){
14315         return h.call(this.scope || this, e);
14316     },
14317
14318     // possible handlers
14319     enter : false,
14320     left : false,
14321     right : false,
14322     up : false,
14323     down : false,
14324     tab : false,
14325     esc : false,
14326     pageUp : false,
14327     pageDown : false,
14328     del : false,
14329     home : false,
14330     end : false,
14331
14332     // quick lookup hash
14333     keyToHandler : {
14334         37 : "left",
14335         39 : "right",
14336         38 : "up",
14337         40 : "down",
14338         33 : "pageUp",
14339         34 : "pageDown",
14340         46 : "del",
14341         36 : "home",
14342         35 : "end",
14343         13 : "enter",
14344         27 : "esc",
14345         9  : "tab"
14346     },
14347
14348         /**
14349          * Enable this KeyNav
14350          */
14351         enable: function(){
14352                 if(this.disabled){
14353             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14354             // the EventObject will normalize Safari automatically
14355             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14356                 this.el.on("keydown", this.relay,  this);
14357             }else{
14358                 this.el.on("keydown", this.prepareEvent,  this);
14359                 this.el.on("keypress", this.relay,  this);
14360             }
14361                     this.disabled = false;
14362                 }
14363         },
14364
14365         /**
14366          * Disable this KeyNav
14367          */
14368         disable: function(){
14369                 if(!this.disabled){
14370                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14371                 this.el.un("keydown", this.relay);
14372             }else{
14373                 this.el.un("keydown", this.prepareEvent);
14374                 this.el.un("keypress", this.relay);
14375             }
14376                     this.disabled = true;
14377                 }
14378         }
14379 };/*
14380  * Based on:
14381  * Ext JS Library 1.1.1
14382  * Copyright(c) 2006-2007, Ext JS, LLC.
14383  *
14384  * Originally Released Under LGPL - original licence link has changed is not relivant.
14385  *
14386  * Fork - LGPL
14387  * <script type="text/javascript">
14388  */
14389
14390  
14391 /**
14392  * @class Roo.KeyMap
14393  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14394  * The constructor accepts the same config object as defined by {@link #addBinding}.
14395  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14396  * combination it will call the function with this signature (if the match is a multi-key
14397  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14398  * A KeyMap can also handle a string representation of keys.<br />
14399  * Usage:
14400  <pre><code>
14401 // map one key by key code
14402 var map = new Roo.KeyMap("my-element", {
14403     key: 13, // or Roo.EventObject.ENTER
14404     fn: myHandler,
14405     scope: myObject
14406 });
14407
14408 // map multiple keys to one action by string
14409 var map = new Roo.KeyMap("my-element", {
14410     key: "a\r\n\t",
14411     fn: myHandler,
14412     scope: myObject
14413 });
14414
14415 // map multiple keys to multiple actions by strings and array of codes
14416 var map = new Roo.KeyMap("my-element", [
14417     {
14418         key: [10,13],
14419         fn: function(){ alert("Return was pressed"); }
14420     }, {
14421         key: "abc",
14422         fn: function(){ alert('a, b or c was pressed'); }
14423     }, {
14424         key: "\t",
14425         ctrl:true,
14426         shift:true,
14427         fn: function(){ alert('Control + shift + tab was pressed.'); }
14428     }
14429 ]);
14430 </code></pre>
14431  * <b>Note: A KeyMap starts enabled</b>
14432  * @constructor
14433  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14434  * @param {Object} config The config (see {@link #addBinding})
14435  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14436  */
14437 Roo.KeyMap = function(el, config, eventName){
14438     this.el  = Roo.get(el);
14439     this.eventName = eventName || "keydown";
14440     this.bindings = [];
14441     if(config){
14442         this.addBinding(config);
14443     }
14444     this.enable();
14445 };
14446
14447 Roo.KeyMap.prototype = {
14448     /**
14449      * True to stop the event from bubbling and prevent the default browser action if the
14450      * key was handled by the KeyMap (defaults to false)
14451      * @type Boolean
14452      */
14453     stopEvent : false,
14454
14455     /**
14456      * Add a new binding to this KeyMap. The following config object properties are supported:
14457      * <pre>
14458 Property    Type             Description
14459 ----------  ---------------  ----------------------------------------------------------------------
14460 key         String/Array     A single keycode or an array of keycodes to handle
14461 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14462 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14463 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14464 fn          Function         The function to call when KeyMap finds the expected key combination
14465 scope       Object           The scope of the callback function
14466 </pre>
14467      *
14468      * Usage:
14469      * <pre><code>
14470 // Create a KeyMap
14471 var map = new Roo.KeyMap(document, {
14472     key: Roo.EventObject.ENTER,
14473     fn: handleKey,
14474     scope: this
14475 });
14476
14477 //Add a new binding to the existing KeyMap later
14478 map.addBinding({
14479     key: 'abc',
14480     shift: true,
14481     fn: handleKey,
14482     scope: this
14483 });
14484 </code></pre>
14485      * @param {Object/Array} config A single KeyMap config or an array of configs
14486      */
14487         addBinding : function(config){
14488         if(config instanceof Array){
14489             for(var i = 0, len = config.length; i < len; i++){
14490                 this.addBinding(config[i]);
14491             }
14492             return;
14493         }
14494         var keyCode = config.key,
14495             shift = config.shift, 
14496             ctrl = config.ctrl, 
14497             alt = config.alt,
14498             fn = config.fn,
14499             scope = config.scope;
14500         if(typeof keyCode == "string"){
14501             var ks = [];
14502             var keyString = keyCode.toUpperCase();
14503             for(var j = 0, len = keyString.length; j < len; j++){
14504                 ks.push(keyString.charCodeAt(j));
14505             }
14506             keyCode = ks;
14507         }
14508         var keyArray = keyCode instanceof Array;
14509         var handler = function(e){
14510             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14511                 var k = e.getKey();
14512                 if(keyArray){
14513                     for(var i = 0, len = keyCode.length; i < len; i++){
14514                         if(keyCode[i] == k){
14515                           if(this.stopEvent){
14516                               e.stopEvent();
14517                           }
14518                           fn.call(scope || window, k, e);
14519                           return;
14520                         }
14521                     }
14522                 }else{
14523                     if(k == keyCode){
14524                         if(this.stopEvent){
14525                            e.stopEvent();
14526                         }
14527                         fn.call(scope || window, k, e);
14528                     }
14529                 }
14530             }
14531         };
14532         this.bindings.push(handler);  
14533         },
14534
14535     /**
14536      * Shorthand for adding a single key listener
14537      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14538      * following options:
14539      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14540      * @param {Function} fn The function to call
14541      * @param {Object} scope (optional) The scope of the function
14542      */
14543     on : function(key, fn, scope){
14544         var keyCode, shift, ctrl, alt;
14545         if(typeof key == "object" && !(key instanceof Array)){
14546             keyCode = key.key;
14547             shift = key.shift;
14548             ctrl = key.ctrl;
14549             alt = key.alt;
14550         }else{
14551             keyCode = key;
14552         }
14553         this.addBinding({
14554             key: keyCode,
14555             shift: shift,
14556             ctrl: ctrl,
14557             alt: alt,
14558             fn: fn,
14559             scope: scope
14560         })
14561     },
14562
14563     // private
14564     handleKeyDown : function(e){
14565             if(this.enabled){ //just in case
14566             var b = this.bindings;
14567             for(var i = 0, len = b.length; i < len; i++){
14568                 b[i].call(this, e);
14569             }
14570             }
14571         },
14572         
14573         /**
14574          * Returns true if this KeyMap is enabled
14575          * @return {Boolean} 
14576          */
14577         isEnabled : function(){
14578             return this.enabled;  
14579         },
14580         
14581         /**
14582          * Enables this KeyMap
14583          */
14584         enable: function(){
14585                 if(!this.enabled){
14586                     this.el.on(this.eventName, this.handleKeyDown, this);
14587                     this.enabled = true;
14588                 }
14589         },
14590
14591         /**
14592          * Disable this KeyMap
14593          */
14594         disable: function(){
14595                 if(this.enabled){
14596                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14597                     this.enabled = false;
14598                 }
14599         }
14600 };/*
14601  * Based on:
14602  * Ext JS Library 1.1.1
14603  * Copyright(c) 2006-2007, Ext JS, LLC.
14604  *
14605  * Originally Released Under LGPL - original licence link has changed is not relivant.
14606  *
14607  * Fork - LGPL
14608  * <script type="text/javascript">
14609  */
14610
14611  
14612 /**
14613  * @class Roo.util.TextMetrics
14614  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14615  * wide, in pixels, a given block of text will be.
14616  * @singleton
14617  */
14618 Roo.util.TextMetrics = function(){
14619     var shared;
14620     return {
14621         /**
14622          * Measures the size of the specified text
14623          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14624          * that can affect the size of the rendered text
14625          * @param {String} text The text to measure
14626          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14627          * in order to accurately measure the text height
14628          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14629          */
14630         measure : function(el, text, fixedWidth){
14631             if(!shared){
14632                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14633             }
14634             shared.bind(el);
14635             shared.setFixedWidth(fixedWidth || 'auto');
14636             return shared.getSize(text);
14637         },
14638
14639         /**
14640          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14641          * the overhead of multiple calls to initialize the style properties on each measurement.
14642          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14643          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14644          * in order to accurately measure the text height
14645          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14646          */
14647         createInstance : function(el, fixedWidth){
14648             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14649         }
14650     };
14651 }();
14652
14653  
14654
14655 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14656     var ml = new Roo.Element(document.createElement('div'));
14657     document.body.appendChild(ml.dom);
14658     ml.position('absolute');
14659     ml.setLeftTop(-1000, -1000);
14660     ml.hide();
14661
14662     if(fixedWidth){
14663         ml.setWidth(fixedWidth);
14664     }
14665      
14666     var instance = {
14667         /**
14668          * Returns the size of the specified text based on the internal element's style and width properties
14669          * @memberOf Roo.util.TextMetrics.Instance#
14670          * @param {String} text The text to measure
14671          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14672          */
14673         getSize : function(text){
14674             ml.update(text);
14675             var s = ml.getSize();
14676             ml.update('');
14677             return s;
14678         },
14679
14680         /**
14681          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14682          * that can affect the size of the rendered text
14683          * @memberOf Roo.util.TextMetrics.Instance#
14684          * @param {String/HTMLElement} el The element, dom node or id
14685          */
14686         bind : function(el){
14687             ml.setStyle(
14688                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14689             );
14690         },
14691
14692         /**
14693          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14694          * to set a fixed width in order to accurately measure the text height.
14695          * @memberOf Roo.util.TextMetrics.Instance#
14696          * @param {Number} width The width to set on the element
14697          */
14698         setFixedWidth : function(width){
14699             ml.setWidth(width);
14700         },
14701
14702         /**
14703          * Returns the measured width of the specified text
14704          * @memberOf Roo.util.TextMetrics.Instance#
14705          * @param {String} text The text to measure
14706          * @return {Number} width The width in pixels
14707          */
14708         getWidth : function(text){
14709             ml.dom.style.width = 'auto';
14710             return this.getSize(text).width;
14711         },
14712
14713         /**
14714          * Returns the measured height of the specified text.  For multiline text, be sure to call
14715          * {@link #setFixedWidth} if necessary.
14716          * @memberOf Roo.util.TextMetrics.Instance#
14717          * @param {String} text The text to measure
14718          * @return {Number} height The height in pixels
14719          */
14720         getHeight : function(text){
14721             return this.getSize(text).height;
14722         }
14723     };
14724
14725     instance.bind(bindTo);
14726
14727     return instance;
14728 };
14729
14730 // backwards compat
14731 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14732  * Based on:
14733  * Ext JS Library 1.1.1
14734  * Copyright(c) 2006-2007, Ext JS, LLC.
14735  *
14736  * Originally Released Under LGPL - original licence link has changed is not relivant.
14737  *
14738  * Fork - LGPL
14739  * <script type="text/javascript">
14740  */
14741
14742 /**
14743  * @class Roo.state.Provider
14744  * Abstract base class for state provider implementations. This class provides methods
14745  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14746  * Provider interface.
14747  */
14748 Roo.state.Provider = function(){
14749     /**
14750      * @event statechange
14751      * Fires when a state change occurs.
14752      * @param {Provider} this This state provider
14753      * @param {String} key The state key which was changed
14754      * @param {String} value The encoded value for the state
14755      */
14756     this.addEvents({
14757         "statechange": true
14758     });
14759     this.state = {};
14760     Roo.state.Provider.superclass.constructor.call(this);
14761 };
14762 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14763     /**
14764      * Returns the current value for a key
14765      * @param {String} name The key name
14766      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14767      * @return {Mixed} The state data
14768      */
14769     get : function(name, defaultValue){
14770         return typeof this.state[name] == "undefined" ?
14771             defaultValue : this.state[name];
14772     },
14773     
14774     /**
14775      * Clears a value from the state
14776      * @param {String} name The key name
14777      */
14778     clear : function(name){
14779         delete this.state[name];
14780         this.fireEvent("statechange", this, name, null);
14781     },
14782     
14783     /**
14784      * Sets the value for a key
14785      * @param {String} name The key name
14786      * @param {Mixed} value The value to set
14787      */
14788     set : function(name, value){
14789         this.state[name] = value;
14790         this.fireEvent("statechange", this, name, value);
14791     },
14792     
14793     /**
14794      * Decodes a string previously encoded with {@link #encodeValue}.
14795      * @param {String} value The value to decode
14796      * @return {Mixed} The decoded value
14797      */
14798     decodeValue : function(cookie){
14799         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14800         var matches = re.exec(unescape(cookie));
14801         if(!matches || !matches[1]) return; // non state cookie
14802         var type = matches[1];
14803         var v = matches[2];
14804         switch(type){
14805             case "n":
14806                 return parseFloat(v);
14807             case "d":
14808                 return new Date(Date.parse(v));
14809             case "b":
14810                 return (v == "1");
14811             case "a":
14812                 var all = [];
14813                 var values = v.split("^");
14814                 for(var i = 0, len = values.length; i < len; i++){
14815                     all.push(this.decodeValue(values[i]));
14816                 }
14817                 return all;
14818            case "o":
14819                 var all = {};
14820                 var values = v.split("^");
14821                 for(var i = 0, len = values.length; i < len; i++){
14822                     var kv = values[i].split("=");
14823                     all[kv[0]] = this.decodeValue(kv[1]);
14824                 }
14825                 return all;
14826            default:
14827                 return v;
14828         }
14829     },
14830     
14831     /**
14832      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14833      * @param {Mixed} value The value to encode
14834      * @return {String} The encoded value
14835      */
14836     encodeValue : function(v){
14837         var enc;
14838         if(typeof v == "number"){
14839             enc = "n:" + v;
14840         }else if(typeof v == "boolean"){
14841             enc = "b:" + (v ? "1" : "0");
14842         }else if(v instanceof Date){
14843             enc = "d:" + v.toGMTString();
14844         }else if(v instanceof Array){
14845             var flat = "";
14846             for(var i = 0, len = v.length; i < len; i++){
14847                 flat += this.encodeValue(v[i]);
14848                 if(i != len-1) flat += "^";
14849             }
14850             enc = "a:" + flat;
14851         }else if(typeof v == "object"){
14852             var flat = "";
14853             for(var key in v){
14854                 if(typeof v[key] != "function"){
14855                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14856                 }
14857             }
14858             enc = "o:" + flat.substring(0, flat.length-1);
14859         }else{
14860             enc = "s:" + v;
14861         }
14862         return escape(enc);        
14863     }
14864 });
14865
14866 /*
14867  * Based on:
14868  * Ext JS Library 1.1.1
14869  * Copyright(c) 2006-2007, Ext JS, LLC.
14870  *
14871  * Originally Released Under LGPL - original licence link has changed is not relivant.
14872  *
14873  * Fork - LGPL
14874  * <script type="text/javascript">
14875  */
14876 /**
14877  * @class Roo.state.Manager
14878  * This is the global state manager. By default all components that are "state aware" check this class
14879  * for state information if you don't pass them a custom state provider. In order for this class
14880  * to be useful, it must be initialized with a provider when your application initializes.
14881  <pre><code>
14882 // in your initialization function
14883 init : function(){
14884    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14885    ...
14886    // supposed you have a {@link Roo.BorderLayout}
14887    var layout = new Roo.BorderLayout(...);
14888    layout.restoreState();
14889    // or a {Roo.BasicDialog}
14890    var dialog = new Roo.BasicDialog(...);
14891    dialog.restoreState();
14892  </code></pre>
14893  * @singleton
14894  */
14895 Roo.state.Manager = function(){
14896     var provider = new Roo.state.Provider();
14897     
14898     return {
14899         /**
14900          * Configures the default state provider for your application
14901          * @param {Provider} stateProvider The state provider to set
14902          */
14903         setProvider : function(stateProvider){
14904             provider = stateProvider;
14905         },
14906         
14907         /**
14908          * Returns the current value for a key
14909          * @param {String} name The key name
14910          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14911          * @return {Mixed} The state data
14912          */
14913         get : function(key, defaultValue){
14914             return provider.get(key, defaultValue);
14915         },
14916         
14917         /**
14918          * Sets the value for a key
14919          * @param {String} name The key name
14920          * @param {Mixed} value The state data
14921          */
14922          set : function(key, value){
14923             provider.set(key, value);
14924         },
14925         
14926         /**
14927          * Clears a value from the state
14928          * @param {String} name The key name
14929          */
14930         clear : function(key){
14931             provider.clear(key);
14932         },
14933         
14934         /**
14935          * Gets the currently configured state provider
14936          * @return {Provider} The state provider
14937          */
14938         getProvider : function(){
14939             return provider;
14940         }
14941     };
14942 }();
14943 /*
14944  * Based on:
14945  * Ext JS Library 1.1.1
14946  * Copyright(c) 2006-2007, Ext JS, LLC.
14947  *
14948  * Originally Released Under LGPL - original licence link has changed is not relivant.
14949  *
14950  * Fork - LGPL
14951  * <script type="text/javascript">
14952  */
14953 /**
14954  * @class Roo.state.CookieProvider
14955  * @extends Roo.state.Provider
14956  * The default Provider implementation which saves state via cookies.
14957  * <br />Usage:
14958  <pre><code>
14959    var cp = new Roo.state.CookieProvider({
14960        path: "/cgi-bin/",
14961        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14962        domain: "roojs.com"
14963    })
14964    Roo.state.Manager.setProvider(cp);
14965  </code></pre>
14966  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14967  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14968  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14969  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14970  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14971  * domain the page is running on including the 'www' like 'www.roojs.com')
14972  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14973  * @constructor
14974  * Create a new CookieProvider
14975  * @param {Object} config The configuration object
14976  */
14977 Roo.state.CookieProvider = function(config){
14978     Roo.state.CookieProvider.superclass.constructor.call(this);
14979     this.path = "/";
14980     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14981     this.domain = null;
14982     this.secure = false;
14983     Roo.apply(this, config);
14984     this.state = this.readCookies();
14985 };
14986
14987 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14988     // private
14989     set : function(name, value){
14990         if(typeof value == "undefined" || value === null){
14991             this.clear(name);
14992             return;
14993         }
14994         this.setCookie(name, value);
14995         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14996     },
14997
14998     // private
14999     clear : function(name){
15000         this.clearCookie(name);
15001         Roo.state.CookieProvider.superclass.clear.call(this, name);
15002     },
15003
15004     // private
15005     readCookies : function(){
15006         var cookies = {};
15007         var c = document.cookie + ";";
15008         var re = /\s?(.*?)=(.*?);/g;
15009         var matches;
15010         while((matches = re.exec(c)) != null){
15011             var name = matches[1];
15012             var value = matches[2];
15013             if(name && name.substring(0,3) == "ys-"){
15014                 cookies[name.substr(3)] = this.decodeValue(value);
15015             }
15016         }
15017         return cookies;
15018     },
15019
15020     // private
15021     setCookie : function(name, value){
15022         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15023            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15024            ((this.path == null) ? "" : ("; path=" + this.path)) +
15025            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15026            ((this.secure == true) ? "; secure" : "");
15027     },
15028
15029     // private
15030     clearCookie : function(name){
15031         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15032            ((this.path == null) ? "" : ("; path=" + this.path)) +
15033            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15034            ((this.secure == true) ? "; secure" : "");
15035     }
15036 });/*
15037  * Based on:
15038  * Ext JS Library 1.1.1
15039  * Copyright(c) 2006-2007, Ext JS, LLC.
15040  *
15041  * Originally Released Under LGPL - original licence link has changed is not relivant.
15042  *
15043  * Fork - LGPL
15044  * <script type="text/javascript">
15045  */
15046  
15047
15048 /**
15049  * @class Roo.ComponentMgr
15050  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15051  * @singleton
15052  */
15053 Roo.ComponentMgr = function(){
15054     var all = new Roo.util.MixedCollection();
15055
15056     return {
15057         /**
15058          * Registers a component.
15059          * @param {Roo.Component} c The component
15060          */
15061         register : function(c){
15062             all.add(c);
15063         },
15064
15065         /**
15066          * Unregisters a component.
15067          * @param {Roo.Component} c The component
15068          */
15069         unregister : function(c){
15070             all.remove(c);
15071         },
15072
15073         /**
15074          * Returns a component by id
15075          * @param {String} id The component id
15076          */
15077         get : function(id){
15078             return all.get(id);
15079         },
15080
15081         /**
15082          * Registers a function that will be called when a specified component is added to ComponentMgr
15083          * @param {String} id The component id
15084          * @param {Funtction} fn The callback function
15085          * @param {Object} scope The scope of the callback
15086          */
15087         onAvailable : function(id, fn, scope){
15088             all.on("add", function(index, o){
15089                 if(o.id == id){
15090                     fn.call(scope || o, o);
15091                     all.un("add", fn, scope);
15092                 }
15093             });
15094         }
15095     };
15096 }();/*
15097  * Based on:
15098  * Ext JS Library 1.1.1
15099  * Copyright(c) 2006-2007, Ext JS, LLC.
15100  *
15101  * Originally Released Under LGPL - original licence link has changed is not relivant.
15102  *
15103  * Fork - LGPL
15104  * <script type="text/javascript">
15105  */
15106  
15107 /**
15108  * @class Roo.Component
15109  * @extends Roo.util.Observable
15110  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15111  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15112  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15113  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15114  * All visual components (widgets) that require rendering into a layout should subclass Component.
15115  * @constructor
15116  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15117  * 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
15118  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15119  */
15120 Roo.Component = function(config){
15121     config = config || {};
15122     if(config.tagName || config.dom || typeof config == "string"){ // element object
15123         config = {el: config, id: config.id || config};
15124     }
15125     this.initialConfig = config;
15126
15127     Roo.apply(this, config);
15128     this.addEvents({
15129         /**
15130          * @event disable
15131          * Fires after the component is disabled.
15132              * @param {Roo.Component} this
15133              */
15134         disable : true,
15135         /**
15136          * @event enable
15137          * Fires after the component is enabled.
15138              * @param {Roo.Component} this
15139              */
15140         enable : true,
15141         /**
15142          * @event beforeshow
15143          * Fires before the component is shown.  Return false to stop the show.
15144              * @param {Roo.Component} this
15145              */
15146         beforeshow : true,
15147         /**
15148          * @event show
15149          * Fires after the component is shown.
15150              * @param {Roo.Component} this
15151              */
15152         show : true,
15153         /**
15154          * @event beforehide
15155          * Fires before the component is hidden. Return false to stop the hide.
15156              * @param {Roo.Component} this
15157              */
15158         beforehide : true,
15159         /**
15160          * @event hide
15161          * Fires after the component is hidden.
15162              * @param {Roo.Component} this
15163              */
15164         hide : true,
15165         /**
15166          * @event beforerender
15167          * Fires before the component is rendered. Return false to stop the render.
15168              * @param {Roo.Component} this
15169              */
15170         beforerender : true,
15171         /**
15172          * @event render
15173          * Fires after the component is rendered.
15174              * @param {Roo.Component} this
15175              */
15176         render : true,
15177         /**
15178          * @event beforedestroy
15179          * Fires before the component is destroyed. Return false to stop the destroy.
15180              * @param {Roo.Component} this
15181              */
15182         beforedestroy : true,
15183         /**
15184          * @event destroy
15185          * Fires after the component is destroyed.
15186              * @param {Roo.Component} this
15187              */
15188         destroy : true
15189     });
15190     if(!this.id){
15191         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15192     }
15193     Roo.ComponentMgr.register(this);
15194     Roo.Component.superclass.constructor.call(this);
15195     this.initComponent();
15196     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15197         this.render(this.renderTo);
15198         delete this.renderTo;
15199     }
15200 };
15201
15202 /** @private */
15203 Roo.Component.AUTO_ID = 1000;
15204
15205 Roo.extend(Roo.Component, Roo.util.Observable, {
15206     /**
15207      * @scope Roo.Component.prototype
15208      * @type {Boolean}
15209      * true if this component is hidden. Read-only.
15210      */
15211     hidden : false,
15212     /**
15213      * @type {Boolean}
15214      * true if this component is disabled. Read-only.
15215      */
15216     disabled : false,
15217     /**
15218      * @type {Boolean}
15219      * true if this component has been rendered. Read-only.
15220      */
15221     rendered : false,
15222     
15223     /** @cfg {String} disableClass
15224      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15225      */
15226     disabledClass : "x-item-disabled",
15227         /** @cfg {Boolean} allowDomMove
15228          * Whether the component can move the Dom node when rendering (defaults to true).
15229          */
15230     allowDomMove : true,
15231     /** @cfg {String} hideMode (display|visibility)
15232      * How this component should hidden. Supported values are
15233      * "visibility" (css visibility), "offsets" (negative offset position) and
15234      * "display" (css display) - defaults to "display".
15235      */
15236     hideMode: 'display',
15237
15238     /** @private */
15239     ctype : "Roo.Component",
15240
15241     /**
15242      * @cfg {String} actionMode 
15243      * which property holds the element that used for  hide() / show() / disable() / enable()
15244      * default is 'el' 
15245      */
15246     actionMode : "el",
15247
15248     /** @private */
15249     getActionEl : function(){
15250         return this[this.actionMode];
15251     },
15252
15253     initComponent : Roo.emptyFn,
15254     /**
15255      * If this is a lazy rendering component, render it to its container element.
15256      * @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.
15257      */
15258     render : function(container, position){
15259         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15260             if(!container && this.el){
15261                 this.el = Roo.get(this.el);
15262                 container = this.el.dom.parentNode;
15263                 this.allowDomMove = false;
15264             }
15265             this.container = Roo.get(container);
15266             this.rendered = true;
15267             if(position !== undefined){
15268                 if(typeof position == 'number'){
15269                     position = this.container.dom.childNodes[position];
15270                 }else{
15271                     position = Roo.getDom(position);
15272                 }
15273             }
15274             this.onRender(this.container, position || null);
15275             if(this.cls){
15276                 this.el.addClass(this.cls);
15277                 delete this.cls;
15278             }
15279             if(this.style){
15280                 this.el.applyStyles(this.style);
15281                 delete this.style;
15282             }
15283             this.fireEvent("render", this);
15284             this.afterRender(this.container);
15285             if(this.hidden){
15286                 this.hide();
15287             }
15288             if(this.disabled){
15289                 this.disable();
15290             }
15291         }
15292         return this;
15293     },
15294
15295     /** @private */
15296     // default function is not really useful
15297     onRender : function(ct, position){
15298         if(this.el){
15299             this.el = Roo.get(this.el);
15300             if(this.allowDomMove !== false){
15301                 ct.dom.insertBefore(this.el.dom, position);
15302             }
15303         }
15304     },
15305
15306     /** @private */
15307     getAutoCreate : function(){
15308         var cfg = typeof this.autoCreate == "object" ?
15309                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15310         if(this.id && !cfg.id){
15311             cfg.id = this.id;
15312         }
15313         return cfg;
15314     },
15315
15316     /** @private */
15317     afterRender : Roo.emptyFn,
15318
15319     /**
15320      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15321      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15322      */
15323     destroy : function(){
15324         if(this.fireEvent("beforedestroy", this) !== false){
15325             this.purgeListeners();
15326             this.beforeDestroy();
15327             if(this.rendered){
15328                 this.el.removeAllListeners();
15329                 this.el.remove();
15330                 if(this.actionMode == "container"){
15331                     this.container.remove();
15332                 }
15333             }
15334             this.onDestroy();
15335             Roo.ComponentMgr.unregister(this);
15336             this.fireEvent("destroy", this);
15337         }
15338     },
15339
15340         /** @private */
15341     beforeDestroy : function(){
15342
15343     },
15344
15345         /** @private */
15346         onDestroy : function(){
15347
15348     },
15349
15350     /**
15351      * Returns the underlying {@link Roo.Element}.
15352      * @return {Roo.Element} The element
15353      */
15354     getEl : function(){
15355         return this.el;
15356     },
15357
15358     /**
15359      * Returns the id of this component.
15360      * @return {String}
15361      */
15362     getId : function(){
15363         return this.id;
15364     },
15365
15366     /**
15367      * Try to focus this component.
15368      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15369      * @return {Roo.Component} this
15370      */
15371     focus : function(selectText){
15372         if(this.rendered){
15373             this.el.focus();
15374             if(selectText === true){
15375                 this.el.dom.select();
15376             }
15377         }
15378         return this;
15379     },
15380
15381     /** @private */
15382     blur : function(){
15383         if(this.rendered){
15384             this.el.blur();
15385         }
15386         return this;
15387     },
15388
15389     /**
15390      * Disable this component.
15391      * @return {Roo.Component} this
15392      */
15393     disable : function(){
15394         if(this.rendered){
15395             this.onDisable();
15396         }
15397         this.disabled = true;
15398         this.fireEvent("disable", this);
15399         return this;
15400     },
15401
15402         // private
15403     onDisable : function(){
15404         this.getActionEl().addClass(this.disabledClass);
15405         this.el.dom.disabled = true;
15406     },
15407
15408     /**
15409      * Enable this component.
15410      * @return {Roo.Component} this
15411      */
15412     enable : function(){
15413         if(this.rendered){
15414             this.onEnable();
15415         }
15416         this.disabled = false;
15417         this.fireEvent("enable", this);
15418         return this;
15419     },
15420
15421         // private
15422     onEnable : function(){
15423         this.getActionEl().removeClass(this.disabledClass);
15424         this.el.dom.disabled = false;
15425     },
15426
15427     /**
15428      * Convenience function for setting disabled/enabled by boolean.
15429      * @param {Boolean} disabled
15430      */
15431     setDisabled : function(disabled){
15432         this[disabled ? "disable" : "enable"]();
15433     },
15434
15435     /**
15436      * Show this component.
15437      * @return {Roo.Component} this
15438      */
15439     show: function(){
15440         if(this.fireEvent("beforeshow", this) !== false){
15441             this.hidden = false;
15442             if(this.rendered){
15443                 this.onShow();
15444             }
15445             this.fireEvent("show", this);
15446         }
15447         return this;
15448     },
15449
15450     // private
15451     onShow : function(){
15452         var ae = this.getActionEl();
15453         if(this.hideMode == 'visibility'){
15454             ae.dom.style.visibility = "visible";
15455         }else if(this.hideMode == 'offsets'){
15456             ae.removeClass('x-hidden');
15457         }else{
15458             ae.dom.style.display = "";
15459         }
15460     },
15461
15462     /**
15463      * Hide this component.
15464      * @return {Roo.Component} this
15465      */
15466     hide: function(){
15467         if(this.fireEvent("beforehide", this) !== false){
15468             this.hidden = true;
15469             if(this.rendered){
15470                 this.onHide();
15471             }
15472             this.fireEvent("hide", this);
15473         }
15474         return this;
15475     },
15476
15477     // private
15478     onHide : function(){
15479         var ae = this.getActionEl();
15480         if(this.hideMode == 'visibility'){
15481             ae.dom.style.visibility = "hidden";
15482         }else if(this.hideMode == 'offsets'){
15483             ae.addClass('x-hidden');
15484         }else{
15485             ae.dom.style.display = "none";
15486         }
15487     },
15488
15489     /**
15490      * Convenience function to hide or show this component by boolean.
15491      * @param {Boolean} visible True to show, false to hide
15492      * @return {Roo.Component} this
15493      */
15494     setVisible: function(visible){
15495         if(visible) {
15496             this.show();
15497         }else{
15498             this.hide();
15499         }
15500         return this;
15501     },
15502
15503     /**
15504      * Returns true if this component is visible.
15505      */
15506     isVisible : function(){
15507         return this.getActionEl().isVisible();
15508     },
15509
15510     cloneConfig : function(overrides){
15511         overrides = overrides || {};
15512         var id = overrides.id || Roo.id();
15513         var cfg = Roo.applyIf(overrides, this.initialConfig);
15514         cfg.id = id; // prevent dup id
15515         return new this.constructor(cfg);
15516     }
15517 });/*
15518  * Based on:
15519  * Ext JS Library 1.1.1
15520  * Copyright(c) 2006-2007, Ext JS, LLC.
15521  *
15522  * Originally Released Under LGPL - original licence link has changed is not relivant.
15523  *
15524  * Fork - LGPL
15525  * <script type="text/javascript">
15526  */
15527
15528 /**
15529  * @class Roo.BoxComponent
15530  * @extends Roo.Component
15531  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15532  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15533  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15534  * layout containers.
15535  * @constructor
15536  * @param {Roo.Element/String/Object} config The configuration options.
15537  */
15538 Roo.BoxComponent = function(config){
15539     Roo.Component.call(this, config);
15540     this.addEvents({
15541         /**
15542          * @event resize
15543          * Fires after the component is resized.
15544              * @param {Roo.Component} this
15545              * @param {Number} adjWidth The box-adjusted width that was set
15546              * @param {Number} adjHeight The box-adjusted height that was set
15547              * @param {Number} rawWidth The width that was originally specified
15548              * @param {Number} rawHeight The height that was originally specified
15549              */
15550         resize : true,
15551         /**
15552          * @event move
15553          * Fires after the component is moved.
15554              * @param {Roo.Component} this
15555              * @param {Number} x The new x position
15556              * @param {Number} y The new y position
15557              */
15558         move : true
15559     });
15560 };
15561
15562 Roo.extend(Roo.BoxComponent, Roo.Component, {
15563     // private, set in afterRender to signify that the component has been rendered
15564     boxReady : false,
15565     // private, used to defer height settings to subclasses
15566     deferHeight: false,
15567     /** @cfg {Number} width
15568      * width (optional) size of component
15569      */
15570      /** @cfg {Number} height
15571      * height (optional) size of component
15572      */
15573      
15574     /**
15575      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15576      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15577      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15578      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15579      * @return {Roo.BoxComponent} this
15580      */
15581     setSize : function(w, h){
15582         // support for standard size objects
15583         if(typeof w == 'object'){
15584             h = w.height;
15585             w = w.width;
15586         }
15587         // not rendered
15588         if(!this.boxReady){
15589             this.width = w;
15590             this.height = h;
15591             return this;
15592         }
15593
15594         // prevent recalcs when not needed
15595         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15596             return this;
15597         }
15598         this.lastSize = {width: w, height: h};
15599
15600         var adj = this.adjustSize(w, h);
15601         var aw = adj.width, ah = adj.height;
15602         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15603             var rz = this.getResizeEl();
15604             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15605                 rz.setSize(aw, ah);
15606             }else if(!this.deferHeight && ah !== undefined){
15607                 rz.setHeight(ah);
15608             }else if(aw !== undefined){
15609                 rz.setWidth(aw);
15610             }
15611             this.onResize(aw, ah, w, h);
15612             this.fireEvent('resize', this, aw, ah, w, h);
15613         }
15614         return this;
15615     },
15616
15617     /**
15618      * Gets the current size of the component's underlying element.
15619      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15620      */
15621     getSize : function(){
15622         return this.el.getSize();
15623     },
15624
15625     /**
15626      * Gets the current XY position of the component's underlying element.
15627      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15628      * @return {Array} The XY position of the element (e.g., [100, 200])
15629      */
15630     getPosition : function(local){
15631         if(local === true){
15632             return [this.el.getLeft(true), this.el.getTop(true)];
15633         }
15634         return this.xy || this.el.getXY();
15635     },
15636
15637     /**
15638      * Gets the current box measurements of the component's underlying element.
15639      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15640      * @returns {Object} box An object in the format {x, y, width, height}
15641      */
15642     getBox : function(local){
15643         var s = this.el.getSize();
15644         if(local){
15645             s.x = this.el.getLeft(true);
15646             s.y = this.el.getTop(true);
15647         }else{
15648             var xy = this.xy || this.el.getXY();
15649             s.x = xy[0];
15650             s.y = xy[1];
15651         }
15652         return s;
15653     },
15654
15655     /**
15656      * Sets the current box measurements of the component's underlying element.
15657      * @param {Object} box An object in the format {x, y, width, height}
15658      * @returns {Roo.BoxComponent} this
15659      */
15660     updateBox : function(box){
15661         this.setSize(box.width, box.height);
15662         this.setPagePosition(box.x, box.y);
15663         return this;
15664     },
15665
15666     // protected
15667     getResizeEl : function(){
15668         return this.resizeEl || this.el;
15669     },
15670
15671     // protected
15672     getPositionEl : function(){
15673         return this.positionEl || this.el;
15674     },
15675
15676     /**
15677      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15678      * This method fires the move event.
15679      * @param {Number} left The new left
15680      * @param {Number} top The new top
15681      * @returns {Roo.BoxComponent} this
15682      */
15683     setPosition : function(x, y){
15684         this.x = x;
15685         this.y = y;
15686         if(!this.boxReady){
15687             return this;
15688         }
15689         var adj = this.adjustPosition(x, y);
15690         var ax = adj.x, ay = adj.y;
15691
15692         var el = this.getPositionEl();
15693         if(ax !== undefined || ay !== undefined){
15694             if(ax !== undefined && ay !== undefined){
15695                 el.setLeftTop(ax, ay);
15696             }else if(ax !== undefined){
15697                 el.setLeft(ax);
15698             }else if(ay !== undefined){
15699                 el.setTop(ay);
15700             }
15701             this.onPosition(ax, ay);
15702             this.fireEvent('move', this, ax, ay);
15703         }
15704         return this;
15705     },
15706
15707     /**
15708      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15709      * This method fires the move event.
15710      * @param {Number} x The new x position
15711      * @param {Number} y The new y position
15712      * @returns {Roo.BoxComponent} this
15713      */
15714     setPagePosition : function(x, y){
15715         this.pageX = x;
15716         this.pageY = y;
15717         if(!this.boxReady){
15718             return;
15719         }
15720         if(x === undefined || y === undefined){ // cannot translate undefined points
15721             return;
15722         }
15723         var p = this.el.translatePoints(x, y);
15724         this.setPosition(p.left, p.top);
15725         return this;
15726     },
15727
15728     // private
15729     onRender : function(ct, position){
15730         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15731         if(this.resizeEl){
15732             this.resizeEl = Roo.get(this.resizeEl);
15733         }
15734         if(this.positionEl){
15735             this.positionEl = Roo.get(this.positionEl);
15736         }
15737     },
15738
15739     // private
15740     afterRender : function(){
15741         Roo.BoxComponent.superclass.afterRender.call(this);
15742         this.boxReady = true;
15743         this.setSize(this.width, this.height);
15744         if(this.x || this.y){
15745             this.setPosition(this.x, this.y);
15746         }
15747         if(this.pageX || this.pageY){
15748             this.setPagePosition(this.pageX, this.pageY);
15749         }
15750     },
15751
15752     /**
15753      * Force the component's size to recalculate based on the underlying element's current height and width.
15754      * @returns {Roo.BoxComponent} this
15755      */
15756     syncSize : function(){
15757         delete this.lastSize;
15758         this.setSize(this.el.getWidth(), this.el.getHeight());
15759         return this;
15760     },
15761
15762     /**
15763      * Called after the component is resized, this method is empty by default but can be implemented by any
15764      * subclass that needs to perform custom logic after a resize occurs.
15765      * @param {Number} adjWidth The box-adjusted width that was set
15766      * @param {Number} adjHeight The box-adjusted height that was set
15767      * @param {Number} rawWidth The width that was originally specified
15768      * @param {Number} rawHeight The height that was originally specified
15769      */
15770     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15771
15772     },
15773
15774     /**
15775      * Called after the component is moved, this method is empty by default but can be implemented by any
15776      * subclass that needs to perform custom logic after a move occurs.
15777      * @param {Number} x The new x position
15778      * @param {Number} y The new y position
15779      */
15780     onPosition : function(x, y){
15781
15782     },
15783
15784     // private
15785     adjustSize : function(w, h){
15786         if(this.autoWidth){
15787             w = 'auto';
15788         }
15789         if(this.autoHeight){
15790             h = 'auto';
15791         }
15792         return {width : w, height: h};
15793     },
15794
15795     // private
15796     adjustPosition : function(x, y){
15797         return {x : x, y: y};
15798     }
15799 });/*
15800  * Original code for Roojs - LGPL
15801  * <script type="text/javascript">
15802  */
15803  
15804 /**
15805  * @class Roo.XComponent
15806  * A delayed Element creator...
15807  * Or a way to group chunks of interface together.
15808  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15809  *  used in conjunction with XComponent.build() it will create an instance of each element,
15810  *  then call addxtype() to build the User interface.
15811  * 
15812  * Mypart.xyx = new Roo.XComponent({
15813
15814     parent : 'Mypart.xyz', // empty == document.element.!!
15815     order : '001',
15816     name : 'xxxx'
15817     region : 'xxxx'
15818     disabled : function() {} 
15819      
15820     tree : function() { // return an tree of xtype declared components
15821         var MODULE = this;
15822         return 
15823         {
15824             xtype : 'NestedLayoutPanel',
15825             // technicall
15826         }
15827      ]
15828  *})
15829  *
15830  *
15831  * It can be used to build a big heiracy, with parent etc.
15832  * or you can just use this to render a single compoent to a dom element
15833  * MYPART.render(Roo.Element | String(id) | dom_element )
15834  *
15835  *
15836  * Usage patterns.
15837  *
15838  * Classic Roo
15839  *
15840  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15841  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15842  *
15843  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15844  *
15845  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15846  * - if mulitple topModules exist, the last one is defined as the top module.
15847  *
15848  * Embeded Roo
15849  * 
15850  * When the top level or multiple modules are to embedded into a existing HTML page,
15851  * the parent element can container '#id' of the element where the module will be drawn.
15852  *
15853  * Bootstrap Roo
15854  *
15855  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15856  * it relies more on a include mechanism, where sub modules are included into an outer page.
15857  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15858  * 
15859  * Bootstrap Roo Included elements
15860  *
15861  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15862  * hence confusing the component builder as it thinks there are multiple top level elements. 
15863  *
15864  * 
15865  * 
15866  * @extends Roo.util.Observable
15867  * @constructor
15868  * @param cfg {Object} configuration of component
15869  * 
15870  */
15871 Roo.XComponent = function(cfg) {
15872     Roo.apply(this, cfg);
15873     this.addEvents({ 
15874         /**
15875              * @event built
15876              * Fires when this the componnt is built
15877              * @param {Roo.XComponent} c the component
15878              */
15879         'built' : true
15880         
15881     });
15882     this.region = this.region || 'center'; // default..
15883     Roo.XComponent.register(this);
15884     this.modules = false;
15885     this.el = false; // where the layout goes..
15886     
15887     
15888 }
15889 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15890     /**
15891      * @property el
15892      * The created element (with Roo.factory())
15893      * @type {Roo.Layout}
15894      */
15895     el  : false,
15896     
15897     /**
15898      * @property el
15899      * for BC  - use el in new code
15900      * @type {Roo.Layout}
15901      */
15902     panel : false,
15903     
15904     /**
15905      * @property layout
15906      * for BC  - use el in new code
15907      * @type {Roo.Layout}
15908      */
15909     layout : false,
15910     
15911      /**
15912      * @cfg {Function|boolean} disabled
15913      * If this module is disabled by some rule, return true from the funtion
15914      */
15915     disabled : false,
15916     
15917     /**
15918      * @cfg {String} parent 
15919      * Name of parent element which it get xtype added to..
15920      */
15921     parent: false,
15922     
15923     /**
15924      * @cfg {String} order
15925      * Used to set the order in which elements are created (usefull for multiple tabs)
15926      */
15927     
15928     order : false,
15929     /**
15930      * @cfg {String} name
15931      * String to display while loading.
15932      */
15933     name : false,
15934     /**
15935      * @cfg {String} region
15936      * Region to render component to (defaults to center)
15937      */
15938     region : 'center',
15939     
15940     /**
15941      * @cfg {Array} items
15942      * A single item array - the first element is the root of the tree..
15943      * It's done this way to stay compatible with the Xtype system...
15944      */
15945     items : false,
15946     
15947     /**
15948      * @property _tree
15949      * The method that retuns the tree of parts that make up this compoennt 
15950      * @type {function}
15951      */
15952     _tree  : false,
15953     
15954      /**
15955      * render
15956      * render element to dom or tree
15957      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15958      */
15959     
15960     render : function(el)
15961     {
15962         
15963         el = el || false;
15964         var hp = this.parent ? 1 : 0;
15965         Roo.debug &&  Roo.log(this);
15966         
15967         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15968             // if parent is a '#.....' string, then let's use that..
15969             var ename = this.parent.substr(1);
15970             this.parent = false;
15971             Roo.debug && Roo.log(ename);
15972             switch (ename) {
15973                 case 'bootstrap-body' :
15974                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15975                         this.parent = { el :  new  Roo.bootstrap.Body() };
15976                         Roo.debug && Roo.log("setting el to doc body");
15977                          
15978                     } else {
15979                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15980                     }
15981                     break;
15982                 case 'bootstrap':
15983                     this.parent = { el : true};
15984                     // fall through
15985                 default:
15986                     el = Roo.get(ename);
15987                     break;
15988             }
15989                 
15990             
15991             if (!el && !this.parent) {
15992                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
15993                 return;
15994             }
15995         }
15996         Roo.debug && Roo.log("EL:");
15997         Roo.debug && Roo.log(el);
15998         Roo.debug && Roo.log("this.parent.el:");
15999         Roo.debug && Roo.log(this.parent.el);
16000         
16001         var tree = this._tree ? this._tree() : this.tree();
16002
16003         // altertive root elements ??? - we need a better way to indicate these.
16004         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16005                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16006         
16007         if (!this.parent && is_alt) {
16008             //el = Roo.get(document.body);
16009             this.parent = { el : true };
16010         }
16011             
16012             
16013         
16014         if (!this.parent) {
16015             
16016             Roo.debug && Roo.log("no parent - creating one");
16017             
16018             el = el ? Roo.get(el) : false;      
16019             
16020             // it's a top level one..
16021             this.parent =  {
16022                 el : new Roo.BorderLayout(el || document.body, {
16023                 
16024                      center: {
16025                          titlebar: false,
16026                          autoScroll:false,
16027                          closeOnTab: true,
16028                          tabPosition: 'top',
16029                           //resizeTabs: true,
16030                          alwaysShowTabs: el && hp? false :  true,
16031                          hideTabs: el || !hp ? true :  false,
16032                          minTabWidth: 140
16033                      }
16034                  })
16035             }
16036         }
16037         
16038         if (!this.parent.el) {
16039                 // probably an old style ctor, which has been disabled.
16040                 return;
16041
16042         }
16043                 // The 'tree' method is  '_tree now' 
16044             
16045         tree.region = tree.region || this.region;
16046         
16047         if (this.parent.el === true) {
16048             // bootstrap... - body..
16049             this.parent.el = Roo.factory(tree);
16050         }
16051         
16052         this.el = this.parent.el.addxtype(tree);
16053         this.fireEvent('built', this);
16054         
16055         this.panel = this.el;
16056         this.layout = this.panel.layout;
16057         this.parentLayout = this.parent.layout  || false;  
16058          
16059     }
16060     
16061 });
16062
16063 Roo.apply(Roo.XComponent, {
16064     /**
16065      * @property  hideProgress
16066      * true to disable the building progress bar.. usefull on single page renders.
16067      * @type Boolean
16068      */
16069     hideProgress : false,
16070     /**
16071      * @property  buildCompleted
16072      * True when the builder has completed building the interface.
16073      * @type Boolean
16074      */
16075     buildCompleted : false,
16076      
16077     /**
16078      * @property  topModule
16079      * the upper most module - uses document.element as it's constructor.
16080      * @type Object
16081      */
16082      
16083     topModule  : false,
16084       
16085     /**
16086      * @property  modules
16087      * array of modules to be created by registration system.
16088      * @type {Array} of Roo.XComponent
16089      */
16090     
16091     modules : [],
16092     /**
16093      * @property  elmodules
16094      * array of modules to be created by which use #ID 
16095      * @type {Array} of Roo.XComponent
16096      */
16097      
16098     elmodules : [],
16099
16100      /**
16101      * @property  build_from_html
16102      * Build elements from html - used by bootstrap HTML stuff 
16103      *    - this is cleared after build is completed
16104      * @type {boolean} true  (default false)
16105      */
16106      
16107     build_from_html : false,
16108
16109     /**
16110      * Register components to be built later.
16111      *
16112      * This solves the following issues
16113      * - Building is not done on page load, but after an authentication process has occured.
16114      * - Interface elements are registered on page load
16115      * - Parent Interface elements may not be loaded before child, so this handles that..
16116      * 
16117      *
16118      * example:
16119      * 
16120      * MyApp.register({
16121           order : '000001',
16122           module : 'Pman.Tab.projectMgr',
16123           region : 'center',
16124           parent : 'Pman.layout',
16125           disabled : false,  // or use a function..
16126         })
16127      
16128      * * @param {Object} details about module
16129      */
16130     register : function(obj) {
16131                 
16132         Roo.XComponent.event.fireEvent('register', obj);
16133         switch(typeof(obj.disabled) ) {
16134                 
16135             case 'undefined':
16136                 break;
16137             
16138             case 'function':
16139                 if ( obj.disabled() ) {
16140                         return;
16141                 }
16142                 break;
16143             
16144             default:
16145                 if (obj.disabled) {
16146                         return;
16147                 }
16148                 break;
16149         }
16150                 
16151         this.modules.push(obj);
16152          
16153     },
16154     /**
16155      * convert a string to an object..
16156      * eg. 'AAA.BBB' -> finds AAA.BBB
16157
16158      */
16159     
16160     toObject : function(str)
16161     {
16162         if (!str || typeof(str) == 'object') {
16163             return str;
16164         }
16165         if (str.substring(0,1) == '#') {
16166             return str;
16167         }
16168
16169         var ar = str.split('.');
16170         var rt, o;
16171         rt = ar.shift();
16172             /** eval:var:o */
16173         try {
16174             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16175         } catch (e) {
16176             throw "Module not found : " + str;
16177         }
16178         
16179         if (o === false) {
16180             throw "Module not found : " + str;
16181         }
16182         Roo.each(ar, function(e) {
16183             if (typeof(o[e]) == 'undefined') {
16184                 throw "Module not found : " + str;
16185             }
16186             o = o[e];
16187         });
16188         
16189         return o;
16190         
16191     },
16192     
16193     
16194     /**
16195      * move modules into their correct place in the tree..
16196      * 
16197      */
16198     preBuild : function ()
16199     {
16200         var _t = this;
16201         Roo.each(this.modules , function (obj)
16202         {
16203             Roo.XComponent.event.fireEvent('beforebuild', obj);
16204             
16205             var opar = obj.parent;
16206             try { 
16207                 obj.parent = this.toObject(opar);
16208             } catch(e) {
16209                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16210                 return;
16211             }
16212             
16213             if (!obj.parent) {
16214                 Roo.debug && Roo.log("GOT top level module");
16215                 Roo.debug && Roo.log(obj);
16216                 obj.modules = new Roo.util.MixedCollection(false, 
16217                     function(o) { return o.order + '' }
16218                 );
16219                 this.topModule = obj;
16220                 return;
16221             }
16222                         // parent is a string (usually a dom element name..)
16223             if (typeof(obj.parent) == 'string') {
16224                 this.elmodules.push(obj);
16225                 return;
16226             }
16227             if (obj.parent.constructor != Roo.XComponent) {
16228                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16229             }
16230             if (!obj.parent.modules) {
16231                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16232                     function(o) { return o.order + '' }
16233                 );
16234             }
16235             if (obj.parent.disabled) {
16236                 obj.disabled = true;
16237             }
16238             obj.parent.modules.add(obj);
16239         }, this);
16240     },
16241     
16242      /**
16243      * make a list of modules to build.
16244      * @return {Array} list of modules. 
16245      */ 
16246     
16247     buildOrder : function()
16248     {
16249         var _this = this;
16250         var cmp = function(a,b) {   
16251             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16252         };
16253         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16254             throw "No top level modules to build";
16255         }
16256         
16257         // make a flat list in order of modules to build.
16258         var mods = this.topModule ? [ this.topModule ] : [];
16259                 
16260         
16261         // elmodules (is a list of DOM based modules )
16262         Roo.each(this.elmodules, function(e) {
16263             mods.push(e);
16264             if (!this.topModule &&
16265                 typeof(e.parent) == 'string' &&
16266                 e.parent.substring(0,1) == '#' &&
16267                 Roo.get(e.parent.substr(1))
16268                ) {
16269                 
16270                 _this.topModule = e;
16271             }
16272             
16273         });
16274
16275         
16276         // add modules to their parents..
16277         var addMod = function(m) {
16278             Roo.debug && Roo.log("build Order: add: " + m.name);
16279                 
16280             mods.push(m);
16281             if (m.modules && !m.disabled) {
16282                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16283                 m.modules.keySort('ASC',  cmp );
16284                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16285     
16286                 m.modules.each(addMod);
16287             } else {
16288                 Roo.debug && Roo.log("build Order: no child modules");
16289             }
16290             // not sure if this is used any more..
16291             if (m.finalize) {
16292                 m.finalize.name = m.name + " (clean up) ";
16293                 mods.push(m.finalize);
16294             }
16295             
16296         }
16297         if (this.topModule && this.topModule.modules) { 
16298             this.topModule.modules.keySort('ASC',  cmp );
16299             this.topModule.modules.each(addMod);
16300         } 
16301         return mods;
16302     },
16303     
16304      /**
16305      * Build the registered modules.
16306      * @param {Object} parent element.
16307      * @param {Function} optional method to call after module has been added.
16308      * 
16309      */ 
16310    
16311     build : function(opts) 
16312     {
16313         
16314         if (typeof(opts) != 'undefined') {
16315             Roo.apply(this,opts);
16316         }
16317         
16318         this.preBuild();
16319         var mods = this.buildOrder();
16320       
16321         //this.allmods = mods;
16322         //Roo.debug && Roo.log(mods);
16323         //return;
16324         if (!mods.length) { // should not happen
16325             throw "NO modules!!!";
16326         }
16327         
16328         
16329         var msg = "Building Interface...";
16330         // flash it up as modal - so we store the mask!?
16331         if (!this.hideProgress && Roo.MessageBox) {
16332             Roo.MessageBox.show({ title: 'loading' });
16333             Roo.MessageBox.show({
16334                title: "Please wait...",
16335                msg: msg,
16336                width:450,
16337                progress:true,
16338                closable:false,
16339                modal: false
16340               
16341             });
16342         }
16343         var total = mods.length;
16344         
16345         var _this = this;
16346         var progressRun = function() {
16347             if (!mods.length) {
16348                 Roo.debug && Roo.log('hide?');
16349                 if (!this.hideProgress && Roo.MessageBox) {
16350                     Roo.MessageBox.hide();
16351                 }
16352                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16353                 
16354                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16355                 
16356                 // THE END...
16357                 return false;   
16358             }
16359             
16360             var m = mods.shift();
16361             
16362             
16363             Roo.debug && Roo.log(m);
16364             // not sure if this is supported any more.. - modules that are are just function
16365             if (typeof(m) == 'function') { 
16366                 m.call(this);
16367                 return progressRun.defer(10, _this);
16368             } 
16369             
16370             
16371             msg = "Building Interface " + (total  - mods.length) + 
16372                     " of " + total + 
16373                     (m.name ? (' - ' + m.name) : '');
16374                         Roo.debug && Roo.log(msg);
16375             if (!this.hideProgress &&  Roo.MessageBox) { 
16376                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16377             }
16378             
16379          
16380             // is the module disabled?
16381             var disabled = (typeof(m.disabled) == 'function') ?
16382                 m.disabled.call(m.module.disabled) : m.disabled;    
16383             
16384             
16385             if (disabled) {
16386                 return progressRun(); // we do not update the display!
16387             }
16388             
16389             // now build 
16390             
16391                         
16392                         
16393             m.render();
16394             // it's 10 on top level, and 1 on others??? why...
16395             return progressRun.defer(10, _this);
16396              
16397         }
16398         progressRun.defer(1, _this);
16399      
16400         
16401         
16402     },
16403         
16404         
16405         /**
16406          * Event Object.
16407          *
16408          *
16409          */
16410         event: false, 
16411     /**
16412          * wrapper for event.on - aliased later..  
16413          * Typically use to register a event handler for register:
16414          *
16415          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16416          *
16417          */
16418     on : false
16419    
16420     
16421     
16422 });
16423
16424 Roo.XComponent.event = new Roo.util.Observable({
16425                 events : { 
16426                         /**
16427                          * @event register
16428                          * Fires when an Component is registered,
16429                          * set the disable property on the Component to stop registration.
16430                          * @param {Roo.XComponent} c the component being registerd.
16431                          * 
16432                          */
16433                         'register' : true,
16434             /**
16435                          * @event beforebuild
16436                          * Fires before each Component is built
16437                          * can be used to apply permissions.
16438                          * @param {Roo.XComponent} c the component being registerd.
16439                          * 
16440                          */
16441                         'beforebuild' : true,
16442                         /**
16443                          * @event buildcomplete
16444                          * Fires on the top level element when all elements have been built
16445                          * @param {Roo.XComponent} the top level component.
16446                          */
16447                         'buildcomplete' : true
16448                         
16449                 }
16450 });
16451
16452 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16453  /*
16454  * Based on:
16455  * Ext JS Library 1.1.1
16456  * Copyright(c) 2006-2007, Ext JS, LLC.
16457  *
16458  * Originally Released Under LGPL - original licence link has changed is not relivant.
16459  *
16460  * Fork - LGPL
16461  * <script type="text/javascript">
16462  */
16463
16464
16465
16466 /*
16467  * These classes are derivatives of the similarly named classes in the YUI Library.
16468  * The original license:
16469  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16470  * Code licensed under the BSD License:
16471  * http://developer.yahoo.net/yui/license.txt
16472  */
16473
16474 (function() {
16475
16476 var Event=Roo.EventManager;
16477 var Dom=Roo.lib.Dom;
16478
16479 /**
16480  * @class Roo.dd.DragDrop
16481  * @extends Roo.util.Observable
16482  * Defines the interface and base operation of items that that can be
16483  * dragged or can be drop targets.  It was designed to be extended, overriding
16484  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16485  * Up to three html elements can be associated with a DragDrop instance:
16486  * <ul>
16487  * <li>linked element: the element that is passed into the constructor.
16488  * This is the element which defines the boundaries for interaction with
16489  * other DragDrop objects.</li>
16490  * <li>handle element(s): The drag operation only occurs if the element that
16491  * was clicked matches a handle element.  By default this is the linked
16492  * element, but there are times that you will want only a portion of the
16493  * linked element to initiate the drag operation, and the setHandleElId()
16494  * method provides a way to define this.</li>
16495  * <li>drag element: this represents the element that would be moved along
16496  * with the cursor during a drag operation.  By default, this is the linked
16497  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16498  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16499  * </li>
16500  * </ul>
16501  * This class should not be instantiated until the onload event to ensure that
16502  * the associated elements are available.
16503  * The following would define a DragDrop obj that would interact with any
16504  * other DragDrop obj in the "group1" group:
16505  * <pre>
16506  *  dd = new Roo.dd.DragDrop("div1", "group1");
16507  * </pre>
16508  * Since none of the event handlers have been implemented, nothing would
16509  * actually happen if you were to run the code above.  Normally you would
16510  * override this class or one of the default implementations, but you can
16511  * also override the methods you want on an instance of the class...
16512  * <pre>
16513  *  dd.onDragDrop = function(e, id) {
16514  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16515  *  }
16516  * </pre>
16517  * @constructor
16518  * @param {String} id of the element that is linked to this instance
16519  * @param {String} sGroup the group of related DragDrop objects
16520  * @param {object} config an object containing configurable attributes
16521  *                Valid properties for DragDrop:
16522  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16523  */
16524 Roo.dd.DragDrop = function(id, sGroup, config) {
16525     if (id) {
16526         this.init(id, sGroup, config);
16527     }
16528     
16529 };
16530
16531 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16532
16533     /**
16534      * The id of the element associated with this object.  This is what we
16535      * refer to as the "linked element" because the size and position of
16536      * this element is used to determine when the drag and drop objects have
16537      * interacted.
16538      * @property id
16539      * @type String
16540      */
16541     id: null,
16542
16543     /**
16544      * Configuration attributes passed into the constructor
16545      * @property config
16546      * @type object
16547      */
16548     config: null,
16549
16550     /**
16551      * The id of the element that will be dragged.  By default this is same
16552      * as the linked element , but could be changed to another element. Ex:
16553      * Roo.dd.DDProxy
16554      * @property dragElId
16555      * @type String
16556      * @private
16557      */
16558     dragElId: null,
16559
16560     /**
16561      * the id of the element that initiates the drag operation.  By default
16562      * this is the linked element, but could be changed to be a child of this
16563      * element.  This lets us do things like only starting the drag when the
16564      * header element within the linked html element is clicked.
16565      * @property handleElId
16566      * @type String
16567      * @private
16568      */
16569     handleElId: null,
16570
16571     /**
16572      * An associative array of HTML tags that will be ignored if clicked.
16573      * @property invalidHandleTypes
16574      * @type {string: string}
16575      */
16576     invalidHandleTypes: null,
16577
16578     /**
16579      * An associative array of ids for elements that will be ignored if clicked
16580      * @property invalidHandleIds
16581      * @type {string: string}
16582      */
16583     invalidHandleIds: null,
16584
16585     /**
16586      * An indexted array of css class names for elements that will be ignored
16587      * if clicked.
16588      * @property invalidHandleClasses
16589      * @type string[]
16590      */
16591     invalidHandleClasses: null,
16592
16593     /**
16594      * The linked element's absolute X position at the time the drag was
16595      * started
16596      * @property startPageX
16597      * @type int
16598      * @private
16599      */
16600     startPageX: 0,
16601
16602     /**
16603      * The linked element's absolute X position at the time the drag was
16604      * started
16605      * @property startPageY
16606      * @type int
16607      * @private
16608      */
16609     startPageY: 0,
16610
16611     /**
16612      * The group defines a logical collection of DragDrop objects that are
16613      * related.  Instances only get events when interacting with other
16614      * DragDrop object in the same group.  This lets us define multiple
16615      * groups using a single DragDrop subclass if we want.
16616      * @property groups
16617      * @type {string: string}
16618      */
16619     groups: null,
16620
16621     /**
16622      * Individual drag/drop instances can be locked.  This will prevent
16623      * onmousedown start drag.
16624      * @property locked
16625      * @type boolean
16626      * @private
16627      */
16628     locked: false,
16629
16630     /**
16631      * Lock this instance
16632      * @method lock
16633      */
16634     lock: function() { this.locked = true; },
16635
16636     /**
16637      * Unlock this instace
16638      * @method unlock
16639      */
16640     unlock: function() { this.locked = false; },
16641
16642     /**
16643      * By default, all insances can be a drop target.  This can be disabled by
16644      * setting isTarget to false.
16645      * @method isTarget
16646      * @type boolean
16647      */
16648     isTarget: true,
16649
16650     /**
16651      * The padding configured for this drag and drop object for calculating
16652      * the drop zone intersection with this object.
16653      * @method padding
16654      * @type int[]
16655      */
16656     padding: null,
16657
16658     /**
16659      * Cached reference to the linked element
16660      * @property _domRef
16661      * @private
16662      */
16663     _domRef: null,
16664
16665     /**
16666      * Internal typeof flag
16667      * @property __ygDragDrop
16668      * @private
16669      */
16670     __ygDragDrop: true,
16671
16672     /**
16673      * Set to true when horizontal contraints are applied
16674      * @property constrainX
16675      * @type boolean
16676      * @private
16677      */
16678     constrainX: false,
16679
16680     /**
16681      * Set to true when vertical contraints are applied
16682      * @property constrainY
16683      * @type boolean
16684      * @private
16685      */
16686     constrainY: false,
16687
16688     /**
16689      * The left constraint
16690      * @property minX
16691      * @type int
16692      * @private
16693      */
16694     minX: 0,
16695
16696     /**
16697      * The right constraint
16698      * @property maxX
16699      * @type int
16700      * @private
16701      */
16702     maxX: 0,
16703
16704     /**
16705      * The up constraint
16706      * @property minY
16707      * @type int
16708      * @type int
16709      * @private
16710      */
16711     minY: 0,
16712
16713     /**
16714      * The down constraint
16715      * @property maxY
16716      * @type int
16717      * @private
16718      */
16719     maxY: 0,
16720
16721     /**
16722      * Maintain offsets when we resetconstraints.  Set to true when you want
16723      * the position of the element relative to its parent to stay the same
16724      * when the page changes
16725      *
16726      * @property maintainOffset
16727      * @type boolean
16728      */
16729     maintainOffset: false,
16730
16731     /**
16732      * Array of pixel locations the element will snap to if we specified a
16733      * horizontal graduation/interval.  This array is generated automatically
16734      * when you define a tick interval.
16735      * @property xTicks
16736      * @type int[]
16737      */
16738     xTicks: null,
16739
16740     /**
16741      * Array of pixel locations the element will snap to if we specified a
16742      * vertical graduation/interval.  This array is generated automatically
16743      * when you define a tick interval.
16744      * @property yTicks
16745      * @type int[]
16746      */
16747     yTicks: null,
16748
16749     /**
16750      * By default the drag and drop instance will only respond to the primary
16751      * button click (left button for a right-handed mouse).  Set to true to
16752      * allow drag and drop to start with any mouse click that is propogated
16753      * by the browser
16754      * @property primaryButtonOnly
16755      * @type boolean
16756      */
16757     primaryButtonOnly: true,
16758
16759     /**
16760      * The availabe property is false until the linked dom element is accessible.
16761      * @property available
16762      * @type boolean
16763      */
16764     available: false,
16765
16766     /**
16767      * By default, drags can only be initiated if the mousedown occurs in the
16768      * region the linked element is.  This is done in part to work around a
16769      * bug in some browsers that mis-report the mousedown if the previous
16770      * mouseup happened outside of the window.  This property is set to true
16771      * if outer handles are defined.
16772      *
16773      * @property hasOuterHandles
16774      * @type boolean
16775      * @default false
16776      */
16777     hasOuterHandles: false,
16778
16779     /**
16780      * Code that executes immediately before the startDrag event
16781      * @method b4StartDrag
16782      * @private
16783      */
16784     b4StartDrag: function(x, y) { },
16785
16786     /**
16787      * Abstract method called after a drag/drop object is clicked
16788      * and the drag or mousedown time thresholds have beeen met.
16789      * @method startDrag
16790      * @param {int} X click location
16791      * @param {int} Y click location
16792      */
16793     startDrag: function(x, y) { /* override this */ },
16794
16795     /**
16796      * Code that executes immediately before the onDrag event
16797      * @method b4Drag
16798      * @private
16799      */
16800     b4Drag: function(e) { },
16801
16802     /**
16803      * Abstract method called during the onMouseMove event while dragging an
16804      * object.
16805      * @method onDrag
16806      * @param {Event} e the mousemove event
16807      */
16808     onDrag: function(e) { /* override this */ },
16809
16810     /**
16811      * Abstract method called when this element fist begins hovering over
16812      * another DragDrop obj
16813      * @method onDragEnter
16814      * @param {Event} e the mousemove event
16815      * @param {String|DragDrop[]} id In POINT mode, the element
16816      * id this is hovering over.  In INTERSECT mode, an array of one or more
16817      * dragdrop items being hovered over.
16818      */
16819     onDragEnter: function(e, id) { /* override this */ },
16820
16821     /**
16822      * Code that executes immediately before the onDragOver event
16823      * @method b4DragOver
16824      * @private
16825      */
16826     b4DragOver: function(e) { },
16827
16828     /**
16829      * Abstract method called when this element is hovering over another
16830      * DragDrop obj
16831      * @method onDragOver
16832      * @param {Event} e the mousemove event
16833      * @param {String|DragDrop[]} id In POINT mode, the element
16834      * id this is hovering over.  In INTERSECT mode, an array of dd items
16835      * being hovered over.
16836      */
16837     onDragOver: function(e, id) { /* override this */ },
16838
16839     /**
16840      * Code that executes immediately before the onDragOut event
16841      * @method b4DragOut
16842      * @private
16843      */
16844     b4DragOut: function(e) { },
16845
16846     /**
16847      * Abstract method called when we are no longer hovering over an element
16848      * @method onDragOut
16849      * @param {Event} e the mousemove event
16850      * @param {String|DragDrop[]} id In POINT mode, the element
16851      * id this was hovering over.  In INTERSECT mode, an array of dd items
16852      * that the mouse is no longer over.
16853      */
16854     onDragOut: function(e, id) { /* override this */ },
16855
16856     /**
16857      * Code that executes immediately before the onDragDrop event
16858      * @method b4DragDrop
16859      * @private
16860      */
16861     b4DragDrop: function(e) { },
16862
16863     /**
16864      * Abstract method called when this item is dropped on another DragDrop
16865      * obj
16866      * @method onDragDrop
16867      * @param {Event} e the mouseup event
16868      * @param {String|DragDrop[]} id In POINT mode, the element
16869      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16870      * was dropped on.
16871      */
16872     onDragDrop: function(e, id) { /* override this */ },
16873
16874     /**
16875      * Abstract method called when this item is dropped on an area with no
16876      * drop target
16877      * @method onInvalidDrop
16878      * @param {Event} e the mouseup event
16879      */
16880     onInvalidDrop: function(e) { /* override this */ },
16881
16882     /**
16883      * Code that executes immediately before the endDrag event
16884      * @method b4EndDrag
16885      * @private
16886      */
16887     b4EndDrag: function(e) { },
16888
16889     /**
16890      * Fired when we are done dragging the object
16891      * @method endDrag
16892      * @param {Event} e the mouseup event
16893      */
16894     endDrag: function(e) { /* override this */ },
16895
16896     /**
16897      * Code executed immediately before the onMouseDown event
16898      * @method b4MouseDown
16899      * @param {Event} e the mousedown event
16900      * @private
16901      */
16902     b4MouseDown: function(e) {  },
16903
16904     /**
16905      * Event handler that fires when a drag/drop obj gets a mousedown
16906      * @method onMouseDown
16907      * @param {Event} e the mousedown event
16908      */
16909     onMouseDown: function(e) { /* override this */ },
16910
16911     /**
16912      * Event handler that fires when a drag/drop obj gets a mouseup
16913      * @method onMouseUp
16914      * @param {Event} e the mouseup event
16915      */
16916     onMouseUp: function(e) { /* override this */ },
16917
16918     /**
16919      * Override the onAvailable method to do what is needed after the initial
16920      * position was determined.
16921      * @method onAvailable
16922      */
16923     onAvailable: function () {
16924     },
16925
16926     /*
16927      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16928      * @type Object
16929      */
16930     defaultPadding : {left:0, right:0, top:0, bottom:0},
16931
16932     /*
16933      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16934  *
16935  * Usage:
16936  <pre><code>
16937  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16938                 { dragElId: "existingProxyDiv" });
16939  dd.startDrag = function(){
16940      this.constrainTo("parent-id");
16941  };
16942  </code></pre>
16943  * Or you can initalize it using the {@link Roo.Element} object:
16944  <pre><code>
16945  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16946      startDrag : function(){
16947          this.constrainTo("parent-id");
16948      }
16949  });
16950  </code></pre>
16951      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16952      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16953      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16954      * an object containing the sides to pad. For example: {right:10, bottom:10}
16955      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16956      */
16957     constrainTo : function(constrainTo, pad, inContent){
16958         if(typeof pad == "number"){
16959             pad = {left: pad, right:pad, top:pad, bottom:pad};
16960         }
16961         pad = pad || this.defaultPadding;
16962         var b = Roo.get(this.getEl()).getBox();
16963         var ce = Roo.get(constrainTo);
16964         var s = ce.getScroll();
16965         var c, cd = ce.dom;
16966         if(cd == document.body){
16967             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16968         }else{
16969             xy = ce.getXY();
16970             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16971         }
16972
16973
16974         var topSpace = b.y - c.y;
16975         var leftSpace = b.x - c.x;
16976
16977         this.resetConstraints();
16978         this.setXConstraint(leftSpace - (pad.left||0), // left
16979                 c.width - leftSpace - b.width - (pad.right||0) //right
16980         );
16981         this.setYConstraint(topSpace - (pad.top||0), //top
16982                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16983         );
16984     },
16985
16986     /**
16987      * Returns a reference to the linked element
16988      * @method getEl
16989      * @return {HTMLElement} the html element
16990      */
16991     getEl: function() {
16992         if (!this._domRef) {
16993             this._domRef = Roo.getDom(this.id);
16994         }
16995
16996         return this._domRef;
16997     },
16998
16999     /**
17000      * Returns a reference to the actual element to drag.  By default this is
17001      * the same as the html element, but it can be assigned to another
17002      * element. An example of this can be found in Roo.dd.DDProxy
17003      * @method getDragEl
17004      * @return {HTMLElement} the html element
17005      */
17006     getDragEl: function() {
17007         return Roo.getDom(this.dragElId);
17008     },
17009
17010     /**
17011      * Sets up the DragDrop object.  Must be called in the constructor of any
17012      * Roo.dd.DragDrop subclass
17013      * @method init
17014      * @param id the id of the linked element
17015      * @param {String} sGroup the group of related items
17016      * @param {object} config configuration attributes
17017      */
17018     init: function(id, sGroup, config) {
17019         this.initTarget(id, sGroup, config);
17020         if (!Roo.isTouch) {
17021             Event.on(this.id, "mousedown", this.handleMouseDown, this);
17022         }
17023         Event.on(this.id, "touchstart", this.handleMouseDown, this);
17024         // Event.on(this.id, "selectstart", Event.preventDefault);
17025     },
17026
17027     /**
17028      * Initializes Targeting functionality only... the object does not
17029      * get a mousedown handler.
17030      * @method initTarget
17031      * @param id the id of the linked element
17032      * @param {String} sGroup the group of related items
17033      * @param {object} config configuration attributes
17034      */
17035     initTarget: function(id, sGroup, config) {
17036
17037         // configuration attributes
17038         this.config = config || {};
17039
17040         // create a local reference to the drag and drop manager
17041         this.DDM = Roo.dd.DDM;
17042         // initialize the groups array
17043         this.groups = {};
17044
17045         // assume that we have an element reference instead of an id if the
17046         // parameter is not a string
17047         if (typeof id !== "string") {
17048             id = Roo.id(id);
17049         }
17050
17051         // set the id
17052         this.id = id;
17053
17054         // add to an interaction group
17055         this.addToGroup((sGroup) ? sGroup : "default");
17056
17057         // We don't want to register this as the handle with the manager
17058         // so we just set the id rather than calling the setter.
17059         this.handleElId = id;
17060
17061         // the linked element is the element that gets dragged by default
17062         this.setDragElId(id);
17063
17064         // by default, clicked anchors will not start drag operations.
17065         this.invalidHandleTypes = { A: "A" };
17066         this.invalidHandleIds = {};
17067         this.invalidHandleClasses = [];
17068
17069         this.applyConfig();
17070
17071         this.handleOnAvailable();
17072     },
17073
17074     /**
17075      * Applies the configuration parameters that were passed into the constructor.
17076      * This is supposed to happen at each level through the inheritance chain.  So
17077      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17078      * DragDrop in order to get all of the parameters that are available in
17079      * each object.
17080      * @method applyConfig
17081      */
17082     applyConfig: function() {
17083
17084         // configurable properties:
17085         //    padding, isTarget, maintainOffset, primaryButtonOnly
17086         this.padding           = this.config.padding || [0, 0, 0, 0];
17087         this.isTarget          = (this.config.isTarget !== false);
17088         this.maintainOffset    = (this.config.maintainOffset);
17089         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17090
17091     },
17092
17093     /**
17094      * Executed when the linked element is available
17095      * @method handleOnAvailable
17096      * @private
17097      */
17098     handleOnAvailable: function() {
17099         this.available = true;
17100         this.resetConstraints();
17101         this.onAvailable();
17102     },
17103
17104      /**
17105      * Configures the padding for the target zone in px.  Effectively expands
17106      * (or reduces) the virtual object size for targeting calculations.
17107      * Supports css-style shorthand; if only one parameter is passed, all sides
17108      * will have that padding, and if only two are passed, the top and bottom
17109      * will have the first param, the left and right the second.
17110      * @method setPadding
17111      * @param {int} iTop    Top pad
17112      * @param {int} iRight  Right pad
17113      * @param {int} iBot    Bot pad
17114      * @param {int} iLeft   Left pad
17115      */
17116     setPadding: function(iTop, iRight, iBot, iLeft) {
17117         // this.padding = [iLeft, iRight, iTop, iBot];
17118         if (!iRight && 0 !== iRight) {
17119             this.padding = [iTop, iTop, iTop, iTop];
17120         } else if (!iBot && 0 !== iBot) {
17121             this.padding = [iTop, iRight, iTop, iRight];
17122         } else {
17123             this.padding = [iTop, iRight, iBot, iLeft];
17124         }
17125     },
17126
17127     /**
17128      * Stores the initial placement of the linked element.
17129      * @method setInitialPosition
17130      * @param {int} diffX   the X offset, default 0
17131      * @param {int} diffY   the Y offset, default 0
17132      */
17133     setInitPosition: function(diffX, diffY) {
17134         var el = this.getEl();
17135
17136         if (!this.DDM.verifyEl(el)) {
17137             return;
17138         }
17139
17140         var dx = diffX || 0;
17141         var dy = diffY || 0;
17142
17143         var p = Dom.getXY( el );
17144
17145         this.initPageX = p[0] - dx;
17146         this.initPageY = p[1] - dy;
17147
17148         this.lastPageX = p[0];
17149         this.lastPageY = p[1];
17150
17151
17152         this.setStartPosition(p);
17153     },
17154
17155     /**
17156      * Sets the start position of the element.  This is set when the obj
17157      * is initialized, the reset when a drag is started.
17158      * @method setStartPosition
17159      * @param pos current position (from previous lookup)
17160      * @private
17161      */
17162     setStartPosition: function(pos) {
17163         var p = pos || Dom.getXY( this.getEl() );
17164         this.deltaSetXY = null;
17165
17166         this.startPageX = p[0];
17167         this.startPageY = p[1];
17168     },
17169
17170     /**
17171      * Add this instance to a group of related drag/drop objects.  All
17172      * instances belong to at least one group, and can belong to as many
17173      * groups as needed.
17174      * @method addToGroup
17175      * @param sGroup {string} the name of the group
17176      */
17177     addToGroup: function(sGroup) {
17178         this.groups[sGroup] = true;
17179         this.DDM.regDragDrop(this, sGroup);
17180     },
17181
17182     /**
17183      * Remove's this instance from the supplied interaction group
17184      * @method removeFromGroup
17185      * @param {string}  sGroup  The group to drop
17186      */
17187     removeFromGroup: function(sGroup) {
17188         if (this.groups[sGroup]) {
17189             delete this.groups[sGroup];
17190         }
17191
17192         this.DDM.removeDDFromGroup(this, sGroup);
17193     },
17194
17195     /**
17196      * Allows you to specify that an element other than the linked element
17197      * will be moved with the cursor during a drag
17198      * @method setDragElId
17199      * @param id {string} the id of the element that will be used to initiate the drag
17200      */
17201     setDragElId: function(id) {
17202         this.dragElId = id;
17203     },
17204
17205     /**
17206      * Allows you to specify a child of the linked element that should be
17207      * used to initiate the drag operation.  An example of this would be if
17208      * you have a content div with text and links.  Clicking anywhere in the
17209      * content area would normally start the drag operation.  Use this method
17210      * to specify that an element inside of the content div is the element
17211      * that starts the drag operation.
17212      * @method setHandleElId
17213      * @param id {string} the id of the element that will be used to
17214      * initiate the drag.
17215      */
17216     setHandleElId: function(id) {
17217         if (typeof id !== "string") {
17218             id = Roo.id(id);
17219         }
17220         this.handleElId = id;
17221         this.DDM.regHandle(this.id, id);
17222     },
17223
17224     /**
17225      * Allows you to set an element outside of the linked element as a drag
17226      * handle
17227      * @method setOuterHandleElId
17228      * @param id the id of the element that will be used to initiate the drag
17229      */
17230     setOuterHandleElId: function(id) {
17231         if (typeof id !== "string") {
17232             id = Roo.id(id);
17233         }
17234         Event.on(id, "mousedown",
17235                 this.handleMouseDown, this);
17236         this.setHandleElId(id);
17237
17238         this.hasOuterHandles = true;
17239     },
17240
17241     /**
17242      * Remove all drag and drop hooks for this element
17243      * @method unreg
17244      */
17245     unreg: function() {
17246         Event.un(this.id, "mousedown",
17247                 this.handleMouseDown);
17248         Event.un(this.id, "touchstart",
17249                 this.handleMouseDown);
17250         this._domRef = null;
17251         this.DDM._remove(this);
17252     },
17253
17254     destroy : function(){
17255         this.unreg();
17256     },
17257
17258     /**
17259      * Returns true if this instance is locked, or the drag drop mgr is locked
17260      * (meaning that all drag/drop is disabled on the page.)
17261      * @method isLocked
17262      * @return {boolean} true if this obj or all drag/drop is locked, else
17263      * false
17264      */
17265     isLocked: function() {
17266         return (this.DDM.isLocked() || this.locked);
17267     },
17268
17269     /**
17270      * Fired when this object is clicked
17271      * @method handleMouseDown
17272      * @param {Event} e
17273      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17274      * @private
17275      */
17276     handleMouseDown: function(e, oDD){
17277      
17278         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17279             //Roo.log('not touch/ button !=0');
17280             return;
17281         }
17282         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17283             return; // double touch..
17284         }
17285         
17286
17287         if (this.isLocked()) {
17288             //Roo.log('locked');
17289             return;
17290         }
17291
17292         this.DDM.refreshCache(this.groups);
17293 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17294         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17295         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17296             //Roo.log('no outer handes or not over target');
17297                 // do nothing.
17298         } else {
17299 //            Roo.log('check validator');
17300             if (this.clickValidator(e)) {
17301 //                Roo.log('validate success');
17302                 // set the initial element position
17303                 this.setStartPosition();
17304
17305
17306                 this.b4MouseDown(e);
17307                 this.onMouseDown(e);
17308
17309                 this.DDM.handleMouseDown(e, this);
17310
17311                 this.DDM.stopEvent(e);
17312             } else {
17313
17314
17315             }
17316         }
17317     },
17318
17319     clickValidator: function(e) {
17320         var target = e.getTarget();
17321         return ( this.isValidHandleChild(target) &&
17322                     (this.id == this.handleElId ||
17323                         this.DDM.handleWasClicked(target, this.id)) );
17324     },
17325
17326     /**
17327      * Allows you to specify a tag name that should not start a drag operation
17328      * when clicked.  This is designed to facilitate embedding links within a
17329      * drag handle that do something other than start the drag.
17330      * @method addInvalidHandleType
17331      * @param {string} tagName the type of element to exclude
17332      */
17333     addInvalidHandleType: function(tagName) {
17334         var type = tagName.toUpperCase();
17335         this.invalidHandleTypes[type] = type;
17336     },
17337
17338     /**
17339      * Lets you to specify an element id for a child of a drag handle
17340      * that should not initiate a drag
17341      * @method addInvalidHandleId
17342      * @param {string} id the element id of the element you wish to ignore
17343      */
17344     addInvalidHandleId: function(id) {
17345         if (typeof id !== "string") {
17346             id = Roo.id(id);
17347         }
17348         this.invalidHandleIds[id] = id;
17349     },
17350
17351     /**
17352      * Lets you specify a css class of elements that will not initiate a drag
17353      * @method addInvalidHandleClass
17354      * @param {string} cssClass the class of the elements you wish to ignore
17355      */
17356     addInvalidHandleClass: function(cssClass) {
17357         this.invalidHandleClasses.push(cssClass);
17358     },
17359
17360     /**
17361      * Unsets an excluded tag name set by addInvalidHandleType
17362      * @method removeInvalidHandleType
17363      * @param {string} tagName the type of element to unexclude
17364      */
17365     removeInvalidHandleType: function(tagName) {
17366         var type = tagName.toUpperCase();
17367         // this.invalidHandleTypes[type] = null;
17368         delete this.invalidHandleTypes[type];
17369     },
17370
17371     /**
17372      * Unsets an invalid handle id
17373      * @method removeInvalidHandleId
17374      * @param {string} id the id of the element to re-enable
17375      */
17376     removeInvalidHandleId: function(id) {
17377         if (typeof id !== "string") {
17378             id = Roo.id(id);
17379         }
17380         delete this.invalidHandleIds[id];
17381     },
17382
17383     /**
17384      * Unsets an invalid css class
17385      * @method removeInvalidHandleClass
17386      * @param {string} cssClass the class of the element(s) you wish to
17387      * re-enable
17388      */
17389     removeInvalidHandleClass: function(cssClass) {
17390         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17391             if (this.invalidHandleClasses[i] == cssClass) {
17392                 delete this.invalidHandleClasses[i];
17393             }
17394         }
17395     },
17396
17397     /**
17398      * Checks the tag exclusion list to see if this click should be ignored
17399      * @method isValidHandleChild
17400      * @param {HTMLElement} node the HTMLElement to evaluate
17401      * @return {boolean} true if this is a valid tag type, false if not
17402      */
17403     isValidHandleChild: function(node) {
17404
17405         var valid = true;
17406         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17407         var nodeName;
17408         try {
17409             nodeName = node.nodeName.toUpperCase();
17410         } catch(e) {
17411             nodeName = node.nodeName;
17412         }
17413         valid = valid && !this.invalidHandleTypes[nodeName];
17414         valid = valid && !this.invalidHandleIds[node.id];
17415
17416         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17417             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17418         }
17419
17420
17421         return valid;
17422
17423     },
17424
17425     /**
17426      * Create the array of horizontal tick marks if an interval was specified
17427      * in setXConstraint().
17428      * @method setXTicks
17429      * @private
17430      */
17431     setXTicks: function(iStartX, iTickSize) {
17432         this.xTicks = [];
17433         this.xTickSize = iTickSize;
17434
17435         var tickMap = {};
17436
17437         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17438             if (!tickMap[i]) {
17439                 this.xTicks[this.xTicks.length] = i;
17440                 tickMap[i] = true;
17441             }
17442         }
17443
17444         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17445             if (!tickMap[i]) {
17446                 this.xTicks[this.xTicks.length] = i;
17447                 tickMap[i] = true;
17448             }
17449         }
17450
17451         this.xTicks.sort(this.DDM.numericSort) ;
17452     },
17453
17454     /**
17455      * Create the array of vertical tick marks if an interval was specified in
17456      * setYConstraint().
17457      * @method setYTicks
17458      * @private
17459      */
17460     setYTicks: function(iStartY, iTickSize) {
17461         this.yTicks = [];
17462         this.yTickSize = iTickSize;
17463
17464         var tickMap = {};
17465
17466         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17467             if (!tickMap[i]) {
17468                 this.yTicks[this.yTicks.length] = i;
17469                 tickMap[i] = true;
17470             }
17471         }
17472
17473         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17474             if (!tickMap[i]) {
17475                 this.yTicks[this.yTicks.length] = i;
17476                 tickMap[i] = true;
17477             }
17478         }
17479
17480         this.yTicks.sort(this.DDM.numericSort) ;
17481     },
17482
17483     /**
17484      * By default, the element can be dragged any place on the screen.  Use
17485      * this method to limit the horizontal travel of the element.  Pass in
17486      * 0,0 for the parameters if you want to lock the drag to the y axis.
17487      * @method setXConstraint
17488      * @param {int} iLeft the number of pixels the element can move to the left
17489      * @param {int} iRight the number of pixels the element can move to the
17490      * right
17491      * @param {int} iTickSize optional parameter for specifying that the
17492      * element
17493      * should move iTickSize pixels at a time.
17494      */
17495     setXConstraint: function(iLeft, iRight, iTickSize) {
17496         this.leftConstraint = iLeft;
17497         this.rightConstraint = iRight;
17498
17499         this.minX = this.initPageX - iLeft;
17500         this.maxX = this.initPageX + iRight;
17501         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17502
17503         this.constrainX = true;
17504     },
17505
17506     /**
17507      * Clears any constraints applied to this instance.  Also clears ticks
17508      * since they can't exist independent of a constraint at this time.
17509      * @method clearConstraints
17510      */
17511     clearConstraints: function() {
17512         this.constrainX = false;
17513         this.constrainY = false;
17514         this.clearTicks();
17515     },
17516
17517     /**
17518      * Clears any tick interval defined for this instance
17519      * @method clearTicks
17520      */
17521     clearTicks: function() {
17522         this.xTicks = null;
17523         this.yTicks = null;
17524         this.xTickSize = 0;
17525         this.yTickSize = 0;
17526     },
17527
17528     /**
17529      * By default, the element can be dragged any place on the screen.  Set
17530      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17531      * parameters if you want to lock the drag to the x axis.
17532      * @method setYConstraint
17533      * @param {int} iUp the number of pixels the element can move up
17534      * @param {int} iDown the number of pixels the element can move down
17535      * @param {int} iTickSize optional parameter for specifying that the
17536      * element should move iTickSize pixels at a time.
17537      */
17538     setYConstraint: function(iUp, iDown, iTickSize) {
17539         this.topConstraint = iUp;
17540         this.bottomConstraint = iDown;
17541
17542         this.minY = this.initPageY - iUp;
17543         this.maxY = this.initPageY + iDown;
17544         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17545
17546         this.constrainY = true;
17547
17548     },
17549
17550     /**
17551      * resetConstraints must be called if you manually reposition a dd element.
17552      * @method resetConstraints
17553      * @param {boolean} maintainOffset
17554      */
17555     resetConstraints: function() {
17556
17557
17558         // Maintain offsets if necessary
17559         if (this.initPageX || this.initPageX === 0) {
17560             // figure out how much this thing has moved
17561             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17562             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17563
17564             this.setInitPosition(dx, dy);
17565
17566         // This is the first time we have detected the element's position
17567         } else {
17568             this.setInitPosition();
17569         }
17570
17571         if (this.constrainX) {
17572             this.setXConstraint( this.leftConstraint,
17573                                  this.rightConstraint,
17574                                  this.xTickSize        );
17575         }
17576
17577         if (this.constrainY) {
17578             this.setYConstraint( this.topConstraint,
17579                                  this.bottomConstraint,
17580                                  this.yTickSize         );
17581         }
17582     },
17583
17584     /**
17585      * Normally the drag element is moved pixel by pixel, but we can specify
17586      * that it move a number of pixels at a time.  This method resolves the
17587      * location when we have it set up like this.
17588      * @method getTick
17589      * @param {int} val where we want to place the object
17590      * @param {int[]} tickArray sorted array of valid points
17591      * @return {int} the closest tick
17592      * @private
17593      */
17594     getTick: function(val, tickArray) {
17595
17596         if (!tickArray) {
17597             // If tick interval is not defined, it is effectively 1 pixel,
17598             // so we return the value passed to us.
17599             return val;
17600         } else if (tickArray[0] >= val) {
17601             // The value is lower than the first tick, so we return the first
17602             // tick.
17603             return tickArray[0];
17604         } else {
17605             for (var i=0, len=tickArray.length; i<len; ++i) {
17606                 var next = i + 1;
17607                 if (tickArray[next] && tickArray[next] >= val) {
17608                     var diff1 = val - tickArray[i];
17609                     var diff2 = tickArray[next] - val;
17610                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17611                 }
17612             }
17613
17614             // The value is larger than the last tick, so we return the last
17615             // tick.
17616             return tickArray[tickArray.length - 1];
17617         }
17618     },
17619
17620     /**
17621      * toString method
17622      * @method toString
17623      * @return {string} string representation of the dd obj
17624      */
17625     toString: function() {
17626         return ("DragDrop " + this.id);
17627     }
17628
17629 });
17630
17631 })();
17632 /*
17633  * Based on:
17634  * Ext JS Library 1.1.1
17635  * Copyright(c) 2006-2007, Ext JS, LLC.
17636  *
17637  * Originally Released Under LGPL - original licence link has changed is not relivant.
17638  *
17639  * Fork - LGPL
17640  * <script type="text/javascript">
17641  */
17642
17643
17644 /**
17645  * The drag and drop utility provides a framework for building drag and drop
17646  * applications.  In addition to enabling drag and drop for specific elements,
17647  * the drag and drop elements are tracked by the manager class, and the
17648  * interactions between the various elements are tracked during the drag and
17649  * the implementing code is notified about these important moments.
17650  */
17651
17652 // Only load the library once.  Rewriting the manager class would orphan
17653 // existing drag and drop instances.
17654 if (!Roo.dd.DragDropMgr) {
17655
17656 /**
17657  * @class Roo.dd.DragDropMgr
17658  * DragDropMgr is a singleton that tracks the element interaction for
17659  * all DragDrop items in the window.  Generally, you will not call
17660  * this class directly, but it does have helper methods that could
17661  * be useful in your DragDrop implementations.
17662  * @singleton
17663  */
17664 Roo.dd.DragDropMgr = function() {
17665
17666     var Event = Roo.EventManager;
17667
17668     return {
17669
17670         /**
17671          * Two dimensional Array of registered DragDrop objects.  The first
17672          * dimension is the DragDrop item group, the second the DragDrop
17673          * object.
17674          * @property ids
17675          * @type {string: string}
17676          * @private
17677          * @static
17678          */
17679         ids: {},
17680
17681         /**
17682          * Array of element ids defined as drag handles.  Used to determine
17683          * if the element that generated the mousedown event is actually the
17684          * handle and not the html element itself.
17685          * @property handleIds
17686          * @type {string: string}
17687          * @private
17688          * @static
17689          */
17690         handleIds: {},
17691
17692         /**
17693          * the DragDrop object that is currently being dragged
17694          * @property dragCurrent
17695          * @type DragDrop
17696          * @private
17697          * @static
17698          **/
17699         dragCurrent: null,
17700
17701         /**
17702          * the DragDrop object(s) that are being hovered over
17703          * @property dragOvers
17704          * @type Array
17705          * @private
17706          * @static
17707          */
17708         dragOvers: {},
17709
17710         /**
17711          * the X distance between the cursor and the object being dragged
17712          * @property deltaX
17713          * @type int
17714          * @private
17715          * @static
17716          */
17717         deltaX: 0,
17718
17719         /**
17720          * the Y distance between the cursor and the object being dragged
17721          * @property deltaY
17722          * @type int
17723          * @private
17724          * @static
17725          */
17726         deltaY: 0,
17727
17728         /**
17729          * Flag to determine if we should prevent the default behavior of the
17730          * events we define. By default this is true, but this can be set to
17731          * false if you need the default behavior (not recommended)
17732          * @property preventDefault
17733          * @type boolean
17734          * @static
17735          */
17736         preventDefault: true,
17737
17738         /**
17739          * Flag to determine if we should stop the propagation of the events
17740          * we generate. This is true by default but you may want to set it to
17741          * false if the html element contains other features that require the
17742          * mouse click.
17743          * @property stopPropagation
17744          * @type boolean
17745          * @static
17746          */
17747         stopPropagation: true,
17748
17749         /**
17750          * Internal flag that is set to true when drag and drop has been
17751          * intialized
17752          * @property initialized
17753          * @private
17754          * @static
17755          */
17756         initalized: false,
17757
17758         /**
17759          * All drag and drop can be disabled.
17760          * @property locked
17761          * @private
17762          * @static
17763          */
17764         locked: false,
17765
17766         /**
17767          * Called the first time an element is registered.
17768          * @method init
17769          * @private
17770          * @static
17771          */
17772         init: function() {
17773             this.initialized = true;
17774         },
17775
17776         /**
17777          * In point mode, drag and drop interaction is defined by the
17778          * location of the cursor during the drag/drop
17779          * @property POINT
17780          * @type int
17781          * @static
17782          */
17783         POINT: 0,
17784
17785         /**
17786          * In intersect mode, drag and drop interactio nis defined by the
17787          * overlap of two or more drag and drop objects.
17788          * @property INTERSECT
17789          * @type int
17790          * @static
17791          */
17792         INTERSECT: 1,
17793
17794         /**
17795          * The current drag and drop mode.  Default: POINT
17796          * @property mode
17797          * @type int
17798          * @static
17799          */
17800         mode: 0,
17801
17802         /**
17803          * Runs method on all drag and drop objects
17804          * @method _execOnAll
17805          * @private
17806          * @static
17807          */
17808         _execOnAll: function(sMethod, args) {
17809             for (var i in this.ids) {
17810                 for (var j in this.ids[i]) {
17811                     var oDD = this.ids[i][j];
17812                     if (! this.isTypeOfDD(oDD)) {
17813                         continue;
17814                     }
17815                     oDD[sMethod].apply(oDD, args);
17816                 }
17817             }
17818         },
17819
17820         /**
17821          * Drag and drop initialization.  Sets up the global event handlers
17822          * @method _onLoad
17823          * @private
17824          * @static
17825          */
17826         _onLoad: function() {
17827
17828             this.init();
17829
17830             if (!Roo.isTouch) {
17831                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17832                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17833             }
17834             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17835             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17836             
17837             Event.on(window,   "unload",    this._onUnload, this, true);
17838             Event.on(window,   "resize",    this._onResize, this, true);
17839             // Event.on(window,   "mouseout",    this._test);
17840
17841         },
17842
17843         /**
17844          * Reset constraints on all drag and drop objs
17845          * @method _onResize
17846          * @private
17847          * @static
17848          */
17849         _onResize: function(e) {
17850             this._execOnAll("resetConstraints", []);
17851         },
17852
17853         /**
17854          * Lock all drag and drop functionality
17855          * @method lock
17856          * @static
17857          */
17858         lock: function() { this.locked = true; },
17859
17860         /**
17861          * Unlock all drag and drop functionality
17862          * @method unlock
17863          * @static
17864          */
17865         unlock: function() { this.locked = false; },
17866
17867         /**
17868          * Is drag and drop locked?
17869          * @method isLocked
17870          * @return {boolean} True if drag and drop is locked, false otherwise.
17871          * @static
17872          */
17873         isLocked: function() { return this.locked; },
17874
17875         /**
17876          * Location cache that is set for all drag drop objects when a drag is
17877          * initiated, cleared when the drag is finished.
17878          * @property locationCache
17879          * @private
17880          * @static
17881          */
17882         locationCache: {},
17883
17884         /**
17885          * Set useCache to false if you want to force object the lookup of each
17886          * drag and drop linked element constantly during a drag.
17887          * @property useCache
17888          * @type boolean
17889          * @static
17890          */
17891         useCache: true,
17892
17893         /**
17894          * The number of pixels that the mouse needs to move after the
17895          * mousedown before the drag is initiated.  Default=3;
17896          * @property clickPixelThresh
17897          * @type int
17898          * @static
17899          */
17900         clickPixelThresh: 3,
17901
17902         /**
17903          * The number of milliseconds after the mousedown event to initiate the
17904          * drag if we don't get a mouseup event. Default=1000
17905          * @property clickTimeThresh
17906          * @type int
17907          * @static
17908          */
17909         clickTimeThresh: 350,
17910
17911         /**
17912          * Flag that indicates that either the drag pixel threshold or the
17913          * mousdown time threshold has been met
17914          * @property dragThreshMet
17915          * @type boolean
17916          * @private
17917          * @static
17918          */
17919         dragThreshMet: false,
17920
17921         /**
17922          * Timeout used for the click time threshold
17923          * @property clickTimeout
17924          * @type Object
17925          * @private
17926          * @static
17927          */
17928         clickTimeout: null,
17929
17930         /**
17931          * The X position of the mousedown event stored for later use when a
17932          * drag threshold is met.
17933          * @property startX
17934          * @type int
17935          * @private
17936          * @static
17937          */
17938         startX: 0,
17939
17940         /**
17941          * The Y position of the mousedown event stored for later use when a
17942          * drag threshold is met.
17943          * @property startY
17944          * @type int
17945          * @private
17946          * @static
17947          */
17948         startY: 0,
17949
17950         /**
17951          * Each DragDrop instance must be registered with the DragDropMgr.
17952          * This is executed in DragDrop.init()
17953          * @method regDragDrop
17954          * @param {DragDrop} oDD the DragDrop object to register
17955          * @param {String} sGroup the name of the group this element belongs to
17956          * @static
17957          */
17958         regDragDrop: function(oDD, sGroup) {
17959             if (!this.initialized) { this.init(); }
17960
17961             if (!this.ids[sGroup]) {
17962                 this.ids[sGroup] = {};
17963             }
17964             this.ids[sGroup][oDD.id] = oDD;
17965         },
17966
17967         /**
17968          * Removes the supplied dd instance from the supplied group. Executed
17969          * by DragDrop.removeFromGroup, so don't call this function directly.
17970          * @method removeDDFromGroup
17971          * @private
17972          * @static
17973          */
17974         removeDDFromGroup: function(oDD, sGroup) {
17975             if (!this.ids[sGroup]) {
17976                 this.ids[sGroup] = {};
17977             }
17978
17979             var obj = this.ids[sGroup];
17980             if (obj && obj[oDD.id]) {
17981                 delete obj[oDD.id];
17982             }
17983         },
17984
17985         /**
17986          * Unregisters a drag and drop item.  This is executed in
17987          * DragDrop.unreg, use that method instead of calling this directly.
17988          * @method _remove
17989          * @private
17990          * @static
17991          */
17992         _remove: function(oDD) {
17993             for (var g in oDD.groups) {
17994                 if (g && this.ids[g][oDD.id]) {
17995                     delete this.ids[g][oDD.id];
17996                 }
17997             }
17998             delete this.handleIds[oDD.id];
17999         },
18000
18001         /**
18002          * Each DragDrop handle element must be registered.  This is done
18003          * automatically when executing DragDrop.setHandleElId()
18004          * @method regHandle
18005          * @param {String} sDDId the DragDrop id this element is a handle for
18006          * @param {String} sHandleId the id of the element that is the drag
18007          * handle
18008          * @static
18009          */
18010         regHandle: function(sDDId, sHandleId) {
18011             if (!this.handleIds[sDDId]) {
18012                 this.handleIds[sDDId] = {};
18013             }
18014             this.handleIds[sDDId][sHandleId] = sHandleId;
18015         },
18016
18017         /**
18018          * Utility function to determine if a given element has been
18019          * registered as a drag drop item.
18020          * @method isDragDrop
18021          * @param {String} id the element id to check
18022          * @return {boolean} true if this element is a DragDrop item,
18023          * false otherwise
18024          * @static
18025          */
18026         isDragDrop: function(id) {
18027             return ( this.getDDById(id) ) ? true : false;
18028         },
18029
18030         /**
18031          * Returns the drag and drop instances that are in all groups the
18032          * passed in instance belongs to.
18033          * @method getRelated
18034          * @param {DragDrop} p_oDD the obj to get related data for
18035          * @param {boolean} bTargetsOnly if true, only return targetable objs
18036          * @return {DragDrop[]} the related instances
18037          * @static
18038          */
18039         getRelated: function(p_oDD, bTargetsOnly) {
18040             var oDDs = [];
18041             for (var i in p_oDD.groups) {
18042                 for (j in this.ids[i]) {
18043                     var dd = this.ids[i][j];
18044                     if (! this.isTypeOfDD(dd)) {
18045                         continue;
18046                     }
18047                     if (!bTargetsOnly || dd.isTarget) {
18048                         oDDs[oDDs.length] = dd;
18049                     }
18050                 }
18051             }
18052
18053             return oDDs;
18054         },
18055
18056         /**
18057          * Returns true if the specified dd target is a legal target for
18058          * the specifice drag obj
18059          * @method isLegalTarget
18060          * @param {DragDrop} the drag obj
18061          * @param {DragDrop} the target
18062          * @return {boolean} true if the target is a legal target for the
18063          * dd obj
18064          * @static
18065          */
18066         isLegalTarget: function (oDD, oTargetDD) {
18067             var targets = this.getRelated(oDD, true);
18068             for (var i=0, len=targets.length;i<len;++i) {
18069                 if (targets[i].id == oTargetDD.id) {
18070                     return true;
18071                 }
18072             }
18073
18074             return false;
18075         },
18076
18077         /**
18078          * My goal is to be able to transparently determine if an object is
18079          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18080          * returns "object", oDD.constructor.toString() always returns
18081          * "DragDrop" and not the name of the subclass.  So for now it just
18082          * evaluates a well-known variable in DragDrop.
18083          * @method isTypeOfDD
18084          * @param {Object} the object to evaluate
18085          * @return {boolean} true if typeof oDD = DragDrop
18086          * @static
18087          */
18088         isTypeOfDD: function (oDD) {
18089             return (oDD && oDD.__ygDragDrop);
18090         },
18091
18092         /**
18093          * Utility function to determine if a given element has been
18094          * registered as a drag drop handle for the given Drag Drop object.
18095          * @method isHandle
18096          * @param {String} id the element id to check
18097          * @return {boolean} true if this element is a DragDrop handle, false
18098          * otherwise
18099          * @static
18100          */
18101         isHandle: function(sDDId, sHandleId) {
18102             return ( this.handleIds[sDDId] &&
18103                             this.handleIds[sDDId][sHandleId] );
18104         },
18105
18106         /**
18107          * Returns the DragDrop instance for a given id
18108          * @method getDDById
18109          * @param {String} id the id of the DragDrop object
18110          * @return {DragDrop} the drag drop object, null if it is not found
18111          * @static
18112          */
18113         getDDById: function(id) {
18114             for (var i in this.ids) {
18115                 if (this.ids[i][id]) {
18116                     return this.ids[i][id];
18117                 }
18118             }
18119             return null;
18120         },
18121
18122         /**
18123          * Fired after a registered DragDrop object gets the mousedown event.
18124          * Sets up the events required to track the object being dragged
18125          * @method handleMouseDown
18126          * @param {Event} e the event
18127          * @param oDD the DragDrop object being dragged
18128          * @private
18129          * @static
18130          */
18131         handleMouseDown: function(e, oDD) {
18132             if(Roo.QuickTips){
18133                 Roo.QuickTips.disable();
18134             }
18135             this.currentTarget = e.getTarget();
18136
18137             this.dragCurrent = oDD;
18138
18139             var el = oDD.getEl();
18140
18141             // track start position
18142             this.startX = e.getPageX();
18143             this.startY = e.getPageY();
18144
18145             this.deltaX = this.startX - el.offsetLeft;
18146             this.deltaY = this.startY - el.offsetTop;
18147
18148             this.dragThreshMet = false;
18149
18150             this.clickTimeout = setTimeout(
18151                     function() {
18152                         var DDM = Roo.dd.DDM;
18153                         DDM.startDrag(DDM.startX, DDM.startY);
18154                     },
18155                     this.clickTimeThresh );
18156         },
18157
18158         /**
18159          * Fired when either the drag pixel threshol or the mousedown hold
18160          * time threshold has been met.
18161          * @method startDrag
18162          * @param x {int} the X position of the original mousedown
18163          * @param y {int} the Y position of the original mousedown
18164          * @static
18165          */
18166         startDrag: function(x, y) {
18167             clearTimeout(this.clickTimeout);
18168             if (this.dragCurrent) {
18169                 this.dragCurrent.b4StartDrag(x, y);
18170                 this.dragCurrent.startDrag(x, y);
18171             }
18172             this.dragThreshMet = true;
18173         },
18174
18175         /**
18176          * Internal function to handle the mouseup event.  Will be invoked
18177          * from the context of the document.
18178          * @method handleMouseUp
18179          * @param {Event} e the event
18180          * @private
18181          * @static
18182          */
18183         handleMouseUp: function(e) {
18184
18185             if(Roo.QuickTips){
18186                 Roo.QuickTips.enable();
18187             }
18188             if (! this.dragCurrent) {
18189                 return;
18190             }
18191
18192             clearTimeout(this.clickTimeout);
18193
18194             if (this.dragThreshMet) {
18195                 this.fireEvents(e, true);
18196             } else {
18197             }
18198
18199             this.stopDrag(e);
18200
18201             this.stopEvent(e);
18202         },
18203
18204         /**
18205          * Utility to stop event propagation and event default, if these
18206          * features are turned on.
18207          * @method stopEvent
18208          * @param {Event} e the event as returned by this.getEvent()
18209          * @static
18210          */
18211         stopEvent: function(e){
18212             if(this.stopPropagation) {
18213                 e.stopPropagation();
18214             }
18215
18216             if (this.preventDefault) {
18217                 e.preventDefault();
18218             }
18219         },
18220
18221         /**
18222          * Internal function to clean up event handlers after the drag
18223          * operation is complete
18224          * @method stopDrag
18225          * @param {Event} e the event
18226          * @private
18227          * @static
18228          */
18229         stopDrag: function(e) {
18230             // Fire the drag end event for the item that was dragged
18231             if (this.dragCurrent) {
18232                 if (this.dragThreshMet) {
18233                     this.dragCurrent.b4EndDrag(e);
18234                     this.dragCurrent.endDrag(e);
18235                 }
18236
18237                 this.dragCurrent.onMouseUp(e);
18238             }
18239
18240             this.dragCurrent = null;
18241             this.dragOvers = {};
18242         },
18243
18244         /**
18245          * Internal function to handle the mousemove event.  Will be invoked
18246          * from the context of the html element.
18247          *
18248          * @TODO figure out what we can do about mouse events lost when the
18249          * user drags objects beyond the window boundary.  Currently we can
18250          * detect this in internet explorer by verifying that the mouse is
18251          * down during the mousemove event.  Firefox doesn't give us the
18252          * button state on the mousemove event.
18253          * @method handleMouseMove
18254          * @param {Event} e the event
18255          * @private
18256          * @static
18257          */
18258         handleMouseMove: function(e) {
18259             if (! this.dragCurrent) {
18260                 return true;
18261             }
18262
18263             // var button = e.which || e.button;
18264
18265             // check for IE mouseup outside of page boundary
18266             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18267                 this.stopEvent(e);
18268                 return this.handleMouseUp(e);
18269             }
18270
18271             if (!this.dragThreshMet) {
18272                 var diffX = Math.abs(this.startX - e.getPageX());
18273                 var diffY = Math.abs(this.startY - e.getPageY());
18274                 if (diffX > this.clickPixelThresh ||
18275                             diffY > this.clickPixelThresh) {
18276                     this.startDrag(this.startX, this.startY);
18277                 }
18278             }
18279
18280             if (this.dragThreshMet) {
18281                 this.dragCurrent.b4Drag(e);
18282                 this.dragCurrent.onDrag(e);
18283                 if(!this.dragCurrent.moveOnly){
18284                     this.fireEvents(e, false);
18285                 }
18286             }
18287
18288             this.stopEvent(e);
18289
18290             return true;
18291         },
18292
18293         /**
18294          * Iterates over all of the DragDrop elements to find ones we are
18295          * hovering over or dropping on
18296          * @method fireEvents
18297          * @param {Event} e the event
18298          * @param {boolean} isDrop is this a drop op or a mouseover op?
18299          * @private
18300          * @static
18301          */
18302         fireEvents: function(e, isDrop) {
18303             var dc = this.dragCurrent;
18304
18305             // If the user did the mouse up outside of the window, we could
18306             // get here even though we have ended the drag.
18307             if (!dc || dc.isLocked()) {
18308                 return;
18309             }
18310
18311             var pt = e.getPoint();
18312
18313             // cache the previous dragOver array
18314             var oldOvers = [];
18315
18316             var outEvts   = [];
18317             var overEvts  = [];
18318             var dropEvts  = [];
18319             var enterEvts = [];
18320
18321             // Check to see if the object(s) we were hovering over is no longer
18322             // being hovered over so we can fire the onDragOut event
18323             for (var i in this.dragOvers) {
18324
18325                 var ddo = this.dragOvers[i];
18326
18327                 if (! this.isTypeOfDD(ddo)) {
18328                     continue;
18329                 }
18330
18331                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18332                     outEvts.push( ddo );
18333                 }
18334
18335                 oldOvers[i] = true;
18336                 delete this.dragOvers[i];
18337             }
18338
18339             for (var sGroup in dc.groups) {
18340
18341                 if ("string" != typeof sGroup) {
18342                     continue;
18343                 }
18344
18345                 for (i in this.ids[sGroup]) {
18346                     var oDD = this.ids[sGroup][i];
18347                     if (! this.isTypeOfDD(oDD)) {
18348                         continue;
18349                     }
18350
18351                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18352                         if (this.isOverTarget(pt, oDD, this.mode)) {
18353                             // look for drop interactions
18354                             if (isDrop) {
18355                                 dropEvts.push( oDD );
18356                             // look for drag enter and drag over interactions
18357                             } else {
18358
18359                                 // initial drag over: dragEnter fires
18360                                 if (!oldOvers[oDD.id]) {
18361                                     enterEvts.push( oDD );
18362                                 // subsequent drag overs: dragOver fires
18363                                 } else {
18364                                     overEvts.push( oDD );
18365                                 }
18366
18367                                 this.dragOvers[oDD.id] = oDD;
18368                             }
18369                         }
18370                     }
18371                 }
18372             }
18373
18374             if (this.mode) {
18375                 if (outEvts.length) {
18376                     dc.b4DragOut(e, outEvts);
18377                     dc.onDragOut(e, outEvts);
18378                 }
18379
18380                 if (enterEvts.length) {
18381                     dc.onDragEnter(e, enterEvts);
18382                 }
18383
18384                 if (overEvts.length) {
18385                     dc.b4DragOver(e, overEvts);
18386                     dc.onDragOver(e, overEvts);
18387                 }
18388
18389                 if (dropEvts.length) {
18390                     dc.b4DragDrop(e, dropEvts);
18391                     dc.onDragDrop(e, dropEvts);
18392                 }
18393
18394             } else {
18395                 // fire dragout events
18396                 var len = 0;
18397                 for (i=0, len=outEvts.length; i<len; ++i) {
18398                     dc.b4DragOut(e, outEvts[i].id);
18399                     dc.onDragOut(e, outEvts[i].id);
18400                 }
18401
18402                 // fire enter events
18403                 for (i=0,len=enterEvts.length; i<len; ++i) {
18404                     // dc.b4DragEnter(e, oDD.id);
18405                     dc.onDragEnter(e, enterEvts[i].id);
18406                 }
18407
18408                 // fire over events
18409                 for (i=0,len=overEvts.length; i<len; ++i) {
18410                     dc.b4DragOver(e, overEvts[i].id);
18411                     dc.onDragOver(e, overEvts[i].id);
18412                 }
18413
18414                 // fire drop events
18415                 for (i=0, len=dropEvts.length; i<len; ++i) {
18416                     dc.b4DragDrop(e, dropEvts[i].id);
18417                     dc.onDragDrop(e, dropEvts[i].id);
18418                 }
18419
18420             }
18421
18422             // notify about a drop that did not find a target
18423             if (isDrop && !dropEvts.length) {
18424                 dc.onInvalidDrop(e);
18425             }
18426
18427         },
18428
18429         /**
18430          * Helper function for getting the best match from the list of drag
18431          * and drop objects returned by the drag and drop events when we are
18432          * in INTERSECT mode.  It returns either the first object that the
18433          * cursor is over, or the object that has the greatest overlap with
18434          * the dragged element.
18435          * @method getBestMatch
18436          * @param  {DragDrop[]} dds The array of drag and drop objects
18437          * targeted
18438          * @return {DragDrop}       The best single match
18439          * @static
18440          */
18441         getBestMatch: function(dds) {
18442             var winner = null;
18443             // Return null if the input is not what we expect
18444             //if (!dds || !dds.length || dds.length == 0) {
18445                // winner = null;
18446             // If there is only one item, it wins
18447             //} else if (dds.length == 1) {
18448
18449             var len = dds.length;
18450
18451             if (len == 1) {
18452                 winner = dds[0];
18453             } else {
18454                 // Loop through the targeted items
18455                 for (var i=0; i<len; ++i) {
18456                     var dd = dds[i];
18457                     // If the cursor is over the object, it wins.  If the
18458                     // cursor is over multiple matches, the first one we come
18459                     // to wins.
18460                     if (dd.cursorIsOver) {
18461                         winner = dd;
18462                         break;
18463                     // Otherwise the object with the most overlap wins
18464                     } else {
18465                         if (!winner ||
18466                             winner.overlap.getArea() < dd.overlap.getArea()) {
18467                             winner = dd;
18468                         }
18469                     }
18470                 }
18471             }
18472
18473             return winner;
18474         },
18475
18476         /**
18477          * Refreshes the cache of the top-left and bottom-right points of the
18478          * drag and drop objects in the specified group(s).  This is in the
18479          * format that is stored in the drag and drop instance, so typical
18480          * usage is:
18481          * <code>
18482          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18483          * </code>
18484          * Alternatively:
18485          * <code>
18486          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18487          * </code>
18488          * @TODO this really should be an indexed array.  Alternatively this
18489          * method could accept both.
18490          * @method refreshCache
18491          * @param {Object} groups an associative array of groups to refresh
18492          * @static
18493          */
18494         refreshCache: function(groups) {
18495             for (var sGroup in groups) {
18496                 if ("string" != typeof sGroup) {
18497                     continue;
18498                 }
18499                 for (var i in this.ids[sGroup]) {
18500                     var oDD = this.ids[sGroup][i];
18501
18502                     if (this.isTypeOfDD(oDD)) {
18503                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18504                         var loc = this.getLocation(oDD);
18505                         if (loc) {
18506                             this.locationCache[oDD.id] = loc;
18507                         } else {
18508                             delete this.locationCache[oDD.id];
18509                             // this will unregister the drag and drop object if
18510                             // the element is not in a usable state
18511                             // oDD.unreg();
18512                         }
18513                     }
18514                 }
18515             }
18516         },
18517
18518         /**
18519          * This checks to make sure an element exists and is in the DOM.  The
18520          * main purpose is to handle cases where innerHTML is used to remove
18521          * drag and drop objects from the DOM.  IE provides an 'unspecified
18522          * error' when trying to access the offsetParent of such an element
18523          * @method verifyEl
18524          * @param {HTMLElement} el the element to check
18525          * @return {boolean} true if the element looks usable
18526          * @static
18527          */
18528         verifyEl: function(el) {
18529             if (el) {
18530                 var parent;
18531                 if(Roo.isIE){
18532                     try{
18533                         parent = el.offsetParent;
18534                     }catch(e){}
18535                 }else{
18536                     parent = el.offsetParent;
18537                 }
18538                 if (parent) {
18539                     return true;
18540                 }
18541             }
18542
18543             return false;
18544         },
18545
18546         /**
18547          * Returns a Region object containing the drag and drop element's position
18548          * and size, including the padding configured for it
18549          * @method getLocation
18550          * @param {DragDrop} oDD the drag and drop object to get the
18551          *                       location for
18552          * @return {Roo.lib.Region} a Region object representing the total area
18553          *                             the element occupies, including any padding
18554          *                             the instance is configured for.
18555          * @static
18556          */
18557         getLocation: function(oDD) {
18558             if (! this.isTypeOfDD(oDD)) {
18559                 return null;
18560             }
18561
18562             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18563
18564             try {
18565                 pos= Roo.lib.Dom.getXY(el);
18566             } catch (e) { }
18567
18568             if (!pos) {
18569                 return null;
18570             }
18571
18572             x1 = pos[0];
18573             x2 = x1 + el.offsetWidth;
18574             y1 = pos[1];
18575             y2 = y1 + el.offsetHeight;
18576
18577             t = y1 - oDD.padding[0];
18578             r = x2 + oDD.padding[1];
18579             b = y2 + oDD.padding[2];
18580             l = x1 - oDD.padding[3];
18581
18582             return new Roo.lib.Region( t, r, b, l );
18583         },
18584
18585         /**
18586          * Checks the cursor location to see if it over the target
18587          * @method isOverTarget
18588          * @param {Roo.lib.Point} pt The point to evaluate
18589          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18590          * @return {boolean} true if the mouse is over the target
18591          * @private
18592          * @static
18593          */
18594         isOverTarget: function(pt, oTarget, intersect) {
18595             // use cache if available
18596             var loc = this.locationCache[oTarget.id];
18597             if (!loc || !this.useCache) {
18598                 loc = this.getLocation(oTarget);
18599                 this.locationCache[oTarget.id] = loc;
18600
18601             }
18602
18603             if (!loc) {
18604                 return false;
18605             }
18606
18607             oTarget.cursorIsOver = loc.contains( pt );
18608
18609             // DragDrop is using this as a sanity check for the initial mousedown
18610             // in this case we are done.  In POINT mode, if the drag obj has no
18611             // contraints, we are also done. Otherwise we need to evaluate the
18612             // location of the target as related to the actual location of the
18613             // dragged element.
18614             var dc = this.dragCurrent;
18615             if (!dc || !dc.getTargetCoord ||
18616                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18617                 return oTarget.cursorIsOver;
18618             }
18619
18620             oTarget.overlap = null;
18621
18622             // Get the current location of the drag element, this is the
18623             // location of the mouse event less the delta that represents
18624             // where the original mousedown happened on the element.  We
18625             // need to consider constraints and ticks as well.
18626             var pos = dc.getTargetCoord(pt.x, pt.y);
18627
18628             var el = dc.getDragEl();
18629             var curRegion = new Roo.lib.Region( pos.y,
18630                                                    pos.x + el.offsetWidth,
18631                                                    pos.y + el.offsetHeight,
18632                                                    pos.x );
18633
18634             var overlap = curRegion.intersect(loc);
18635
18636             if (overlap) {
18637                 oTarget.overlap = overlap;
18638                 return (intersect) ? true : oTarget.cursorIsOver;
18639             } else {
18640                 return false;
18641             }
18642         },
18643
18644         /**
18645          * unload event handler
18646          * @method _onUnload
18647          * @private
18648          * @static
18649          */
18650         _onUnload: function(e, me) {
18651             Roo.dd.DragDropMgr.unregAll();
18652         },
18653
18654         /**
18655          * Cleans up the drag and drop events and objects.
18656          * @method unregAll
18657          * @private
18658          * @static
18659          */
18660         unregAll: function() {
18661
18662             if (this.dragCurrent) {
18663                 this.stopDrag();
18664                 this.dragCurrent = null;
18665             }
18666
18667             this._execOnAll("unreg", []);
18668
18669             for (i in this.elementCache) {
18670                 delete this.elementCache[i];
18671             }
18672
18673             this.elementCache = {};
18674             this.ids = {};
18675         },
18676
18677         /**
18678          * A cache of DOM elements
18679          * @property elementCache
18680          * @private
18681          * @static
18682          */
18683         elementCache: {},
18684
18685         /**
18686          * Get the wrapper for the DOM element specified
18687          * @method getElWrapper
18688          * @param {String} id the id of the element to get
18689          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18690          * @private
18691          * @deprecated This wrapper isn't that useful
18692          * @static
18693          */
18694         getElWrapper: function(id) {
18695             var oWrapper = this.elementCache[id];
18696             if (!oWrapper || !oWrapper.el) {
18697                 oWrapper = this.elementCache[id] =
18698                     new this.ElementWrapper(Roo.getDom(id));
18699             }
18700             return oWrapper;
18701         },
18702
18703         /**
18704          * Returns the actual DOM element
18705          * @method getElement
18706          * @param {String} id the id of the elment to get
18707          * @return {Object} The element
18708          * @deprecated use Roo.getDom instead
18709          * @static
18710          */
18711         getElement: function(id) {
18712             return Roo.getDom(id);
18713         },
18714
18715         /**
18716          * Returns the style property for the DOM element (i.e.,
18717          * document.getElById(id).style)
18718          * @method getCss
18719          * @param {String} id the id of the elment to get
18720          * @return {Object} The style property of the element
18721          * @deprecated use Roo.getDom instead
18722          * @static
18723          */
18724         getCss: function(id) {
18725             var el = Roo.getDom(id);
18726             return (el) ? el.style : null;
18727         },
18728
18729         /**
18730          * Inner class for cached elements
18731          * @class DragDropMgr.ElementWrapper
18732          * @for DragDropMgr
18733          * @private
18734          * @deprecated
18735          */
18736         ElementWrapper: function(el) {
18737                 /**
18738                  * The element
18739                  * @property el
18740                  */
18741                 this.el = el || null;
18742                 /**
18743                  * The element id
18744                  * @property id
18745                  */
18746                 this.id = this.el && el.id;
18747                 /**
18748                  * A reference to the style property
18749                  * @property css
18750                  */
18751                 this.css = this.el && el.style;
18752             },
18753
18754         /**
18755          * Returns the X position of an html element
18756          * @method getPosX
18757          * @param el the element for which to get the position
18758          * @return {int} the X coordinate
18759          * @for DragDropMgr
18760          * @deprecated use Roo.lib.Dom.getX instead
18761          * @static
18762          */
18763         getPosX: function(el) {
18764             return Roo.lib.Dom.getX(el);
18765         },
18766
18767         /**
18768          * Returns the Y position of an html element
18769          * @method getPosY
18770          * @param el the element for which to get the position
18771          * @return {int} the Y coordinate
18772          * @deprecated use Roo.lib.Dom.getY instead
18773          * @static
18774          */
18775         getPosY: function(el) {
18776             return Roo.lib.Dom.getY(el);
18777         },
18778
18779         /**
18780          * Swap two nodes.  In IE, we use the native method, for others we
18781          * emulate the IE behavior
18782          * @method swapNode
18783          * @param n1 the first node to swap
18784          * @param n2 the other node to swap
18785          * @static
18786          */
18787         swapNode: function(n1, n2) {
18788             if (n1.swapNode) {
18789                 n1.swapNode(n2);
18790             } else {
18791                 var p = n2.parentNode;
18792                 var s = n2.nextSibling;
18793
18794                 if (s == n1) {
18795                     p.insertBefore(n1, n2);
18796                 } else if (n2 == n1.nextSibling) {
18797                     p.insertBefore(n2, n1);
18798                 } else {
18799                     n1.parentNode.replaceChild(n2, n1);
18800                     p.insertBefore(n1, s);
18801                 }
18802             }
18803         },
18804
18805         /**
18806          * Returns the current scroll position
18807          * @method getScroll
18808          * @private
18809          * @static
18810          */
18811         getScroll: function () {
18812             var t, l, dde=document.documentElement, db=document.body;
18813             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18814                 t = dde.scrollTop;
18815                 l = dde.scrollLeft;
18816             } else if (db) {
18817                 t = db.scrollTop;
18818                 l = db.scrollLeft;
18819             } else {
18820
18821             }
18822             return { top: t, left: l };
18823         },
18824
18825         /**
18826          * Returns the specified element style property
18827          * @method getStyle
18828          * @param {HTMLElement} el          the element
18829          * @param {string}      styleProp   the style property
18830          * @return {string} The value of the style property
18831          * @deprecated use Roo.lib.Dom.getStyle
18832          * @static
18833          */
18834         getStyle: function(el, styleProp) {
18835             return Roo.fly(el).getStyle(styleProp);
18836         },
18837
18838         /**
18839          * Gets the scrollTop
18840          * @method getScrollTop
18841          * @return {int} the document's scrollTop
18842          * @static
18843          */
18844         getScrollTop: function () { return this.getScroll().top; },
18845
18846         /**
18847          * Gets the scrollLeft
18848          * @method getScrollLeft
18849          * @return {int} the document's scrollTop
18850          * @static
18851          */
18852         getScrollLeft: function () { return this.getScroll().left; },
18853
18854         /**
18855          * Sets the x/y position of an element to the location of the
18856          * target element.
18857          * @method moveToEl
18858          * @param {HTMLElement} moveEl      The element to move
18859          * @param {HTMLElement} targetEl    The position reference element
18860          * @static
18861          */
18862         moveToEl: function (moveEl, targetEl) {
18863             var aCoord = Roo.lib.Dom.getXY(targetEl);
18864             Roo.lib.Dom.setXY(moveEl, aCoord);
18865         },
18866
18867         /**
18868          * Numeric array sort function
18869          * @method numericSort
18870          * @static
18871          */
18872         numericSort: function(a, b) { return (a - b); },
18873
18874         /**
18875          * Internal counter
18876          * @property _timeoutCount
18877          * @private
18878          * @static
18879          */
18880         _timeoutCount: 0,
18881
18882         /**
18883          * Trying to make the load order less important.  Without this we get
18884          * an error if this file is loaded before the Event Utility.
18885          * @method _addListeners
18886          * @private
18887          * @static
18888          */
18889         _addListeners: function() {
18890             var DDM = Roo.dd.DDM;
18891             if ( Roo.lib.Event && document ) {
18892                 DDM._onLoad();
18893             } else {
18894                 if (DDM._timeoutCount > 2000) {
18895                 } else {
18896                     setTimeout(DDM._addListeners, 10);
18897                     if (document && document.body) {
18898                         DDM._timeoutCount += 1;
18899                     }
18900                 }
18901             }
18902         },
18903
18904         /**
18905          * Recursively searches the immediate parent and all child nodes for
18906          * the handle element in order to determine wheter or not it was
18907          * clicked.
18908          * @method handleWasClicked
18909          * @param node the html element to inspect
18910          * @static
18911          */
18912         handleWasClicked: function(node, id) {
18913             if (this.isHandle(id, node.id)) {
18914                 return true;
18915             } else {
18916                 // check to see if this is a text node child of the one we want
18917                 var p = node.parentNode;
18918
18919                 while (p) {
18920                     if (this.isHandle(id, p.id)) {
18921                         return true;
18922                     } else {
18923                         p = p.parentNode;
18924                     }
18925                 }
18926             }
18927
18928             return false;
18929         }
18930
18931     };
18932
18933 }();
18934
18935 // shorter alias, save a few bytes
18936 Roo.dd.DDM = Roo.dd.DragDropMgr;
18937 Roo.dd.DDM._addListeners();
18938
18939 }/*
18940  * Based on:
18941  * Ext JS Library 1.1.1
18942  * Copyright(c) 2006-2007, Ext JS, LLC.
18943  *
18944  * Originally Released Under LGPL - original licence link has changed is not relivant.
18945  *
18946  * Fork - LGPL
18947  * <script type="text/javascript">
18948  */
18949
18950 /**
18951  * @class Roo.dd.DD
18952  * A DragDrop implementation where the linked element follows the
18953  * mouse cursor during a drag.
18954  * @extends Roo.dd.DragDrop
18955  * @constructor
18956  * @param {String} id the id of the linked element
18957  * @param {String} sGroup the group of related DragDrop items
18958  * @param {object} config an object containing configurable attributes
18959  *                Valid properties for DD:
18960  *                    scroll
18961  */
18962 Roo.dd.DD = function(id, sGroup, config) {
18963     if (id) {
18964         this.init(id, sGroup, config);
18965     }
18966 };
18967
18968 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18969
18970     /**
18971      * When set to true, the utility automatically tries to scroll the browser
18972      * window wehn a drag and drop element is dragged near the viewport boundary.
18973      * Defaults to true.
18974      * @property scroll
18975      * @type boolean
18976      */
18977     scroll: true,
18978
18979     /**
18980      * Sets the pointer offset to the distance between the linked element's top
18981      * left corner and the location the element was clicked
18982      * @method autoOffset
18983      * @param {int} iPageX the X coordinate of the click
18984      * @param {int} iPageY the Y coordinate of the click
18985      */
18986     autoOffset: function(iPageX, iPageY) {
18987         var x = iPageX - this.startPageX;
18988         var y = iPageY - this.startPageY;
18989         this.setDelta(x, y);
18990     },
18991
18992     /**
18993      * Sets the pointer offset.  You can call this directly to force the
18994      * offset to be in a particular location (e.g., pass in 0,0 to set it
18995      * to the center of the object)
18996      * @method setDelta
18997      * @param {int} iDeltaX the distance from the left
18998      * @param {int} iDeltaY the distance from the top
18999      */
19000     setDelta: function(iDeltaX, iDeltaY) {
19001         this.deltaX = iDeltaX;
19002         this.deltaY = iDeltaY;
19003     },
19004
19005     /**
19006      * Sets the drag element to the location of the mousedown or click event,
19007      * maintaining the cursor location relative to the location on the element
19008      * that was clicked.  Override this if you want to place the element in a
19009      * location other than where the cursor is.
19010      * @method setDragElPos
19011      * @param {int} iPageX the X coordinate of the mousedown or drag event
19012      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19013      */
19014     setDragElPos: function(iPageX, iPageY) {
19015         // the first time we do this, we are going to check to make sure
19016         // the element has css positioning
19017
19018         var el = this.getDragEl();
19019         this.alignElWithMouse(el, iPageX, iPageY);
19020     },
19021
19022     /**
19023      * Sets the element to the location of the mousedown or click event,
19024      * maintaining the cursor location relative to the location on the element
19025      * that was clicked.  Override this if you want to place the element in a
19026      * location other than where the cursor is.
19027      * @method alignElWithMouse
19028      * @param {HTMLElement} el the element to move
19029      * @param {int} iPageX the X coordinate of the mousedown or drag event
19030      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19031      */
19032     alignElWithMouse: function(el, iPageX, iPageY) {
19033         var oCoord = this.getTargetCoord(iPageX, iPageY);
19034         var fly = el.dom ? el : Roo.fly(el);
19035         if (!this.deltaSetXY) {
19036             var aCoord = [oCoord.x, oCoord.y];
19037             fly.setXY(aCoord);
19038             var newLeft = fly.getLeft(true);
19039             var newTop  = fly.getTop(true);
19040             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19041         } else {
19042             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19043         }
19044
19045         this.cachePosition(oCoord.x, oCoord.y);
19046         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19047         return oCoord;
19048     },
19049
19050     /**
19051      * Saves the most recent position so that we can reset the constraints and
19052      * tick marks on-demand.  We need to know this so that we can calculate the
19053      * number of pixels the element is offset from its original position.
19054      * @method cachePosition
19055      * @param iPageX the current x position (optional, this just makes it so we
19056      * don't have to look it up again)
19057      * @param iPageY the current y position (optional, this just makes it so we
19058      * don't have to look it up again)
19059      */
19060     cachePosition: function(iPageX, iPageY) {
19061         if (iPageX) {
19062             this.lastPageX = iPageX;
19063             this.lastPageY = iPageY;
19064         } else {
19065             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19066             this.lastPageX = aCoord[0];
19067             this.lastPageY = aCoord[1];
19068         }
19069     },
19070
19071     /**
19072      * Auto-scroll the window if the dragged object has been moved beyond the
19073      * visible window boundary.
19074      * @method autoScroll
19075      * @param {int} x the drag element's x position
19076      * @param {int} y the drag element's y position
19077      * @param {int} h the height of the drag element
19078      * @param {int} w the width of the drag element
19079      * @private
19080      */
19081     autoScroll: function(x, y, h, w) {
19082
19083         if (this.scroll) {
19084             // The client height
19085             var clientH = Roo.lib.Dom.getViewWidth();
19086
19087             // The client width
19088             var clientW = Roo.lib.Dom.getViewHeight();
19089
19090             // The amt scrolled down
19091             var st = this.DDM.getScrollTop();
19092
19093             // The amt scrolled right
19094             var sl = this.DDM.getScrollLeft();
19095
19096             // Location of the bottom of the element
19097             var bot = h + y;
19098
19099             // Location of the right of the element
19100             var right = w + x;
19101
19102             // The distance from the cursor to the bottom of the visible area,
19103             // adjusted so that we don't scroll if the cursor is beyond the
19104             // element drag constraints
19105             var toBot = (clientH + st - y - this.deltaY);
19106
19107             // The distance from the cursor to the right of the visible area
19108             var toRight = (clientW + sl - x - this.deltaX);
19109
19110
19111             // How close to the edge the cursor must be before we scroll
19112             // var thresh = (document.all) ? 100 : 40;
19113             var thresh = 40;
19114
19115             // How many pixels to scroll per autoscroll op.  This helps to reduce
19116             // clunky scrolling. IE is more sensitive about this ... it needs this
19117             // value to be higher.
19118             var scrAmt = (document.all) ? 80 : 30;
19119
19120             // Scroll down if we are near the bottom of the visible page and the
19121             // obj extends below the crease
19122             if ( bot > clientH && toBot < thresh ) {
19123                 window.scrollTo(sl, st + scrAmt);
19124             }
19125
19126             // Scroll up if the window is scrolled down and the top of the object
19127             // goes above the top border
19128             if ( y < st && st > 0 && y - st < thresh ) {
19129                 window.scrollTo(sl, st - scrAmt);
19130             }
19131
19132             // Scroll right if the obj is beyond the right border and the cursor is
19133             // near the border.
19134             if ( right > clientW && toRight < thresh ) {
19135                 window.scrollTo(sl + scrAmt, st);
19136             }
19137
19138             // Scroll left if the window has been scrolled to the right and the obj
19139             // extends past the left border
19140             if ( x < sl && sl > 0 && x - sl < thresh ) {
19141                 window.scrollTo(sl - scrAmt, st);
19142             }
19143         }
19144     },
19145
19146     /**
19147      * Finds the location the element should be placed if we want to move
19148      * it to where the mouse location less the click offset would place us.
19149      * @method getTargetCoord
19150      * @param {int} iPageX the X coordinate of the click
19151      * @param {int} iPageY the Y coordinate of the click
19152      * @return an object that contains the coordinates (Object.x and Object.y)
19153      * @private
19154      */
19155     getTargetCoord: function(iPageX, iPageY) {
19156
19157
19158         var x = iPageX - this.deltaX;
19159         var y = iPageY - this.deltaY;
19160
19161         if (this.constrainX) {
19162             if (x < this.minX) { x = this.minX; }
19163             if (x > this.maxX) { x = this.maxX; }
19164         }
19165
19166         if (this.constrainY) {
19167             if (y < this.minY) { y = this.minY; }
19168             if (y > this.maxY) { y = this.maxY; }
19169         }
19170
19171         x = this.getTick(x, this.xTicks);
19172         y = this.getTick(y, this.yTicks);
19173
19174
19175         return {x:x, y:y};
19176     },
19177
19178     /*
19179      * Sets up config options specific to this class. Overrides
19180      * Roo.dd.DragDrop, but all versions of this method through the
19181      * inheritance chain are called
19182      */
19183     applyConfig: function() {
19184         Roo.dd.DD.superclass.applyConfig.call(this);
19185         this.scroll = (this.config.scroll !== false);
19186     },
19187
19188     /*
19189      * Event that fires prior to the onMouseDown event.  Overrides
19190      * Roo.dd.DragDrop.
19191      */
19192     b4MouseDown: function(e) {
19193         // this.resetConstraints();
19194         this.autoOffset(e.getPageX(),
19195                             e.getPageY());
19196     },
19197
19198     /*
19199      * Event that fires prior to the onDrag event.  Overrides
19200      * Roo.dd.DragDrop.
19201      */
19202     b4Drag: function(e) {
19203         this.setDragElPos(e.getPageX(),
19204                             e.getPageY());
19205     },
19206
19207     toString: function() {
19208         return ("DD " + this.id);
19209     }
19210
19211     //////////////////////////////////////////////////////////////////////////
19212     // Debugging ygDragDrop events that can be overridden
19213     //////////////////////////////////////////////////////////////////////////
19214     /*
19215     startDrag: function(x, y) {
19216     },
19217
19218     onDrag: function(e) {
19219     },
19220
19221     onDragEnter: function(e, id) {
19222     },
19223
19224     onDragOver: function(e, id) {
19225     },
19226
19227     onDragOut: function(e, id) {
19228     },
19229
19230     onDragDrop: function(e, id) {
19231     },
19232
19233     endDrag: function(e) {
19234     }
19235
19236     */
19237
19238 });/*
19239  * Based on:
19240  * Ext JS Library 1.1.1
19241  * Copyright(c) 2006-2007, Ext JS, LLC.
19242  *
19243  * Originally Released Under LGPL - original licence link has changed is not relivant.
19244  *
19245  * Fork - LGPL
19246  * <script type="text/javascript">
19247  */
19248
19249 /**
19250  * @class Roo.dd.DDProxy
19251  * A DragDrop implementation that inserts an empty, bordered div into
19252  * the document that follows the cursor during drag operations.  At the time of
19253  * the click, the frame div is resized to the dimensions of the linked html
19254  * element, and moved to the exact location of the linked element.
19255  *
19256  * References to the "frame" element refer to the single proxy element that
19257  * was created to be dragged in place of all DDProxy elements on the
19258  * page.
19259  *
19260  * @extends Roo.dd.DD
19261  * @constructor
19262  * @param {String} id the id of the linked html element
19263  * @param {String} sGroup the group of related DragDrop objects
19264  * @param {object} config an object containing configurable attributes
19265  *                Valid properties for DDProxy in addition to those in DragDrop:
19266  *                   resizeFrame, centerFrame, dragElId
19267  */
19268 Roo.dd.DDProxy = function(id, sGroup, config) {
19269     if (id) {
19270         this.init(id, sGroup, config);
19271         this.initFrame();
19272     }
19273 };
19274
19275 /**
19276  * The default drag frame div id
19277  * @property Roo.dd.DDProxy.dragElId
19278  * @type String
19279  * @static
19280  */
19281 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19282
19283 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19284
19285     /**
19286      * By default we resize the drag frame to be the same size as the element
19287      * we want to drag (this is to get the frame effect).  We can turn it off
19288      * if we want a different behavior.
19289      * @property resizeFrame
19290      * @type boolean
19291      */
19292     resizeFrame: true,
19293
19294     /**
19295      * By default the frame is positioned exactly where the drag element is, so
19296      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19297      * you do not have constraints on the obj is to have the drag frame centered
19298      * around the cursor.  Set centerFrame to true for this effect.
19299      * @property centerFrame
19300      * @type boolean
19301      */
19302     centerFrame: false,
19303
19304     /**
19305      * Creates the proxy element if it does not yet exist
19306      * @method createFrame
19307      */
19308     createFrame: function() {
19309         var self = this;
19310         var body = document.body;
19311
19312         if (!body || !body.firstChild) {
19313             setTimeout( function() { self.createFrame(); }, 50 );
19314             return;
19315         }
19316
19317         var div = this.getDragEl();
19318
19319         if (!div) {
19320             div    = document.createElement("div");
19321             div.id = this.dragElId;
19322             var s  = div.style;
19323
19324             s.position   = "absolute";
19325             s.visibility = "hidden";
19326             s.cursor     = "move";
19327             s.border     = "2px solid #aaa";
19328             s.zIndex     = 999;
19329
19330             // appendChild can blow up IE if invoked prior to the window load event
19331             // while rendering a table.  It is possible there are other scenarios
19332             // that would cause this to happen as well.
19333             body.insertBefore(div, body.firstChild);
19334         }
19335     },
19336
19337     /**
19338      * Initialization for the drag frame element.  Must be called in the
19339      * constructor of all subclasses
19340      * @method initFrame
19341      */
19342     initFrame: function() {
19343         this.createFrame();
19344     },
19345
19346     applyConfig: function() {
19347         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19348
19349         this.resizeFrame = (this.config.resizeFrame !== false);
19350         this.centerFrame = (this.config.centerFrame);
19351         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19352     },
19353
19354     /**
19355      * Resizes the drag frame to the dimensions of the clicked object, positions
19356      * it over the object, and finally displays it
19357      * @method showFrame
19358      * @param {int} iPageX X click position
19359      * @param {int} iPageY Y click position
19360      * @private
19361      */
19362     showFrame: function(iPageX, iPageY) {
19363         var el = this.getEl();
19364         var dragEl = this.getDragEl();
19365         var s = dragEl.style;
19366
19367         this._resizeProxy();
19368
19369         if (this.centerFrame) {
19370             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19371                            Math.round(parseInt(s.height, 10)/2) );
19372         }
19373
19374         this.setDragElPos(iPageX, iPageY);
19375
19376         Roo.fly(dragEl).show();
19377     },
19378
19379     /**
19380      * The proxy is automatically resized to the dimensions of the linked
19381      * element when a drag is initiated, unless resizeFrame is set to false
19382      * @method _resizeProxy
19383      * @private
19384      */
19385     _resizeProxy: function() {
19386         if (this.resizeFrame) {
19387             var el = this.getEl();
19388             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19389         }
19390     },
19391
19392     // overrides Roo.dd.DragDrop
19393     b4MouseDown: function(e) {
19394         var x = e.getPageX();
19395         var y = e.getPageY();
19396         this.autoOffset(x, y);
19397         this.setDragElPos(x, y);
19398     },
19399
19400     // overrides Roo.dd.DragDrop
19401     b4StartDrag: function(x, y) {
19402         // show the drag frame
19403         this.showFrame(x, y);
19404     },
19405
19406     // overrides Roo.dd.DragDrop
19407     b4EndDrag: function(e) {
19408         Roo.fly(this.getDragEl()).hide();
19409     },
19410
19411     // overrides Roo.dd.DragDrop
19412     // By default we try to move the element to the last location of the frame.
19413     // This is so that the default behavior mirrors that of Roo.dd.DD.
19414     endDrag: function(e) {
19415
19416         var lel = this.getEl();
19417         var del = this.getDragEl();
19418
19419         // Show the drag frame briefly so we can get its position
19420         del.style.visibility = "";
19421
19422         this.beforeMove();
19423         // Hide the linked element before the move to get around a Safari
19424         // rendering bug.
19425         lel.style.visibility = "hidden";
19426         Roo.dd.DDM.moveToEl(lel, del);
19427         del.style.visibility = "hidden";
19428         lel.style.visibility = "";
19429
19430         this.afterDrag();
19431     },
19432
19433     beforeMove : function(){
19434
19435     },
19436
19437     afterDrag : function(){
19438
19439     },
19440
19441     toString: function() {
19442         return ("DDProxy " + this.id);
19443     }
19444
19445 });
19446 /*
19447  * Based on:
19448  * Ext JS Library 1.1.1
19449  * Copyright(c) 2006-2007, Ext JS, LLC.
19450  *
19451  * Originally Released Under LGPL - original licence link has changed is not relivant.
19452  *
19453  * Fork - LGPL
19454  * <script type="text/javascript">
19455  */
19456
19457  /**
19458  * @class Roo.dd.DDTarget
19459  * A DragDrop implementation that does not move, but can be a drop
19460  * target.  You would get the same result by simply omitting implementation
19461  * for the event callbacks, but this way we reduce the processing cost of the
19462  * event listener and the callbacks.
19463  * @extends Roo.dd.DragDrop
19464  * @constructor
19465  * @param {String} id the id of the element that is a drop target
19466  * @param {String} sGroup the group of related DragDrop objects
19467  * @param {object} config an object containing configurable attributes
19468  *                 Valid properties for DDTarget in addition to those in
19469  *                 DragDrop:
19470  *                    none
19471  */
19472 Roo.dd.DDTarget = function(id, sGroup, config) {
19473     if (id) {
19474         this.initTarget(id, sGroup, config);
19475     }
19476     if (config.listeners || config.events) { 
19477        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19478             listeners : config.listeners || {}, 
19479             events : config.events || {} 
19480         });    
19481     }
19482 };
19483
19484 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19485 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19486     toString: function() {
19487         return ("DDTarget " + this.id);
19488     }
19489 });
19490 /*
19491  * Based on:
19492  * Ext JS Library 1.1.1
19493  * Copyright(c) 2006-2007, Ext JS, LLC.
19494  *
19495  * Originally Released Under LGPL - original licence link has changed is not relivant.
19496  *
19497  * Fork - LGPL
19498  * <script type="text/javascript">
19499  */
19500  
19501
19502 /**
19503  * @class Roo.dd.ScrollManager
19504  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19505  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19506  * @singleton
19507  */
19508 Roo.dd.ScrollManager = function(){
19509     var ddm = Roo.dd.DragDropMgr;
19510     var els = {};
19511     var dragEl = null;
19512     var proc = {};
19513     
19514     
19515     
19516     var onStop = function(e){
19517         dragEl = null;
19518         clearProc();
19519     };
19520     
19521     var triggerRefresh = function(){
19522         if(ddm.dragCurrent){
19523              ddm.refreshCache(ddm.dragCurrent.groups);
19524         }
19525     };
19526     
19527     var doScroll = function(){
19528         if(ddm.dragCurrent){
19529             var dds = Roo.dd.ScrollManager;
19530             if(!dds.animate){
19531                 if(proc.el.scroll(proc.dir, dds.increment)){
19532                     triggerRefresh();
19533                 }
19534             }else{
19535                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19536             }
19537         }
19538     };
19539     
19540     var clearProc = function(){
19541         if(proc.id){
19542             clearInterval(proc.id);
19543         }
19544         proc.id = 0;
19545         proc.el = null;
19546         proc.dir = "";
19547     };
19548     
19549     var startProc = function(el, dir){
19550          Roo.log('scroll startproc');
19551         clearProc();
19552         proc.el = el;
19553         proc.dir = dir;
19554         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19555     };
19556     
19557     var onFire = function(e, isDrop){
19558        
19559         if(isDrop || !ddm.dragCurrent){ return; }
19560         var dds = Roo.dd.ScrollManager;
19561         if(!dragEl || dragEl != ddm.dragCurrent){
19562             dragEl = ddm.dragCurrent;
19563             // refresh regions on drag start
19564             dds.refreshCache();
19565         }
19566         
19567         var xy = Roo.lib.Event.getXY(e);
19568         var pt = new Roo.lib.Point(xy[0], xy[1]);
19569         for(var id in els){
19570             var el = els[id], r = el._region;
19571             if(r && r.contains(pt) && el.isScrollable()){
19572                 if(r.bottom - pt.y <= dds.thresh){
19573                     if(proc.el != el){
19574                         startProc(el, "down");
19575                     }
19576                     return;
19577                 }else if(r.right - pt.x <= dds.thresh){
19578                     if(proc.el != el){
19579                         startProc(el, "left");
19580                     }
19581                     return;
19582                 }else if(pt.y - r.top <= dds.thresh){
19583                     if(proc.el != el){
19584                         startProc(el, "up");
19585                     }
19586                     return;
19587                 }else if(pt.x - r.left <= dds.thresh){
19588                     if(proc.el != el){
19589                         startProc(el, "right");
19590                     }
19591                     return;
19592                 }
19593             }
19594         }
19595         clearProc();
19596     };
19597     
19598     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19599     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19600     
19601     return {
19602         /**
19603          * Registers new overflow element(s) to auto scroll
19604          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19605          */
19606         register : function(el){
19607             if(el instanceof Array){
19608                 for(var i = 0, len = el.length; i < len; i++) {
19609                         this.register(el[i]);
19610                 }
19611             }else{
19612                 el = Roo.get(el);
19613                 els[el.id] = el;
19614             }
19615             Roo.dd.ScrollManager.els = els;
19616         },
19617         
19618         /**
19619          * Unregisters overflow element(s) so they are no longer scrolled
19620          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19621          */
19622         unregister : function(el){
19623             if(el instanceof Array){
19624                 for(var i = 0, len = el.length; i < len; i++) {
19625                         this.unregister(el[i]);
19626                 }
19627             }else{
19628                 el = Roo.get(el);
19629                 delete els[el.id];
19630             }
19631         },
19632         
19633         /**
19634          * The number of pixels from the edge of a container the pointer needs to be to 
19635          * trigger scrolling (defaults to 25)
19636          * @type Number
19637          */
19638         thresh : 25,
19639         
19640         /**
19641          * The number of pixels to scroll in each scroll increment (defaults to 50)
19642          * @type Number
19643          */
19644         increment : 100,
19645         
19646         /**
19647          * The frequency of scrolls in milliseconds (defaults to 500)
19648          * @type Number
19649          */
19650         frequency : 500,
19651         
19652         /**
19653          * True to animate the scroll (defaults to true)
19654          * @type Boolean
19655          */
19656         animate: true,
19657         
19658         /**
19659          * The animation duration in seconds - 
19660          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19661          * @type Number
19662          */
19663         animDuration: .4,
19664         
19665         /**
19666          * Manually trigger a cache refresh.
19667          */
19668         refreshCache : function(){
19669             for(var id in els){
19670                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19671                     els[id]._region = els[id].getRegion();
19672                 }
19673             }
19674         }
19675     };
19676 }();/*
19677  * Based on:
19678  * Ext JS Library 1.1.1
19679  * Copyright(c) 2006-2007, Ext JS, LLC.
19680  *
19681  * Originally Released Under LGPL - original licence link has changed is not relivant.
19682  *
19683  * Fork - LGPL
19684  * <script type="text/javascript">
19685  */
19686  
19687
19688 /**
19689  * @class Roo.dd.Registry
19690  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19691  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19692  * @singleton
19693  */
19694 Roo.dd.Registry = function(){
19695     var elements = {}; 
19696     var handles = {}; 
19697     var autoIdSeed = 0;
19698
19699     var getId = function(el, autogen){
19700         if(typeof el == "string"){
19701             return el;
19702         }
19703         var id = el.id;
19704         if(!id && autogen !== false){
19705             id = "roodd-" + (++autoIdSeed);
19706             el.id = id;
19707         }
19708         return id;
19709     };
19710     
19711     return {
19712     /**
19713      * Register a drag drop element
19714      * @param {String|HTMLElement} element The id or DOM node to register
19715      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19716      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19717      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19718      * populated in the data object (if applicable):
19719      * <pre>
19720 Value      Description<br />
19721 ---------  ------------------------------------------<br />
19722 handles    Array of DOM nodes that trigger dragging<br />
19723            for the element being registered<br />
19724 isHandle   True if the element passed in triggers<br />
19725            dragging itself, else false
19726 </pre>
19727      */
19728         register : function(el, data){
19729             data = data || {};
19730             if(typeof el == "string"){
19731                 el = document.getElementById(el);
19732             }
19733             data.ddel = el;
19734             elements[getId(el)] = data;
19735             if(data.isHandle !== false){
19736                 handles[data.ddel.id] = data;
19737             }
19738             if(data.handles){
19739                 var hs = data.handles;
19740                 for(var i = 0, len = hs.length; i < len; i++){
19741                         handles[getId(hs[i])] = data;
19742                 }
19743             }
19744         },
19745
19746     /**
19747      * Unregister a drag drop element
19748      * @param {String|HTMLElement}  element The id or DOM node to unregister
19749      */
19750         unregister : function(el){
19751             var id = getId(el, false);
19752             var data = elements[id];
19753             if(data){
19754                 delete elements[id];
19755                 if(data.handles){
19756                     var hs = data.handles;
19757                     for(var i = 0, len = hs.length; i < len; i++){
19758                         delete handles[getId(hs[i], false)];
19759                     }
19760                 }
19761             }
19762         },
19763
19764     /**
19765      * Returns the handle registered for a DOM Node by id
19766      * @param {String|HTMLElement} id The DOM node or id to look up
19767      * @return {Object} handle The custom handle data
19768      */
19769         getHandle : function(id){
19770             if(typeof id != "string"){ // must be element?
19771                 id = id.id;
19772             }
19773             return handles[id];
19774         },
19775
19776     /**
19777      * Returns the handle that is registered for the DOM node that is the target of the event
19778      * @param {Event} e The event
19779      * @return {Object} handle The custom handle data
19780      */
19781         getHandleFromEvent : function(e){
19782             var t = Roo.lib.Event.getTarget(e);
19783             return t ? handles[t.id] : null;
19784         },
19785
19786     /**
19787      * Returns a custom data object that is registered for a DOM node by id
19788      * @param {String|HTMLElement} id The DOM node or id to look up
19789      * @return {Object} data The custom data
19790      */
19791         getTarget : function(id){
19792             if(typeof id != "string"){ // must be element?
19793                 id = id.id;
19794             }
19795             return elements[id];
19796         },
19797
19798     /**
19799      * Returns a custom data object that is registered for the DOM node that is the target of the event
19800      * @param {Event} e The event
19801      * @return {Object} data The custom data
19802      */
19803         getTargetFromEvent : function(e){
19804             var t = Roo.lib.Event.getTarget(e);
19805             return t ? elements[t.id] || handles[t.id] : null;
19806         }
19807     };
19808 }();/*
19809  * Based on:
19810  * Ext JS Library 1.1.1
19811  * Copyright(c) 2006-2007, Ext JS, LLC.
19812  *
19813  * Originally Released Under LGPL - original licence link has changed is not relivant.
19814  *
19815  * Fork - LGPL
19816  * <script type="text/javascript">
19817  */
19818  
19819
19820 /**
19821  * @class Roo.dd.StatusProxy
19822  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19823  * default drag proxy used by all Roo.dd components.
19824  * @constructor
19825  * @param {Object} config
19826  */
19827 Roo.dd.StatusProxy = function(config){
19828     Roo.apply(this, config);
19829     this.id = this.id || Roo.id();
19830     this.el = new Roo.Layer({
19831         dh: {
19832             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19833                 {tag: "div", cls: "x-dd-drop-icon"},
19834                 {tag: "div", cls: "x-dd-drag-ghost"}
19835             ]
19836         }, 
19837         shadow: !config || config.shadow !== false
19838     });
19839     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19840     this.dropStatus = this.dropNotAllowed;
19841 };
19842
19843 Roo.dd.StatusProxy.prototype = {
19844     /**
19845      * @cfg {String} dropAllowed
19846      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19847      */
19848     dropAllowed : "x-dd-drop-ok",
19849     /**
19850      * @cfg {String} dropNotAllowed
19851      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19852      */
19853     dropNotAllowed : "x-dd-drop-nodrop",
19854
19855     /**
19856      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19857      * over the current target element.
19858      * @param {String} cssClass The css class for the new drop status indicator image
19859      */
19860     setStatus : function(cssClass){
19861         cssClass = cssClass || this.dropNotAllowed;
19862         if(this.dropStatus != cssClass){
19863             this.el.replaceClass(this.dropStatus, cssClass);
19864             this.dropStatus = cssClass;
19865         }
19866     },
19867
19868     /**
19869      * Resets the status indicator to the default dropNotAllowed value
19870      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19871      */
19872     reset : function(clearGhost){
19873         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19874         this.dropStatus = this.dropNotAllowed;
19875         if(clearGhost){
19876             this.ghost.update("");
19877         }
19878     },
19879
19880     /**
19881      * Updates the contents of the ghost element
19882      * @param {String} html The html that will replace the current innerHTML of the ghost element
19883      */
19884     update : function(html){
19885         if(typeof html == "string"){
19886             this.ghost.update(html);
19887         }else{
19888             this.ghost.update("");
19889             html.style.margin = "0";
19890             this.ghost.dom.appendChild(html);
19891         }
19892         // ensure float = none set?? cant remember why though.
19893         var el = this.ghost.dom.firstChild;
19894                 if(el){
19895                         Roo.fly(el).setStyle('float', 'none');
19896                 }
19897     },
19898     
19899     /**
19900      * Returns the underlying proxy {@link Roo.Layer}
19901      * @return {Roo.Layer} el
19902     */
19903     getEl : function(){
19904         return this.el;
19905     },
19906
19907     /**
19908      * Returns the ghost element
19909      * @return {Roo.Element} el
19910      */
19911     getGhost : function(){
19912         return this.ghost;
19913     },
19914
19915     /**
19916      * Hides the proxy
19917      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19918      */
19919     hide : function(clear){
19920         this.el.hide();
19921         if(clear){
19922             this.reset(true);
19923         }
19924     },
19925
19926     /**
19927      * Stops the repair animation if it's currently running
19928      */
19929     stop : function(){
19930         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19931             this.anim.stop();
19932         }
19933     },
19934
19935     /**
19936      * Displays this proxy
19937      */
19938     show : function(){
19939         this.el.show();
19940     },
19941
19942     /**
19943      * Force the Layer to sync its shadow and shim positions to the element
19944      */
19945     sync : function(){
19946         this.el.sync();
19947     },
19948
19949     /**
19950      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19951      * invalid drop operation by the item being dragged.
19952      * @param {Array} xy The XY position of the element ([x, y])
19953      * @param {Function} callback The function to call after the repair is complete
19954      * @param {Object} scope The scope in which to execute the callback
19955      */
19956     repair : function(xy, callback, scope){
19957         this.callback = callback;
19958         this.scope = scope;
19959         if(xy && this.animRepair !== false){
19960             this.el.addClass("x-dd-drag-repair");
19961             this.el.hideUnders(true);
19962             this.anim = this.el.shift({
19963                 duration: this.repairDuration || .5,
19964                 easing: 'easeOut',
19965                 xy: xy,
19966                 stopFx: true,
19967                 callback: this.afterRepair,
19968                 scope: this
19969             });
19970         }else{
19971             this.afterRepair();
19972         }
19973     },
19974
19975     // private
19976     afterRepair : function(){
19977         this.hide(true);
19978         if(typeof this.callback == "function"){
19979             this.callback.call(this.scope || this);
19980         }
19981         this.callback = null;
19982         this.scope = null;
19983     }
19984 };/*
19985  * Based on:
19986  * Ext JS Library 1.1.1
19987  * Copyright(c) 2006-2007, Ext JS, LLC.
19988  *
19989  * Originally Released Under LGPL - original licence link has changed is not relivant.
19990  *
19991  * Fork - LGPL
19992  * <script type="text/javascript">
19993  */
19994
19995 /**
19996  * @class Roo.dd.DragSource
19997  * @extends Roo.dd.DDProxy
19998  * A simple class that provides the basic implementation needed to make any element draggable.
19999  * @constructor
20000  * @param {String/HTMLElement/Element} el The container element
20001  * @param {Object} config
20002  */
20003 Roo.dd.DragSource = function(el, config){
20004     this.el = Roo.get(el);
20005     this.dragData = {};
20006     
20007     Roo.apply(this, config);
20008     
20009     if(!this.proxy){
20010         this.proxy = new Roo.dd.StatusProxy();
20011     }
20012
20013     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
20014           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
20015     
20016     this.dragging = false;
20017 };
20018
20019 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
20020     /**
20021      * @cfg {String} dropAllowed
20022      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20023      */
20024     dropAllowed : "x-dd-drop-ok",
20025     /**
20026      * @cfg {String} dropNotAllowed
20027      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20028      */
20029     dropNotAllowed : "x-dd-drop-nodrop",
20030
20031     /**
20032      * Returns the data object associated with this drag source
20033      * @return {Object} data An object containing arbitrary data
20034      */
20035     getDragData : function(e){
20036         return this.dragData;
20037     },
20038
20039     // private
20040     onDragEnter : function(e, id){
20041         var target = Roo.dd.DragDropMgr.getDDById(id);
20042         this.cachedTarget = target;
20043         if(this.beforeDragEnter(target, e, id) !== false){
20044             if(target.isNotifyTarget){
20045                 var status = target.notifyEnter(this, e, this.dragData);
20046                 this.proxy.setStatus(status);
20047             }else{
20048                 this.proxy.setStatus(this.dropAllowed);
20049             }
20050             
20051             if(this.afterDragEnter){
20052                 /**
20053                  * An empty function by default, but provided so that you can perform a custom action
20054                  * when the dragged item enters the drop target by providing an implementation.
20055                  * @param {Roo.dd.DragDrop} target The drop target
20056                  * @param {Event} e The event object
20057                  * @param {String} id The id of the dragged element
20058                  * @method afterDragEnter
20059                  */
20060                 this.afterDragEnter(target, e, id);
20061             }
20062         }
20063     },
20064
20065     /**
20066      * An empty function by default, but provided so that you can perform a custom action
20067      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20068      * @param {Roo.dd.DragDrop} target The drop target
20069      * @param {Event} e The event object
20070      * @param {String} id The id of the dragged element
20071      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20072      */
20073     beforeDragEnter : function(target, e, id){
20074         return true;
20075     },
20076
20077     // private
20078     alignElWithMouse: function() {
20079         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20080         this.proxy.sync();
20081     },
20082
20083     // private
20084     onDragOver : function(e, id){
20085         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20086         if(this.beforeDragOver(target, e, id) !== false){
20087             if(target.isNotifyTarget){
20088                 var status = target.notifyOver(this, e, this.dragData);
20089                 this.proxy.setStatus(status);
20090             }
20091
20092             if(this.afterDragOver){
20093                 /**
20094                  * An empty function by default, but provided so that you can perform a custom action
20095                  * while the dragged item is over the drop target by providing an implementation.
20096                  * @param {Roo.dd.DragDrop} target The drop target
20097                  * @param {Event} e The event object
20098                  * @param {String} id The id of the dragged element
20099                  * @method afterDragOver
20100                  */
20101                 this.afterDragOver(target, e, id);
20102             }
20103         }
20104     },
20105
20106     /**
20107      * An empty function by default, but provided so that you can perform a custom action
20108      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20109      * @param {Roo.dd.DragDrop} target The drop target
20110      * @param {Event} e The event object
20111      * @param {String} id The id of the dragged element
20112      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20113      */
20114     beforeDragOver : function(target, e, id){
20115         return true;
20116     },
20117
20118     // private
20119     onDragOut : function(e, id){
20120         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20121         if(this.beforeDragOut(target, e, id) !== false){
20122             if(target.isNotifyTarget){
20123                 target.notifyOut(this, e, this.dragData);
20124             }
20125             this.proxy.reset();
20126             if(this.afterDragOut){
20127                 /**
20128                  * An empty function by default, but provided so that you can perform a custom action
20129                  * after the dragged item is dragged out of the target without dropping.
20130                  * @param {Roo.dd.DragDrop} target The drop target
20131                  * @param {Event} e The event object
20132                  * @param {String} id The id of the dragged element
20133                  * @method afterDragOut
20134                  */
20135                 this.afterDragOut(target, e, id);
20136             }
20137         }
20138         this.cachedTarget = null;
20139     },
20140
20141     /**
20142      * An empty function by default, but provided so that you can perform a custom action before the dragged
20143      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20144      * @param {Roo.dd.DragDrop} target The drop target
20145      * @param {Event} e The event object
20146      * @param {String} id The id of the dragged element
20147      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20148      */
20149     beforeDragOut : function(target, e, id){
20150         return true;
20151     },
20152     
20153     // private
20154     onDragDrop : function(e, id){
20155         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20156         if(this.beforeDragDrop(target, e, id) !== false){
20157             if(target.isNotifyTarget){
20158                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20159                     this.onValidDrop(target, e, id);
20160                 }else{
20161                     this.onInvalidDrop(target, e, id);
20162                 }
20163             }else{
20164                 this.onValidDrop(target, e, id);
20165             }
20166             
20167             if(this.afterDragDrop){
20168                 /**
20169                  * An empty function by default, but provided so that you can perform a custom action
20170                  * after a valid drag drop has occurred by providing an implementation.
20171                  * @param {Roo.dd.DragDrop} target The drop target
20172                  * @param {Event} e The event object
20173                  * @param {String} id The id of the dropped element
20174                  * @method afterDragDrop
20175                  */
20176                 this.afterDragDrop(target, e, id);
20177             }
20178         }
20179         delete this.cachedTarget;
20180     },
20181
20182     /**
20183      * An empty function by default, but provided so that you can perform a custom action before the dragged
20184      * item is dropped onto the target and optionally cancel the onDragDrop.
20185      * @param {Roo.dd.DragDrop} target The drop target
20186      * @param {Event} e The event object
20187      * @param {String} id The id of the dragged element
20188      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20189      */
20190     beforeDragDrop : function(target, e, id){
20191         return true;
20192     },
20193
20194     // private
20195     onValidDrop : function(target, e, id){
20196         this.hideProxy();
20197         if(this.afterValidDrop){
20198             /**
20199              * An empty function by default, but provided so that you can perform a custom action
20200              * after a valid drop has occurred by providing an implementation.
20201              * @param {Object} target The target DD 
20202              * @param {Event} e The event object
20203              * @param {String} id The id of the dropped element
20204              * @method afterInvalidDrop
20205              */
20206             this.afterValidDrop(target, e, id);
20207         }
20208     },
20209
20210     // private
20211     getRepairXY : function(e, data){
20212         return this.el.getXY();  
20213     },
20214
20215     // private
20216     onInvalidDrop : function(target, e, id){
20217         this.beforeInvalidDrop(target, e, id);
20218         if(this.cachedTarget){
20219             if(this.cachedTarget.isNotifyTarget){
20220                 this.cachedTarget.notifyOut(this, e, this.dragData);
20221             }
20222             this.cacheTarget = null;
20223         }
20224         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20225
20226         if(this.afterInvalidDrop){
20227             /**
20228              * An empty function by default, but provided so that you can perform a custom action
20229              * after an invalid drop has occurred by providing an implementation.
20230              * @param {Event} e The event object
20231              * @param {String} id The id of the dropped element
20232              * @method afterInvalidDrop
20233              */
20234             this.afterInvalidDrop(e, id);
20235         }
20236     },
20237
20238     // private
20239     afterRepair : function(){
20240         if(Roo.enableFx){
20241             this.el.highlight(this.hlColor || "c3daf9");
20242         }
20243         this.dragging = false;
20244     },
20245
20246     /**
20247      * An empty function by default, but provided so that you can perform a custom action after an invalid
20248      * drop has occurred.
20249      * @param {Roo.dd.DragDrop} target The drop target
20250      * @param {Event} e The event object
20251      * @param {String} id The id of the dragged element
20252      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20253      */
20254     beforeInvalidDrop : function(target, e, id){
20255         return true;
20256     },
20257
20258     // private
20259     handleMouseDown : function(e){
20260         if(this.dragging) {
20261             return;
20262         }
20263         var data = this.getDragData(e);
20264         if(data && this.onBeforeDrag(data, e) !== false){
20265             this.dragData = data;
20266             this.proxy.stop();
20267             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20268         } 
20269     },
20270
20271     /**
20272      * An empty function by default, but provided so that you can perform a custom action before the initial
20273      * drag event begins and optionally cancel it.
20274      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20275      * @param {Event} e The event object
20276      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20277      */
20278     onBeforeDrag : function(data, e){
20279         return true;
20280     },
20281
20282     /**
20283      * An empty function by default, but provided so that you can perform a custom action once the initial
20284      * drag event has begun.  The drag cannot be canceled from this function.
20285      * @param {Number} x The x position of the click on the dragged object
20286      * @param {Number} y The y position of the click on the dragged object
20287      */
20288     onStartDrag : Roo.emptyFn,
20289
20290     // private - YUI override
20291     startDrag : function(x, y){
20292         this.proxy.reset();
20293         this.dragging = true;
20294         this.proxy.update("");
20295         this.onInitDrag(x, y);
20296         this.proxy.show();
20297     },
20298
20299     // private
20300     onInitDrag : function(x, y){
20301         var clone = this.el.dom.cloneNode(true);
20302         clone.id = Roo.id(); // prevent duplicate ids
20303         this.proxy.update(clone);
20304         this.onStartDrag(x, y);
20305         return true;
20306     },
20307
20308     /**
20309      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20310      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20311      */
20312     getProxy : function(){
20313         return this.proxy;  
20314     },
20315
20316     /**
20317      * Hides the drag source's {@link Roo.dd.StatusProxy}
20318      */
20319     hideProxy : function(){
20320         this.proxy.hide();  
20321         this.proxy.reset(true);
20322         this.dragging = false;
20323     },
20324
20325     // private
20326     triggerCacheRefresh : function(){
20327         Roo.dd.DDM.refreshCache(this.groups);
20328     },
20329
20330     // private - override to prevent hiding
20331     b4EndDrag: function(e) {
20332     },
20333
20334     // private - override to prevent moving
20335     endDrag : function(e){
20336         this.onEndDrag(this.dragData, e);
20337     },
20338
20339     // private
20340     onEndDrag : function(data, e){
20341     },
20342     
20343     // private - pin to cursor
20344     autoOffset : function(x, y) {
20345         this.setDelta(-12, -20);
20346     }    
20347 });/*
20348  * Based on:
20349  * Ext JS Library 1.1.1
20350  * Copyright(c) 2006-2007, Ext JS, LLC.
20351  *
20352  * Originally Released Under LGPL - original licence link has changed is not relivant.
20353  *
20354  * Fork - LGPL
20355  * <script type="text/javascript">
20356  */
20357
20358
20359 /**
20360  * @class Roo.dd.DropTarget
20361  * @extends Roo.dd.DDTarget
20362  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20363  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20364  * @constructor
20365  * @param {String/HTMLElement/Element} el The container element
20366  * @param {Object} config
20367  */
20368 Roo.dd.DropTarget = function(el, config){
20369     this.el = Roo.get(el);
20370     
20371     var listeners = false; ;
20372     if (config && config.listeners) {
20373         listeners= config.listeners;
20374         delete config.listeners;
20375     }
20376     Roo.apply(this, config);
20377     
20378     if(this.containerScroll){
20379         Roo.dd.ScrollManager.register(this.el);
20380     }
20381     this.addEvents( {
20382          /**
20383          * @scope Roo.dd.DropTarget
20384          */
20385          
20386          /**
20387          * @event enter
20388          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20389          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20390          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20391          * 
20392          * IMPORTANT : it should set this.overClass and this.dropAllowed
20393          * 
20394          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20395          * @param {Event} e The event
20396          * @param {Object} data An object containing arbitrary data supplied by the drag source
20397          */
20398         "enter" : true,
20399         
20400          /**
20401          * @event over
20402          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20403          * This method will be called on every mouse movement while the drag source is over the drop target.
20404          * This default implementation simply returns the dropAllowed config value.
20405          * 
20406          * IMPORTANT : it should set this.dropAllowed
20407          * 
20408          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20409          * @param {Event} e The event
20410          * @param {Object} data An object containing arbitrary data supplied by the drag source
20411          
20412          */
20413         "over" : true,
20414         /**
20415          * @event out
20416          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20417          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20418          * overClass (if any) from the drop element.
20419          * 
20420          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20421          * @param {Event} e The event
20422          * @param {Object} data An object containing arbitrary data supplied by the drag source
20423          */
20424          "out" : true,
20425          
20426         /**
20427          * @event drop
20428          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20429          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20430          * implementation that does something to process the drop event and returns true so that the drag source's
20431          * repair action does not run.
20432          * 
20433          * IMPORTANT : it should set this.success
20434          * 
20435          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20436          * @param {Event} e The event
20437          * @param {Object} data An object containing arbitrary data supplied by the drag source
20438         */
20439          "drop" : true
20440     });
20441             
20442      
20443     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20444         this.el.dom, 
20445         this.ddGroup || this.group,
20446         {
20447             isTarget: true,
20448             listeners : listeners || {} 
20449            
20450         
20451         }
20452     );
20453
20454 };
20455
20456 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20457     /**
20458      * @cfg {String} overClass
20459      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20460      */
20461      /**
20462      * @cfg {String} ddGroup
20463      * The drag drop group to handle drop events for
20464      */
20465      
20466     /**
20467      * @cfg {String} dropAllowed
20468      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20469      */
20470     dropAllowed : "x-dd-drop-ok",
20471     /**
20472      * @cfg {String} dropNotAllowed
20473      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20474      */
20475     dropNotAllowed : "x-dd-drop-nodrop",
20476     /**
20477      * @cfg {boolean} success
20478      * set this after drop listener.. 
20479      */
20480     success : false,
20481     /**
20482      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20483      * if the drop point is valid for over/enter..
20484      */
20485     valid : false,
20486     // private
20487     isTarget : true,
20488
20489     // private
20490     isNotifyTarget : true,
20491     
20492     /**
20493      * @hide
20494      */
20495     notifyEnter : function(dd, e, data)
20496     {
20497         this.valid = true;
20498         this.fireEvent('enter', dd, e, data);
20499         if(this.overClass){
20500             this.el.addClass(this.overClass);
20501         }
20502         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20503             this.valid ? this.dropAllowed : this.dropNotAllowed
20504         );
20505     },
20506
20507     /**
20508      * @hide
20509      */
20510     notifyOver : function(dd, e, data)
20511     {
20512         this.valid = true;
20513         this.fireEvent('over', dd, e, data);
20514         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20515             this.valid ? this.dropAllowed : this.dropNotAllowed
20516         );
20517     },
20518
20519     /**
20520      * @hide
20521      */
20522     notifyOut : function(dd, e, data)
20523     {
20524         this.fireEvent('out', dd, e, data);
20525         if(this.overClass){
20526             this.el.removeClass(this.overClass);
20527         }
20528     },
20529
20530     /**
20531      * @hide
20532      */
20533     notifyDrop : function(dd, e, data)
20534     {
20535         this.success = false;
20536         this.fireEvent('drop', dd, e, data);
20537         return this.success;
20538     }
20539 });/*
20540  * Based on:
20541  * Ext JS Library 1.1.1
20542  * Copyright(c) 2006-2007, Ext JS, LLC.
20543  *
20544  * Originally Released Under LGPL - original licence link has changed is not relivant.
20545  *
20546  * Fork - LGPL
20547  * <script type="text/javascript">
20548  */
20549
20550
20551 /**
20552  * @class Roo.dd.DragZone
20553  * @extends Roo.dd.DragSource
20554  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20555  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20556  * @constructor
20557  * @param {String/HTMLElement/Element} el The container element
20558  * @param {Object} config
20559  */
20560 Roo.dd.DragZone = function(el, config){
20561     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20562     if(this.containerScroll){
20563         Roo.dd.ScrollManager.register(this.el);
20564     }
20565 };
20566
20567 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20568     /**
20569      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20570      * for auto scrolling during drag operations.
20571      */
20572     /**
20573      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20574      * method after a failed drop (defaults to "c3daf9" - light blue)
20575      */
20576
20577     /**
20578      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20579      * for a valid target to drag based on the mouse down. Override this method
20580      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20581      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20582      * @param {EventObject} e The mouse down event
20583      * @return {Object} The dragData
20584      */
20585     getDragData : function(e){
20586         return Roo.dd.Registry.getHandleFromEvent(e);
20587     },
20588     
20589     /**
20590      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20591      * this.dragData.ddel
20592      * @param {Number} x The x position of the click on the dragged object
20593      * @param {Number} y The y position of the click on the dragged object
20594      * @return {Boolean} true to continue the drag, false to cancel
20595      */
20596     onInitDrag : function(x, y){
20597         this.proxy.update(this.dragData.ddel.cloneNode(true));
20598         this.onStartDrag(x, y);
20599         return true;
20600     },
20601     
20602     /**
20603      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20604      */
20605     afterRepair : function(){
20606         if(Roo.enableFx){
20607             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20608         }
20609         this.dragging = false;
20610     },
20611
20612     /**
20613      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20614      * the XY of this.dragData.ddel
20615      * @param {EventObject} e The mouse up event
20616      * @return {Array} The xy location (e.g. [100, 200])
20617      */
20618     getRepairXY : function(e){
20619         return Roo.Element.fly(this.dragData.ddel).getXY();  
20620     }
20621 });/*
20622  * Based on:
20623  * Ext JS Library 1.1.1
20624  * Copyright(c) 2006-2007, Ext JS, LLC.
20625  *
20626  * Originally Released Under LGPL - original licence link has changed is not relivant.
20627  *
20628  * Fork - LGPL
20629  * <script type="text/javascript">
20630  */
20631 /**
20632  * @class Roo.dd.DropZone
20633  * @extends Roo.dd.DropTarget
20634  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20635  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20636  * @constructor
20637  * @param {String/HTMLElement/Element} el The container element
20638  * @param {Object} config
20639  */
20640 Roo.dd.DropZone = function(el, config){
20641     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20642 };
20643
20644 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20645     /**
20646      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20647      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20648      * provide your own custom lookup.
20649      * @param {Event} e The event
20650      * @return {Object} data The custom data
20651      */
20652     getTargetFromEvent : function(e){
20653         return Roo.dd.Registry.getTargetFromEvent(e);
20654     },
20655
20656     /**
20657      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20658      * that it has registered.  This method has no default implementation and should be overridden to provide
20659      * node-specific processing if necessary.
20660      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20661      * {@link #getTargetFromEvent} for this node)
20662      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20663      * @param {Event} e The event
20664      * @param {Object} data An object containing arbitrary data supplied by the drag source
20665      */
20666     onNodeEnter : function(n, dd, e, data){
20667         
20668     },
20669
20670     /**
20671      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20672      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20673      * overridden to provide the proper feedback.
20674      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20675      * {@link #getTargetFromEvent} for this node)
20676      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20677      * @param {Event} e The event
20678      * @param {Object} data An object containing arbitrary data supplied by the drag source
20679      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20680      * underlying {@link Roo.dd.StatusProxy} can be updated
20681      */
20682     onNodeOver : function(n, dd, e, data){
20683         return this.dropAllowed;
20684     },
20685
20686     /**
20687      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20688      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20689      * node-specific processing if necessary.
20690      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20691      * {@link #getTargetFromEvent} for this node)
20692      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20693      * @param {Event} e The event
20694      * @param {Object} data An object containing arbitrary data supplied by the drag source
20695      */
20696     onNodeOut : function(n, dd, e, data){
20697         
20698     },
20699
20700     /**
20701      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20702      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20703      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20704      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20705      * {@link #getTargetFromEvent} for this node)
20706      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20707      * @param {Event} e The event
20708      * @param {Object} data An object containing arbitrary data supplied by the drag source
20709      * @return {Boolean} True if the drop was valid, else false
20710      */
20711     onNodeDrop : function(n, dd, e, data){
20712         return false;
20713     },
20714
20715     /**
20716      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20717      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20718      * it should be overridden to provide the proper feedback if necessary.
20719      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20720      * @param {Event} e The event
20721      * @param {Object} data An object containing arbitrary data supplied by the drag source
20722      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20723      * underlying {@link Roo.dd.StatusProxy} can be updated
20724      */
20725     onContainerOver : function(dd, e, data){
20726         return this.dropNotAllowed;
20727     },
20728
20729     /**
20730      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20731      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20732      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20733      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20734      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20735      * @param {Event} e The event
20736      * @param {Object} data An object containing arbitrary data supplied by the drag source
20737      * @return {Boolean} True if the drop was valid, else false
20738      */
20739     onContainerDrop : function(dd, e, data){
20740         return false;
20741     },
20742
20743     /**
20744      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20745      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20746      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20747      * you should override this method and provide a custom implementation.
20748      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20749      * @param {Event} e The event
20750      * @param {Object} data An object containing arbitrary data supplied by the drag source
20751      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20752      * underlying {@link Roo.dd.StatusProxy} can be updated
20753      */
20754     notifyEnter : function(dd, e, data){
20755         return this.dropNotAllowed;
20756     },
20757
20758     /**
20759      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20760      * This method will be called on every mouse movement while the drag source is over the drop zone.
20761      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20762      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20763      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20764      * registered node, it will call {@link #onContainerOver}.
20765      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20766      * @param {Event} e The event
20767      * @param {Object} data An object containing arbitrary data supplied by the drag source
20768      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20769      * underlying {@link Roo.dd.StatusProxy} can be updated
20770      */
20771     notifyOver : function(dd, e, data){
20772         var n = this.getTargetFromEvent(e);
20773         if(!n){ // not over valid drop target
20774             if(this.lastOverNode){
20775                 this.onNodeOut(this.lastOverNode, dd, e, data);
20776                 this.lastOverNode = null;
20777             }
20778             return this.onContainerOver(dd, e, data);
20779         }
20780         if(this.lastOverNode != n){
20781             if(this.lastOverNode){
20782                 this.onNodeOut(this.lastOverNode, dd, e, data);
20783             }
20784             this.onNodeEnter(n, dd, e, data);
20785             this.lastOverNode = n;
20786         }
20787         return this.onNodeOver(n, dd, e, data);
20788     },
20789
20790     /**
20791      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20792      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20793      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20794      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20795      * @param {Event} e The event
20796      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20797      */
20798     notifyOut : function(dd, e, data){
20799         if(this.lastOverNode){
20800             this.onNodeOut(this.lastOverNode, dd, e, data);
20801             this.lastOverNode = null;
20802         }
20803     },
20804
20805     /**
20806      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20807      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20808      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20809      * otherwise it will call {@link #onContainerDrop}.
20810      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20811      * @param {Event} e The event
20812      * @param {Object} data An object containing arbitrary data supplied by the drag source
20813      * @return {Boolean} True if the drop was valid, else false
20814      */
20815     notifyDrop : function(dd, e, data){
20816         if(this.lastOverNode){
20817             this.onNodeOut(this.lastOverNode, dd, e, data);
20818             this.lastOverNode = null;
20819         }
20820         var n = this.getTargetFromEvent(e);
20821         return n ?
20822             this.onNodeDrop(n, dd, e, data) :
20823             this.onContainerDrop(dd, e, data);
20824     },
20825
20826     // private
20827     triggerCacheRefresh : function(){
20828         Roo.dd.DDM.refreshCache(this.groups);
20829     }  
20830 });/*
20831  * Based on:
20832  * Ext JS Library 1.1.1
20833  * Copyright(c) 2006-2007, Ext JS, LLC.
20834  *
20835  * Originally Released Under LGPL - original licence link has changed is not relivant.
20836  *
20837  * Fork - LGPL
20838  * <script type="text/javascript">
20839  */
20840
20841
20842 /**
20843  * @class Roo.data.SortTypes
20844  * @singleton
20845  * Defines the default sorting (casting?) comparison functions used when sorting data.
20846  */
20847 Roo.data.SortTypes = {
20848     /**
20849      * Default sort that does nothing
20850      * @param {Mixed} s The value being converted
20851      * @return {Mixed} The comparison value
20852      */
20853     none : function(s){
20854         return s;
20855     },
20856     
20857     /**
20858      * The regular expression used to strip tags
20859      * @type {RegExp}
20860      * @property
20861      */
20862     stripTagsRE : /<\/?[^>]+>/gi,
20863     
20864     /**
20865      * Strips all HTML tags to sort on text only
20866      * @param {Mixed} s The value being converted
20867      * @return {String} The comparison value
20868      */
20869     asText : function(s){
20870         return String(s).replace(this.stripTagsRE, "");
20871     },
20872     
20873     /**
20874      * Strips all HTML tags to sort on text only - Case insensitive
20875      * @param {Mixed} s The value being converted
20876      * @return {String} The comparison value
20877      */
20878     asUCText : function(s){
20879         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20880     },
20881     
20882     /**
20883      * Case insensitive string
20884      * @param {Mixed} s The value being converted
20885      * @return {String} The comparison value
20886      */
20887     asUCString : function(s) {
20888         return String(s).toUpperCase();
20889     },
20890     
20891     /**
20892      * Date sorting
20893      * @param {Mixed} s The value being converted
20894      * @return {Number} The comparison value
20895      */
20896     asDate : function(s) {
20897         if(!s){
20898             return 0;
20899         }
20900         if(s instanceof Date){
20901             return s.getTime();
20902         }
20903         return Date.parse(String(s));
20904     },
20905     
20906     /**
20907      * Float sorting
20908      * @param {Mixed} s The value being converted
20909      * @return {Float} The comparison value
20910      */
20911     asFloat : function(s) {
20912         var val = parseFloat(String(s).replace(/,/g, ""));
20913         if(isNaN(val)) val = 0;
20914         return val;
20915     },
20916     
20917     /**
20918      * Integer sorting
20919      * @param {Mixed} s The value being converted
20920      * @return {Number} The comparison value
20921      */
20922     asInt : function(s) {
20923         var val = parseInt(String(s).replace(/,/g, ""));
20924         if(isNaN(val)) val = 0;
20925         return val;
20926     }
20927 };/*
20928  * Based on:
20929  * Ext JS Library 1.1.1
20930  * Copyright(c) 2006-2007, Ext JS, LLC.
20931  *
20932  * Originally Released Under LGPL - original licence link has changed is not relivant.
20933  *
20934  * Fork - LGPL
20935  * <script type="text/javascript">
20936  */
20937
20938 /**
20939 * @class Roo.data.Record
20940  * Instances of this class encapsulate both record <em>definition</em> information, and record
20941  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20942  * to access Records cached in an {@link Roo.data.Store} object.<br>
20943  * <p>
20944  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20945  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20946  * objects.<br>
20947  * <p>
20948  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20949  * @constructor
20950  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20951  * {@link #create}. The parameters are the same.
20952  * @param {Array} data An associative Array of data values keyed by the field name.
20953  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20954  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20955  * not specified an integer id is generated.
20956  */
20957 Roo.data.Record = function(data, id){
20958     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20959     this.data = data;
20960 };
20961
20962 /**
20963  * Generate a constructor for a specific record layout.
20964  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20965  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20966  * Each field definition object may contain the following properties: <ul>
20967  * <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,
20968  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20969  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20970  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20971  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20972  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20973  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20974  * this may be omitted.</p></li>
20975  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20976  * <ul><li>auto (Default, implies no conversion)</li>
20977  * <li>string</li>
20978  * <li>int</li>
20979  * <li>float</li>
20980  * <li>boolean</li>
20981  * <li>date</li></ul></p></li>
20982  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20983  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20984  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20985  * by the Reader into an object that will be stored in the Record. It is passed the
20986  * following parameters:<ul>
20987  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20988  * </ul></p></li>
20989  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20990  * </ul>
20991  * <br>usage:<br><pre><code>
20992 var TopicRecord = Roo.data.Record.create(
20993     {name: 'title', mapping: 'topic_title'},
20994     {name: 'author', mapping: 'username'},
20995     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20996     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20997     {name: 'lastPoster', mapping: 'user2'},
20998     {name: 'excerpt', mapping: 'post_text'}
20999 );
21000
21001 var myNewRecord = new TopicRecord({
21002     title: 'Do my job please',
21003     author: 'noobie',
21004     totalPosts: 1,
21005     lastPost: new Date(),
21006     lastPoster: 'Animal',
21007     excerpt: 'No way dude!'
21008 });
21009 myStore.add(myNewRecord);
21010 </code></pre>
21011  * @method create
21012  * @static
21013  */
21014 Roo.data.Record.create = function(o){
21015     var f = function(){
21016         f.superclass.constructor.apply(this, arguments);
21017     };
21018     Roo.extend(f, Roo.data.Record);
21019     var p = f.prototype;
21020     p.fields = new Roo.util.MixedCollection(false, function(field){
21021         return field.name;
21022     });
21023     for(var i = 0, len = o.length; i < len; i++){
21024         p.fields.add(new Roo.data.Field(o[i]));
21025     }
21026     f.getField = function(name){
21027         return p.fields.get(name);  
21028     };
21029     return f;
21030 };
21031
21032 Roo.data.Record.AUTO_ID = 1000;
21033 Roo.data.Record.EDIT = 'edit';
21034 Roo.data.Record.REJECT = 'reject';
21035 Roo.data.Record.COMMIT = 'commit';
21036
21037 Roo.data.Record.prototype = {
21038     /**
21039      * Readonly flag - true if this record has been modified.
21040      * @type Boolean
21041      */
21042     dirty : false,
21043     editing : false,
21044     error: null,
21045     modified: null,
21046
21047     // private
21048     join : function(store){
21049         this.store = store;
21050     },
21051
21052     /**
21053      * Set the named field to the specified value.
21054      * @param {String} name The name of the field to set.
21055      * @param {Object} value The value to set the field to.
21056      */
21057     set : function(name, value){
21058         if(this.data[name] == value){
21059             return;
21060         }
21061         this.dirty = true;
21062         if(!this.modified){
21063             this.modified = {};
21064         }
21065         if(typeof this.modified[name] == 'undefined'){
21066             this.modified[name] = this.data[name];
21067         }
21068         this.data[name] = value;
21069         if(!this.editing && this.store){
21070             this.store.afterEdit(this);
21071         }       
21072     },
21073
21074     /**
21075      * Get the value of the named field.
21076      * @param {String} name The name of the field to get the value of.
21077      * @return {Object} The value of the field.
21078      */
21079     get : function(name){
21080         return this.data[name]; 
21081     },
21082
21083     // private
21084     beginEdit : function(){
21085         this.editing = true;
21086         this.modified = {}; 
21087     },
21088
21089     // private
21090     cancelEdit : function(){
21091         this.editing = false;
21092         delete this.modified;
21093     },
21094
21095     // private
21096     endEdit : function(){
21097         this.editing = false;
21098         if(this.dirty && this.store){
21099             this.store.afterEdit(this);
21100         }
21101     },
21102
21103     /**
21104      * Usually called by the {@link Roo.data.Store} which owns the Record.
21105      * Rejects all changes made to the Record since either creation, or the last commit operation.
21106      * Modified fields are reverted to their original values.
21107      * <p>
21108      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21109      * of reject operations.
21110      */
21111     reject : function(){
21112         var m = this.modified;
21113         for(var n in m){
21114             if(typeof m[n] != "function"){
21115                 this.data[n] = m[n];
21116             }
21117         }
21118         this.dirty = false;
21119         delete this.modified;
21120         this.editing = false;
21121         if(this.store){
21122             this.store.afterReject(this);
21123         }
21124     },
21125
21126     /**
21127      * Usually called by the {@link Roo.data.Store} which owns the Record.
21128      * Commits all changes made to the Record since either creation, or the last commit operation.
21129      * <p>
21130      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21131      * of commit operations.
21132      */
21133     commit : function(){
21134         this.dirty = false;
21135         delete this.modified;
21136         this.editing = false;
21137         if(this.store){
21138             this.store.afterCommit(this);
21139         }
21140     },
21141
21142     // private
21143     hasError : function(){
21144         return this.error != null;
21145     },
21146
21147     // private
21148     clearError : function(){
21149         this.error = null;
21150     },
21151
21152     /**
21153      * Creates a copy of this record.
21154      * @param {String} id (optional) A new record id if you don't want to use this record's id
21155      * @return {Record}
21156      */
21157     copy : function(newId) {
21158         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21159     }
21160 };/*
21161  * Based on:
21162  * Ext JS Library 1.1.1
21163  * Copyright(c) 2006-2007, Ext JS, LLC.
21164  *
21165  * Originally Released Under LGPL - original licence link has changed is not relivant.
21166  *
21167  * Fork - LGPL
21168  * <script type="text/javascript">
21169  */
21170
21171
21172
21173 /**
21174  * @class Roo.data.Store
21175  * @extends Roo.util.Observable
21176  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21177  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21178  * <p>
21179  * 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
21180  * has no knowledge of the format of the data returned by the Proxy.<br>
21181  * <p>
21182  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21183  * instances from the data object. These records are cached and made available through accessor functions.
21184  * @constructor
21185  * Creates a new Store.
21186  * @param {Object} config A config object containing the objects needed for the Store to access data,
21187  * and read the data into Records.
21188  */
21189 Roo.data.Store = function(config){
21190     this.data = new Roo.util.MixedCollection(false);
21191     this.data.getKey = function(o){
21192         return o.id;
21193     };
21194     this.baseParams = {};
21195     // private
21196     this.paramNames = {
21197         "start" : "start",
21198         "limit" : "limit",
21199         "sort" : "sort",
21200         "dir" : "dir",
21201         "multisort" : "_multisort"
21202     };
21203
21204     if(config && config.data){
21205         this.inlineData = config.data;
21206         delete config.data;
21207     }
21208
21209     Roo.apply(this, config);
21210     
21211     if(this.reader){ // reader passed
21212         this.reader = Roo.factory(this.reader, Roo.data);
21213         this.reader.xmodule = this.xmodule || false;
21214         if(!this.recordType){
21215             this.recordType = this.reader.recordType;
21216         }
21217         if(this.reader.onMetaChange){
21218             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21219         }
21220     }
21221
21222     if(this.recordType){
21223         this.fields = this.recordType.prototype.fields;
21224     }
21225     this.modified = [];
21226
21227     this.addEvents({
21228         /**
21229          * @event datachanged
21230          * Fires when the data cache has changed, and a widget which is using this Store
21231          * as a Record cache should refresh its view.
21232          * @param {Store} this
21233          */
21234         datachanged : true,
21235         /**
21236          * @event metachange
21237          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21238          * @param {Store} this
21239          * @param {Object} meta The JSON metadata
21240          */
21241         metachange : true,
21242         /**
21243          * @event add
21244          * Fires when Records have been added to the Store
21245          * @param {Store} this
21246          * @param {Roo.data.Record[]} records The array of Records added
21247          * @param {Number} index The index at which the record(s) were added
21248          */
21249         add : true,
21250         /**
21251          * @event remove
21252          * Fires when a Record has been removed from the Store
21253          * @param {Store} this
21254          * @param {Roo.data.Record} record The Record that was removed
21255          * @param {Number} index The index at which the record was removed
21256          */
21257         remove : true,
21258         /**
21259          * @event update
21260          * Fires when a Record has been updated
21261          * @param {Store} this
21262          * @param {Roo.data.Record} record The Record that was updated
21263          * @param {String} operation The update operation being performed.  Value may be one of:
21264          * <pre><code>
21265  Roo.data.Record.EDIT
21266  Roo.data.Record.REJECT
21267  Roo.data.Record.COMMIT
21268          * </code></pre>
21269          */
21270         update : true,
21271         /**
21272          * @event clear
21273          * Fires when the data cache has been cleared.
21274          * @param {Store} this
21275          */
21276         clear : true,
21277         /**
21278          * @event beforeload
21279          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21280          * the load action will be canceled.
21281          * @param {Store} this
21282          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21283          */
21284         beforeload : true,
21285         /**
21286          * @event beforeloadadd
21287          * Fires after a new set of Records has been loaded.
21288          * @param {Store} this
21289          * @param {Roo.data.Record[]} records The Records that were loaded
21290          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21291          */
21292         beforeloadadd : true,
21293         /**
21294          * @event load
21295          * Fires after a new set of Records has been loaded, before they are added to the store.
21296          * @param {Store} this
21297          * @param {Roo.data.Record[]} records The Records that were loaded
21298          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21299          * @params {Object} return from reader
21300          */
21301         load : true,
21302         /**
21303          * @event loadexception
21304          * Fires if an exception occurs in the Proxy during loading.
21305          * Called with the signature of the Proxy's "loadexception" event.
21306          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21307          * 
21308          * @param {Proxy} 
21309          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21310          * @param {Object} load options 
21311          * @param {Object} jsonData from your request (normally this contains the Exception)
21312          */
21313         loadexception : true
21314     });
21315     
21316     if(this.proxy){
21317         this.proxy = Roo.factory(this.proxy, Roo.data);
21318         this.proxy.xmodule = this.xmodule || false;
21319         this.relayEvents(this.proxy,  ["loadexception"]);
21320     }
21321     this.sortToggle = {};
21322     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21323
21324     Roo.data.Store.superclass.constructor.call(this);
21325
21326     if(this.inlineData){
21327         this.loadData(this.inlineData);
21328         delete this.inlineData;
21329     }
21330 };
21331
21332 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21333      /**
21334     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21335     * without a remote query - used by combo/forms at present.
21336     */
21337     
21338     /**
21339     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21340     */
21341     /**
21342     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21343     */
21344     /**
21345     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21346     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21347     */
21348     /**
21349     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21350     * on any HTTP request
21351     */
21352     /**
21353     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21354     */
21355     /**
21356     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21357     */
21358     multiSort: false,
21359     /**
21360     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21361     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21362     */
21363     remoteSort : false,
21364
21365     /**
21366     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21367      * loaded or when a record is removed. (defaults to false).
21368     */
21369     pruneModifiedRecords : false,
21370
21371     // private
21372     lastOptions : null,
21373
21374     /**
21375      * Add Records to the Store and fires the add event.
21376      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21377      */
21378     add : function(records){
21379         records = [].concat(records);
21380         for(var i = 0, len = records.length; i < len; i++){
21381             records[i].join(this);
21382         }
21383         var index = this.data.length;
21384         this.data.addAll(records);
21385         this.fireEvent("add", this, records, index);
21386     },
21387
21388     /**
21389      * Remove a Record from the Store and fires the remove event.
21390      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21391      */
21392     remove : function(record){
21393         var index = this.data.indexOf(record);
21394         this.data.removeAt(index);
21395         if(this.pruneModifiedRecords){
21396             this.modified.remove(record);
21397         }
21398         this.fireEvent("remove", this, record, index);
21399     },
21400
21401     /**
21402      * Remove all Records from the Store and fires the clear event.
21403      */
21404     removeAll : function(){
21405         this.data.clear();
21406         if(this.pruneModifiedRecords){
21407             this.modified = [];
21408         }
21409         this.fireEvent("clear", this);
21410     },
21411
21412     /**
21413      * Inserts Records to the Store at the given index and fires the add event.
21414      * @param {Number} index The start index at which to insert the passed Records.
21415      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21416      */
21417     insert : function(index, records){
21418         records = [].concat(records);
21419         for(var i = 0, len = records.length; i < len; i++){
21420             this.data.insert(index, records[i]);
21421             records[i].join(this);
21422         }
21423         this.fireEvent("add", this, records, index);
21424     },
21425
21426     /**
21427      * Get the index within the cache of the passed Record.
21428      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21429      * @return {Number} The index of the passed Record. Returns -1 if not found.
21430      */
21431     indexOf : function(record){
21432         return this.data.indexOf(record);
21433     },
21434
21435     /**
21436      * Get the index within the cache of the Record with the passed id.
21437      * @param {String} id The id of the Record to find.
21438      * @return {Number} The index of the Record. Returns -1 if not found.
21439      */
21440     indexOfId : function(id){
21441         return this.data.indexOfKey(id);
21442     },
21443
21444     /**
21445      * Get the Record with the specified id.
21446      * @param {String} id The id of the Record to find.
21447      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21448      */
21449     getById : function(id){
21450         return this.data.key(id);
21451     },
21452
21453     /**
21454      * Get the Record at the specified index.
21455      * @param {Number} index The index of the Record to find.
21456      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21457      */
21458     getAt : function(index){
21459         return this.data.itemAt(index);
21460     },
21461
21462     /**
21463      * Returns a range of Records between specified indices.
21464      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21465      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21466      * @return {Roo.data.Record[]} An array of Records
21467      */
21468     getRange : function(start, end){
21469         return this.data.getRange(start, end);
21470     },
21471
21472     // private
21473     storeOptions : function(o){
21474         o = Roo.apply({}, o);
21475         delete o.callback;
21476         delete o.scope;
21477         this.lastOptions = o;
21478     },
21479
21480     /**
21481      * Loads the Record cache from the configured Proxy using the configured Reader.
21482      * <p>
21483      * If using remote paging, then the first load call must specify the <em>start</em>
21484      * and <em>limit</em> properties in the options.params property to establish the initial
21485      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21486      * <p>
21487      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21488      * and this call will return before the new data has been loaded. Perform any post-processing
21489      * in a callback function, or in a "load" event handler.</strong>
21490      * <p>
21491      * @param {Object} options An object containing properties which control loading options:<ul>
21492      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21493      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21494      * passed the following arguments:<ul>
21495      * <li>r : Roo.data.Record[]</li>
21496      * <li>options: Options object from the load call</li>
21497      * <li>success: Boolean success indicator</li></ul></li>
21498      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21499      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21500      * </ul>
21501      */
21502     load : function(options){
21503         options = options || {};
21504         if(this.fireEvent("beforeload", this, options) !== false){
21505             this.storeOptions(options);
21506             var p = Roo.apply(options.params || {}, this.baseParams);
21507             // if meta was not loaded from remote source.. try requesting it.
21508             if (!this.reader.metaFromRemote) {
21509                 p._requestMeta = 1;
21510             }
21511             if(this.sortInfo && this.remoteSort){
21512                 var pn = this.paramNames;
21513                 p[pn["sort"]] = this.sortInfo.field;
21514                 p[pn["dir"]] = this.sortInfo.direction;
21515             }
21516             if (this.multiSort) {
21517                 var pn = this.paramNames;
21518                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21519             }
21520             
21521             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21522         }
21523     },
21524
21525     /**
21526      * Reloads the Record cache from the configured Proxy using the configured Reader and
21527      * the options from the last load operation performed.
21528      * @param {Object} options (optional) An object containing properties which may override the options
21529      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21530      * the most recently used options are reused).
21531      */
21532     reload : function(options){
21533         this.load(Roo.applyIf(options||{}, this.lastOptions));
21534     },
21535
21536     // private
21537     // Called as a callback by the Reader during a load operation.
21538     loadRecords : function(o, options, success){
21539         if(!o || success === false){
21540             if(success !== false){
21541                 this.fireEvent("load", this, [], options, o);
21542             }
21543             if(options.callback){
21544                 options.callback.call(options.scope || this, [], options, false);
21545             }
21546             return;
21547         }
21548         // if data returned failure - throw an exception.
21549         if (o.success === false) {
21550             // show a message if no listener is registered.
21551             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21552                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21553             }
21554             // loadmask wil be hooked into this..
21555             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21556             return;
21557         }
21558         var r = o.records, t = o.totalRecords || r.length;
21559         
21560         this.fireEvent("beforeloadadd", this, r, options, o);
21561         
21562         if(!options || options.add !== true){
21563             if(this.pruneModifiedRecords){
21564                 this.modified = [];
21565             }
21566             for(var i = 0, len = r.length; i < len; i++){
21567                 r[i].join(this);
21568             }
21569             if(this.snapshot){
21570                 this.data = this.snapshot;
21571                 delete this.snapshot;
21572             }
21573             this.data.clear();
21574             this.data.addAll(r);
21575             this.totalLength = t;
21576             this.applySort();
21577             this.fireEvent("datachanged", this);
21578         }else{
21579             this.totalLength = Math.max(t, this.data.length+r.length);
21580             this.add(r);
21581         }
21582         this.fireEvent("load", this, r, options, o);
21583         if(options.callback){
21584             options.callback.call(options.scope || this, r, options, true);
21585         }
21586     },
21587
21588
21589     /**
21590      * Loads data from a passed data block. A Reader which understands the format of the data
21591      * must have been configured in the constructor.
21592      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21593      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21594      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21595      */
21596     loadData : function(o, append){
21597         var r = this.reader.readRecords(o);
21598         this.loadRecords(r, {add: append}, true);
21599     },
21600
21601     /**
21602      * Gets the number of cached records.
21603      * <p>
21604      * <em>If using paging, this may not be the total size of the dataset. If the data object
21605      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21606      * the data set size</em>
21607      */
21608     getCount : function(){
21609         return this.data.length || 0;
21610     },
21611
21612     /**
21613      * Gets the total number of records in the dataset as returned by the server.
21614      * <p>
21615      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21616      * the dataset size</em>
21617      */
21618     getTotalCount : function(){
21619         return this.totalLength || 0;
21620     },
21621
21622     /**
21623      * Returns the sort state of the Store as an object with two properties:
21624      * <pre><code>
21625  field {String} The name of the field by which the Records are sorted
21626  direction {String} The sort order, "ASC" or "DESC"
21627      * </code></pre>
21628      */
21629     getSortState : function(){
21630         return this.sortInfo;
21631     },
21632
21633     // private
21634     applySort : function(){
21635         if(this.sortInfo && !this.remoteSort){
21636             var s = this.sortInfo, f = s.field;
21637             var st = this.fields.get(f).sortType;
21638             var fn = function(r1, r2){
21639                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21640                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21641             };
21642             this.data.sort(s.direction, fn);
21643             if(this.snapshot && this.snapshot != this.data){
21644                 this.snapshot.sort(s.direction, fn);
21645             }
21646         }
21647     },
21648
21649     /**
21650      * Sets the default sort column and order to be used by the next load operation.
21651      * @param {String} fieldName The name of the field to sort by.
21652      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21653      */
21654     setDefaultSort : function(field, dir){
21655         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21656     },
21657
21658     /**
21659      * Sort the Records.
21660      * If remote sorting is used, the sort is performed on the server, and the cache is
21661      * reloaded. If local sorting is used, the cache is sorted internally.
21662      * @param {String} fieldName The name of the field to sort by.
21663      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21664      */
21665     sort : function(fieldName, dir){
21666         var f = this.fields.get(fieldName);
21667         if(!dir){
21668             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21669             
21670             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21671                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21672             }else{
21673                 dir = f.sortDir;
21674             }
21675         }
21676         this.sortToggle[f.name] = dir;
21677         this.sortInfo = {field: f.name, direction: dir};
21678         if(!this.remoteSort){
21679             this.applySort();
21680             this.fireEvent("datachanged", this);
21681         }else{
21682             this.load(this.lastOptions);
21683         }
21684     },
21685
21686     /**
21687      * Calls the specified function for each of the Records in the cache.
21688      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21689      * Returning <em>false</em> aborts and exits the iteration.
21690      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21691      */
21692     each : function(fn, scope){
21693         this.data.each(fn, scope);
21694     },
21695
21696     /**
21697      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21698      * (e.g., during paging).
21699      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21700      */
21701     getModifiedRecords : function(){
21702         return this.modified;
21703     },
21704
21705     // private
21706     createFilterFn : function(property, value, anyMatch){
21707         if(!value.exec){ // not a regex
21708             value = String(value);
21709             if(value.length == 0){
21710                 return false;
21711             }
21712             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21713         }
21714         return function(r){
21715             return value.test(r.data[property]);
21716         };
21717     },
21718
21719     /**
21720      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21721      * @param {String} property A field on your records
21722      * @param {Number} start The record index to start at (defaults to 0)
21723      * @param {Number} end The last record index to include (defaults to length - 1)
21724      * @return {Number} The sum
21725      */
21726     sum : function(property, start, end){
21727         var rs = this.data.items, v = 0;
21728         start = start || 0;
21729         end = (end || end === 0) ? end : rs.length-1;
21730
21731         for(var i = start; i <= end; i++){
21732             v += (rs[i].data[property] || 0);
21733         }
21734         return v;
21735     },
21736
21737     /**
21738      * Filter the records by a specified property.
21739      * @param {String} field A field on your records
21740      * @param {String/RegExp} value Either a string that the field
21741      * should start with or a RegExp to test against the field
21742      * @param {Boolean} anyMatch True to match any part not just the beginning
21743      */
21744     filter : function(property, value, anyMatch){
21745         var fn = this.createFilterFn(property, value, anyMatch);
21746         return fn ? this.filterBy(fn) : this.clearFilter();
21747     },
21748
21749     /**
21750      * Filter by a function. The specified function will be called with each
21751      * record in this data source. If the function returns true the record is included,
21752      * otherwise it is filtered.
21753      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21754      * @param {Object} scope (optional) The scope of the function (defaults to this)
21755      */
21756     filterBy : function(fn, scope){
21757         this.snapshot = this.snapshot || this.data;
21758         this.data = this.queryBy(fn, scope||this);
21759         this.fireEvent("datachanged", this);
21760     },
21761
21762     /**
21763      * Query the records by a specified property.
21764      * @param {String} field A field on your records
21765      * @param {String/RegExp} value Either a string that the field
21766      * should start with or a RegExp to test against the field
21767      * @param {Boolean} anyMatch True to match any part not just the beginning
21768      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21769      */
21770     query : function(property, value, anyMatch){
21771         var fn = this.createFilterFn(property, value, anyMatch);
21772         return fn ? this.queryBy(fn) : this.data.clone();
21773     },
21774
21775     /**
21776      * Query by a function. The specified function will be called with each
21777      * record in this data source. If the function returns true the record is included
21778      * in the results.
21779      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21780      * @param {Object} scope (optional) The scope of the function (defaults to this)
21781       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21782      **/
21783     queryBy : function(fn, scope){
21784         var data = this.snapshot || this.data;
21785         return data.filterBy(fn, scope||this);
21786     },
21787
21788     /**
21789      * Collects unique values for a particular dataIndex from this store.
21790      * @param {String} dataIndex The property to collect
21791      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21792      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21793      * @return {Array} An array of the unique values
21794      **/
21795     collect : function(dataIndex, allowNull, bypassFilter){
21796         var d = (bypassFilter === true && this.snapshot) ?
21797                 this.snapshot.items : this.data.items;
21798         var v, sv, r = [], l = {};
21799         for(var i = 0, len = d.length; i < len; i++){
21800             v = d[i].data[dataIndex];
21801             sv = String(v);
21802             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21803                 l[sv] = true;
21804                 r[r.length] = v;
21805             }
21806         }
21807         return r;
21808     },
21809
21810     /**
21811      * Revert to a view of the Record cache with no filtering applied.
21812      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21813      */
21814     clearFilter : function(suppressEvent){
21815         if(this.snapshot && this.snapshot != this.data){
21816             this.data = this.snapshot;
21817             delete this.snapshot;
21818             if(suppressEvent !== true){
21819                 this.fireEvent("datachanged", this);
21820             }
21821         }
21822     },
21823
21824     // private
21825     afterEdit : function(record){
21826         if(this.modified.indexOf(record) == -1){
21827             this.modified.push(record);
21828         }
21829         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21830     },
21831     
21832     // private
21833     afterReject : function(record){
21834         this.modified.remove(record);
21835         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21836     },
21837
21838     // private
21839     afterCommit : function(record){
21840         this.modified.remove(record);
21841         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21842     },
21843
21844     /**
21845      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21846      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21847      */
21848     commitChanges : function(){
21849         var m = this.modified.slice(0);
21850         this.modified = [];
21851         for(var i = 0, len = m.length; i < len; i++){
21852             m[i].commit();
21853         }
21854     },
21855
21856     /**
21857      * Cancel outstanding changes on all changed records.
21858      */
21859     rejectChanges : function(){
21860         var m = this.modified.slice(0);
21861         this.modified = [];
21862         for(var i = 0, len = m.length; i < len; i++){
21863             m[i].reject();
21864         }
21865     },
21866
21867     onMetaChange : function(meta, rtype, o){
21868         this.recordType = rtype;
21869         this.fields = rtype.prototype.fields;
21870         delete this.snapshot;
21871         this.sortInfo = meta.sortInfo || this.sortInfo;
21872         this.modified = [];
21873         this.fireEvent('metachange', this, this.reader.meta);
21874     },
21875     
21876     moveIndex : function(data, type)
21877     {
21878         var index = this.indexOf(data);
21879         
21880         var newIndex = index + type;
21881         
21882         this.remove(data);
21883         
21884         this.insert(newIndex, data);
21885         
21886     }
21887 });/*
21888  * Based on:
21889  * Ext JS Library 1.1.1
21890  * Copyright(c) 2006-2007, Ext JS, LLC.
21891  *
21892  * Originally Released Under LGPL - original licence link has changed is not relivant.
21893  *
21894  * Fork - LGPL
21895  * <script type="text/javascript">
21896  */
21897
21898 /**
21899  * @class Roo.data.SimpleStore
21900  * @extends Roo.data.Store
21901  * Small helper class to make creating Stores from Array data easier.
21902  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21903  * @cfg {Array} fields An array of field definition objects, or field name strings.
21904  * @cfg {Array} data The multi-dimensional array of data
21905  * @constructor
21906  * @param {Object} config
21907  */
21908 Roo.data.SimpleStore = function(config){
21909     Roo.data.SimpleStore.superclass.constructor.call(this, {
21910         isLocal : true,
21911         reader: new Roo.data.ArrayReader({
21912                 id: config.id
21913             },
21914             Roo.data.Record.create(config.fields)
21915         ),
21916         proxy : new Roo.data.MemoryProxy(config.data)
21917     });
21918     this.load();
21919 };
21920 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21921  * Based on:
21922  * Ext JS Library 1.1.1
21923  * Copyright(c) 2006-2007, Ext JS, LLC.
21924  *
21925  * Originally Released Under LGPL - original licence link has changed is not relivant.
21926  *
21927  * Fork - LGPL
21928  * <script type="text/javascript">
21929  */
21930
21931 /**
21932 /**
21933  * @extends Roo.data.Store
21934  * @class Roo.data.JsonStore
21935  * Small helper class to make creating Stores for JSON data easier. <br/>
21936 <pre><code>
21937 var store = new Roo.data.JsonStore({
21938     url: 'get-images.php',
21939     root: 'images',
21940     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21941 });
21942 </code></pre>
21943  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21944  * JsonReader and HttpProxy (unless inline data is provided).</b>
21945  * @cfg {Array} fields An array of field definition objects, or field name strings.
21946  * @constructor
21947  * @param {Object} config
21948  */
21949 Roo.data.JsonStore = function(c){
21950     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21951         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21952         reader: new Roo.data.JsonReader(c, c.fields)
21953     }));
21954 };
21955 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21956  * Based on:
21957  * Ext JS Library 1.1.1
21958  * Copyright(c) 2006-2007, Ext JS, LLC.
21959  *
21960  * Originally Released Under LGPL - original licence link has changed is not relivant.
21961  *
21962  * Fork - LGPL
21963  * <script type="text/javascript">
21964  */
21965
21966  
21967 Roo.data.Field = function(config){
21968     if(typeof config == "string"){
21969         config = {name: config};
21970     }
21971     Roo.apply(this, config);
21972     
21973     if(!this.type){
21974         this.type = "auto";
21975     }
21976     
21977     var st = Roo.data.SortTypes;
21978     // named sortTypes are supported, here we look them up
21979     if(typeof this.sortType == "string"){
21980         this.sortType = st[this.sortType];
21981     }
21982     
21983     // set default sortType for strings and dates
21984     if(!this.sortType){
21985         switch(this.type){
21986             case "string":
21987                 this.sortType = st.asUCString;
21988                 break;
21989             case "date":
21990                 this.sortType = st.asDate;
21991                 break;
21992             default:
21993                 this.sortType = st.none;
21994         }
21995     }
21996
21997     // define once
21998     var stripRe = /[\$,%]/g;
21999
22000     // prebuilt conversion function for this field, instead of
22001     // switching every time we're reading a value
22002     if(!this.convert){
22003         var cv, dateFormat = this.dateFormat;
22004         switch(this.type){
22005             case "":
22006             case "auto":
22007             case undefined:
22008                 cv = function(v){ return v; };
22009                 break;
22010             case "string":
22011                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
22012                 break;
22013             case "int":
22014                 cv = function(v){
22015                     return v !== undefined && v !== null && v !== '' ?
22016                            parseInt(String(v).replace(stripRe, ""), 10) : '';
22017                     };
22018                 break;
22019             case "float":
22020                 cv = function(v){
22021                     return v !== undefined && v !== null && v !== '' ?
22022                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
22023                     };
22024                 break;
22025             case "bool":
22026             case "boolean":
22027                 cv = function(v){ return v === true || v === "true" || v == 1; };
22028                 break;
22029             case "date":
22030                 cv = function(v){
22031                     if(!v){
22032                         return '';
22033                     }
22034                     if(v instanceof Date){
22035                         return v;
22036                     }
22037                     if(dateFormat){
22038                         if(dateFormat == "timestamp"){
22039                             return new Date(v*1000);
22040                         }
22041                         return Date.parseDate(v, dateFormat);
22042                     }
22043                     var parsed = Date.parse(v);
22044                     return parsed ? new Date(parsed) : null;
22045                 };
22046              break;
22047             
22048         }
22049         this.convert = cv;
22050     }
22051 };
22052
22053 Roo.data.Field.prototype = {
22054     dateFormat: null,
22055     defaultValue: "",
22056     mapping: null,
22057     sortType : null,
22058     sortDir : "ASC"
22059 };/*
22060  * Based on:
22061  * Ext JS Library 1.1.1
22062  * Copyright(c) 2006-2007, Ext JS, LLC.
22063  *
22064  * Originally Released Under LGPL - original licence link has changed is not relivant.
22065  *
22066  * Fork - LGPL
22067  * <script type="text/javascript">
22068  */
22069  
22070 // Base class for reading structured data from a data source.  This class is intended to be
22071 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22072
22073 /**
22074  * @class Roo.data.DataReader
22075  * Base class for reading structured data from a data source.  This class is intended to be
22076  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22077  */
22078
22079 Roo.data.DataReader = function(meta, recordType){
22080     
22081     this.meta = meta;
22082     
22083     this.recordType = recordType instanceof Array ? 
22084         Roo.data.Record.create(recordType) : recordType;
22085 };
22086
22087 Roo.data.DataReader.prototype = {
22088      /**
22089      * Create an empty record
22090      * @param {Object} data (optional) - overlay some values
22091      * @return {Roo.data.Record} record created.
22092      */
22093     newRow :  function(d) {
22094         var da =  {};
22095         this.recordType.prototype.fields.each(function(c) {
22096             switch( c.type) {
22097                 case 'int' : da[c.name] = 0; break;
22098                 case 'date' : da[c.name] = new Date(); break;
22099                 case 'float' : da[c.name] = 0.0; break;
22100                 case 'boolean' : da[c.name] = false; break;
22101                 default : da[c.name] = ""; break;
22102             }
22103             
22104         });
22105         return new this.recordType(Roo.apply(da, d));
22106     }
22107     
22108 };/*
22109  * Based on:
22110  * Ext JS Library 1.1.1
22111  * Copyright(c) 2006-2007, Ext JS, LLC.
22112  *
22113  * Originally Released Under LGPL - original licence link has changed is not relivant.
22114  *
22115  * Fork - LGPL
22116  * <script type="text/javascript">
22117  */
22118
22119 /**
22120  * @class Roo.data.DataProxy
22121  * @extends Roo.data.Observable
22122  * This class is an abstract base class for implementations which provide retrieval of
22123  * unformatted data objects.<br>
22124  * <p>
22125  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22126  * (of the appropriate type which knows how to parse the data object) to provide a block of
22127  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22128  * <p>
22129  * Custom implementations must implement the load method as described in
22130  * {@link Roo.data.HttpProxy#load}.
22131  */
22132 Roo.data.DataProxy = function(){
22133     this.addEvents({
22134         /**
22135          * @event beforeload
22136          * Fires before a network request is made to retrieve a data object.
22137          * @param {Object} This DataProxy object.
22138          * @param {Object} params The params parameter to the load function.
22139          */
22140         beforeload : true,
22141         /**
22142          * @event load
22143          * Fires before the load method's callback is called.
22144          * @param {Object} This DataProxy object.
22145          * @param {Object} o The data object.
22146          * @param {Object} arg The callback argument object passed to the load function.
22147          */
22148         load : true,
22149         /**
22150          * @event loadexception
22151          * Fires if an Exception occurs during data retrieval.
22152          * @param {Object} This DataProxy object.
22153          * @param {Object} o The data object.
22154          * @param {Object} arg The callback argument object passed to the load function.
22155          * @param {Object} e The Exception.
22156          */
22157         loadexception : true
22158     });
22159     Roo.data.DataProxy.superclass.constructor.call(this);
22160 };
22161
22162 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22163
22164     /**
22165      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22166      */
22167 /*
22168  * Based on:
22169  * Ext JS Library 1.1.1
22170  * Copyright(c) 2006-2007, Ext JS, LLC.
22171  *
22172  * Originally Released Under LGPL - original licence link has changed is not relivant.
22173  *
22174  * Fork - LGPL
22175  * <script type="text/javascript">
22176  */
22177 /**
22178  * @class Roo.data.MemoryProxy
22179  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22180  * to the Reader when its load method is called.
22181  * @constructor
22182  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22183  */
22184 Roo.data.MemoryProxy = function(data){
22185     if (data.data) {
22186         data = data.data;
22187     }
22188     Roo.data.MemoryProxy.superclass.constructor.call(this);
22189     this.data = data;
22190 };
22191
22192 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22193     /**
22194      * Load data from the requested source (in this case an in-memory
22195      * data object passed to the constructor), read the data object into
22196      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22197      * process that block using the passed callback.
22198      * @param {Object} params This parameter is not used by the MemoryProxy class.
22199      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22200      * object into a block of Roo.data.Records.
22201      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22202      * The function must be passed <ul>
22203      * <li>The Record block object</li>
22204      * <li>The "arg" argument from the load function</li>
22205      * <li>A boolean success indicator</li>
22206      * </ul>
22207      * @param {Object} scope The scope in which to call the callback
22208      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22209      */
22210     load : function(params, reader, callback, scope, arg){
22211         params = params || {};
22212         var result;
22213         try {
22214             result = reader.readRecords(this.data);
22215         }catch(e){
22216             this.fireEvent("loadexception", this, arg, null, e);
22217             callback.call(scope, null, arg, false);
22218             return;
22219         }
22220         callback.call(scope, result, arg, true);
22221     },
22222     
22223     // private
22224     update : function(params, records){
22225         
22226     }
22227 });/*
22228  * Based on:
22229  * Ext JS Library 1.1.1
22230  * Copyright(c) 2006-2007, Ext JS, LLC.
22231  *
22232  * Originally Released Under LGPL - original licence link has changed is not relivant.
22233  *
22234  * Fork - LGPL
22235  * <script type="text/javascript">
22236  */
22237 /**
22238  * @class Roo.data.HttpProxy
22239  * @extends Roo.data.DataProxy
22240  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22241  * configured to reference a certain URL.<br><br>
22242  * <p>
22243  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22244  * from which the running page was served.<br><br>
22245  * <p>
22246  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22247  * <p>
22248  * Be aware that to enable the browser to parse an XML document, the server must set
22249  * the Content-Type header in the HTTP response to "text/xml".
22250  * @constructor
22251  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22252  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22253  * will be used to make the request.
22254  */
22255 Roo.data.HttpProxy = function(conn){
22256     Roo.data.HttpProxy.superclass.constructor.call(this);
22257     // is conn a conn config or a real conn?
22258     this.conn = conn;
22259     this.useAjax = !conn || !conn.events;
22260   
22261 };
22262
22263 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22264     // thse are take from connection...
22265     
22266     /**
22267      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22268      */
22269     /**
22270      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22271      * extra parameters to each request made by this object. (defaults to undefined)
22272      */
22273     /**
22274      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22275      *  to each request made by this object. (defaults to undefined)
22276      */
22277     /**
22278      * @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)
22279      */
22280     /**
22281      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22282      */
22283      /**
22284      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22285      * @type Boolean
22286      */
22287   
22288
22289     /**
22290      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22291      * @type Boolean
22292      */
22293     /**
22294      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22295      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22296      * a finer-grained basis than the DataProxy events.
22297      */
22298     getConnection : function(){
22299         return this.useAjax ? Roo.Ajax : this.conn;
22300     },
22301
22302     /**
22303      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22304      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22305      * process that block using the passed callback.
22306      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22307      * for the request to the remote server.
22308      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22309      * object into a block of Roo.data.Records.
22310      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22311      * The function must be passed <ul>
22312      * <li>The Record block object</li>
22313      * <li>The "arg" argument from the load function</li>
22314      * <li>A boolean success indicator</li>
22315      * </ul>
22316      * @param {Object} scope The scope in which to call the callback
22317      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22318      */
22319     load : function(params, reader, callback, scope, arg){
22320         if(this.fireEvent("beforeload", this, params) !== false){
22321             var  o = {
22322                 params : params || {},
22323                 request: {
22324                     callback : callback,
22325                     scope : scope,
22326                     arg : arg
22327                 },
22328                 reader: reader,
22329                 callback : this.loadResponse,
22330                 scope: this
22331             };
22332             if(this.useAjax){
22333                 Roo.applyIf(o, this.conn);
22334                 if(this.activeRequest){
22335                     Roo.Ajax.abort(this.activeRequest);
22336                 }
22337                 this.activeRequest = Roo.Ajax.request(o);
22338             }else{
22339                 this.conn.request(o);
22340             }
22341         }else{
22342             callback.call(scope||this, null, arg, false);
22343         }
22344     },
22345
22346     // private
22347     loadResponse : function(o, success, response){
22348         delete this.activeRequest;
22349         if(!success){
22350             this.fireEvent("loadexception", this, o, response);
22351             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22352             return;
22353         }
22354         var result;
22355         try {
22356             result = o.reader.read(response);
22357         }catch(e){
22358             this.fireEvent("loadexception", this, o, response, e);
22359             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22360             return;
22361         }
22362         
22363         this.fireEvent("load", this, o, o.request.arg);
22364         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22365     },
22366
22367     // private
22368     update : function(dataSet){
22369
22370     },
22371
22372     // private
22373     updateResponse : function(dataSet){
22374
22375     }
22376 });/*
22377  * Based on:
22378  * Ext JS Library 1.1.1
22379  * Copyright(c) 2006-2007, Ext JS, LLC.
22380  *
22381  * Originally Released Under LGPL - original licence link has changed is not relivant.
22382  *
22383  * Fork - LGPL
22384  * <script type="text/javascript">
22385  */
22386
22387 /**
22388  * @class Roo.data.ScriptTagProxy
22389  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22390  * other than the originating domain of the running page.<br><br>
22391  * <p>
22392  * <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
22393  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22394  * <p>
22395  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22396  * source code that is used as the source inside a &lt;script> tag.<br><br>
22397  * <p>
22398  * In order for the browser to process the returned data, the server must wrap the data object
22399  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22400  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22401  * depending on whether the callback name was passed:
22402  * <p>
22403  * <pre><code>
22404 boolean scriptTag = false;
22405 String cb = request.getParameter("callback");
22406 if (cb != null) {
22407     scriptTag = true;
22408     response.setContentType("text/javascript");
22409 } else {
22410     response.setContentType("application/x-json");
22411 }
22412 Writer out = response.getWriter();
22413 if (scriptTag) {
22414     out.write(cb + "(");
22415 }
22416 out.print(dataBlock.toJsonString());
22417 if (scriptTag) {
22418     out.write(");");
22419 }
22420 </pre></code>
22421  *
22422  * @constructor
22423  * @param {Object} config A configuration object.
22424  */
22425 Roo.data.ScriptTagProxy = function(config){
22426     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22427     Roo.apply(this, config);
22428     this.head = document.getElementsByTagName("head")[0];
22429 };
22430
22431 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22432
22433 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22434     /**
22435      * @cfg {String} url The URL from which to request the data object.
22436      */
22437     /**
22438      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22439      */
22440     timeout : 30000,
22441     /**
22442      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22443      * the server the name of the callback function set up by the load call to process the returned data object.
22444      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22445      * javascript output which calls this named function passing the data object as its only parameter.
22446      */
22447     callbackParam : "callback",
22448     /**
22449      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22450      * name to the request.
22451      */
22452     nocache : true,
22453
22454     /**
22455      * Load data from the configured URL, read the data object into
22456      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22457      * process that block using the passed callback.
22458      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22459      * for the request to the remote server.
22460      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22461      * object into a block of Roo.data.Records.
22462      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22463      * The function must be passed <ul>
22464      * <li>The Record block object</li>
22465      * <li>The "arg" argument from the load function</li>
22466      * <li>A boolean success indicator</li>
22467      * </ul>
22468      * @param {Object} scope The scope in which to call the callback
22469      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22470      */
22471     load : function(params, reader, callback, scope, arg){
22472         if(this.fireEvent("beforeload", this, params) !== false){
22473
22474             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22475
22476             var url = this.url;
22477             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22478             if(this.nocache){
22479                 url += "&_dc=" + (new Date().getTime());
22480             }
22481             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22482             var trans = {
22483                 id : transId,
22484                 cb : "stcCallback"+transId,
22485                 scriptId : "stcScript"+transId,
22486                 params : params,
22487                 arg : arg,
22488                 url : url,
22489                 callback : callback,
22490                 scope : scope,
22491                 reader : reader
22492             };
22493             var conn = this;
22494
22495             window[trans.cb] = function(o){
22496                 conn.handleResponse(o, trans);
22497             };
22498
22499             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22500
22501             if(this.autoAbort !== false){
22502                 this.abort();
22503             }
22504
22505             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22506
22507             var script = document.createElement("script");
22508             script.setAttribute("src", url);
22509             script.setAttribute("type", "text/javascript");
22510             script.setAttribute("id", trans.scriptId);
22511             this.head.appendChild(script);
22512
22513             this.trans = trans;
22514         }else{
22515             callback.call(scope||this, null, arg, false);
22516         }
22517     },
22518
22519     // private
22520     isLoading : function(){
22521         return this.trans ? true : false;
22522     },
22523
22524     /**
22525      * Abort the current server request.
22526      */
22527     abort : function(){
22528         if(this.isLoading()){
22529             this.destroyTrans(this.trans);
22530         }
22531     },
22532
22533     // private
22534     destroyTrans : function(trans, isLoaded){
22535         this.head.removeChild(document.getElementById(trans.scriptId));
22536         clearTimeout(trans.timeoutId);
22537         if(isLoaded){
22538             window[trans.cb] = undefined;
22539             try{
22540                 delete window[trans.cb];
22541             }catch(e){}
22542         }else{
22543             // if hasn't been loaded, wait for load to remove it to prevent script error
22544             window[trans.cb] = function(){
22545                 window[trans.cb] = undefined;
22546                 try{
22547                     delete window[trans.cb];
22548                 }catch(e){}
22549             };
22550         }
22551     },
22552
22553     // private
22554     handleResponse : function(o, trans){
22555         this.trans = false;
22556         this.destroyTrans(trans, true);
22557         var result;
22558         try {
22559             result = trans.reader.readRecords(o);
22560         }catch(e){
22561             this.fireEvent("loadexception", this, o, trans.arg, e);
22562             trans.callback.call(trans.scope||window, null, trans.arg, false);
22563             return;
22564         }
22565         this.fireEvent("load", this, o, trans.arg);
22566         trans.callback.call(trans.scope||window, result, trans.arg, true);
22567     },
22568
22569     // private
22570     handleFailure : function(trans){
22571         this.trans = false;
22572         this.destroyTrans(trans, false);
22573         this.fireEvent("loadexception", this, null, trans.arg);
22574         trans.callback.call(trans.scope||window, null, trans.arg, false);
22575     }
22576 });/*
22577  * Based on:
22578  * Ext JS Library 1.1.1
22579  * Copyright(c) 2006-2007, Ext JS, LLC.
22580  *
22581  * Originally Released Under LGPL - original licence link has changed is not relivant.
22582  *
22583  * Fork - LGPL
22584  * <script type="text/javascript">
22585  */
22586
22587 /**
22588  * @class Roo.data.JsonReader
22589  * @extends Roo.data.DataReader
22590  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22591  * based on mappings in a provided Roo.data.Record constructor.
22592  * 
22593  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22594  * in the reply previously. 
22595  * 
22596  * <p>
22597  * Example code:
22598  * <pre><code>
22599 var RecordDef = Roo.data.Record.create([
22600     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22601     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22602 ]);
22603 var myReader = new Roo.data.JsonReader({
22604     totalProperty: "results",    // The property which contains the total dataset size (optional)
22605     root: "rows",                // The property which contains an Array of row objects
22606     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22607 }, RecordDef);
22608 </code></pre>
22609  * <p>
22610  * This would consume a JSON file like this:
22611  * <pre><code>
22612 { 'results': 2, 'rows': [
22613     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22614     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22615 }
22616 </code></pre>
22617  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22618  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22619  * paged from the remote server.
22620  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22621  * @cfg {String} root name of the property which contains the Array of row objects.
22622  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22623  * @cfg {Array} fields Array of field definition objects
22624  * @constructor
22625  * Create a new JsonReader
22626  * @param {Object} meta Metadata configuration options
22627  * @param {Object} recordType Either an Array of field definition objects,
22628  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22629  */
22630 Roo.data.JsonReader = function(meta, recordType){
22631     
22632     meta = meta || {};
22633     // set some defaults:
22634     Roo.applyIf(meta, {
22635         totalProperty: 'total',
22636         successProperty : 'success',
22637         root : 'data',
22638         id : 'id'
22639     });
22640     
22641     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22642 };
22643 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22644     
22645     /**
22646      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22647      * Used by Store query builder to append _requestMeta to params.
22648      * 
22649      */
22650     metaFromRemote : false,
22651     /**
22652      * This method is only used by a DataProxy which has retrieved data from a remote server.
22653      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22654      * @return {Object} data A data block which is used by an Roo.data.Store object as
22655      * a cache of Roo.data.Records.
22656      */
22657     read : function(response){
22658         var json = response.responseText;
22659        
22660         var o = /* eval:var:o */ eval("("+json+")");
22661         if(!o) {
22662             throw {message: "JsonReader.read: Json object not found"};
22663         }
22664         
22665         if(o.metaData){
22666             
22667             delete this.ef;
22668             this.metaFromRemote = true;
22669             this.meta = o.metaData;
22670             this.recordType = Roo.data.Record.create(o.metaData.fields);
22671             this.onMetaChange(this.meta, this.recordType, o);
22672         }
22673         return this.readRecords(o);
22674     },
22675
22676     // private function a store will implement
22677     onMetaChange : function(meta, recordType, o){
22678
22679     },
22680
22681     /**
22682          * @ignore
22683          */
22684     simpleAccess: function(obj, subsc) {
22685         return obj[subsc];
22686     },
22687
22688         /**
22689          * @ignore
22690          */
22691     getJsonAccessor: function(){
22692         var re = /[\[\.]/;
22693         return function(expr) {
22694             try {
22695                 return(re.test(expr))
22696                     ? new Function("obj", "return obj." + expr)
22697                     : function(obj){
22698                         return obj[expr];
22699                     };
22700             } catch(e){}
22701             return Roo.emptyFn;
22702         };
22703     }(),
22704
22705     /**
22706      * Create a data block containing Roo.data.Records from an XML document.
22707      * @param {Object} o An object which contains an Array of row objects in the property specified
22708      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22709      * which contains the total size of the dataset.
22710      * @return {Object} data A data block which is used by an Roo.data.Store object as
22711      * a cache of Roo.data.Records.
22712      */
22713     readRecords : function(o){
22714         /**
22715          * After any data loads, the raw JSON data is available for further custom processing.
22716          * @type Object
22717          */
22718         this.o = o;
22719         var s = this.meta, Record = this.recordType,
22720             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
22721
22722 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22723         if (!this.ef) {
22724             if(s.totalProperty) {
22725                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22726                 }
22727                 if(s.successProperty) {
22728                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22729                 }
22730                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22731                 if (s.id) {
22732                         var g = this.getJsonAccessor(s.id);
22733                         this.getId = function(rec) {
22734                                 var r = g(rec);  
22735                                 return (r === undefined || r === "") ? null : r;
22736                         };
22737                 } else {
22738                         this.getId = function(){return null;};
22739                 }
22740             this.ef = [];
22741             for(var jj = 0; jj < fl; jj++){
22742                 f = fi[jj];
22743                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22744                 this.ef[jj] = this.getJsonAccessor(map);
22745             }
22746         }
22747
22748         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22749         if(s.totalProperty){
22750             var vt = parseInt(this.getTotal(o), 10);
22751             if(!isNaN(vt)){
22752                 totalRecords = vt;
22753             }
22754         }
22755         if(s.successProperty){
22756             var vs = this.getSuccess(o);
22757             if(vs === false || vs === 'false'){
22758                 success = false;
22759             }
22760         }
22761         var records = [];
22762         for(var i = 0; i < c; i++){
22763                 var n = root[i];
22764             var values = {};
22765             var id = this.getId(n);
22766             for(var j = 0; j < fl; j++){
22767                 f = fi[j];
22768             var v = this.ef[j](n);
22769             if (!f.convert) {
22770                 Roo.log('missing convert for ' + f.name);
22771                 Roo.log(f);
22772                 continue;
22773             }
22774             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22775             }
22776             var record = new Record(values, id);
22777             record.json = n;
22778             records[i] = record;
22779         }
22780         return {
22781             raw : o,
22782             success : success,
22783             records : records,
22784             totalRecords : totalRecords
22785         };
22786     }
22787 });/*
22788  * Based on:
22789  * Ext JS Library 1.1.1
22790  * Copyright(c) 2006-2007, Ext JS, LLC.
22791  *
22792  * Originally Released Under LGPL - original licence link has changed is not relivant.
22793  *
22794  * Fork - LGPL
22795  * <script type="text/javascript">
22796  */
22797
22798 /**
22799  * @class Roo.data.XmlReader
22800  * @extends Roo.data.DataReader
22801  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22802  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22803  * <p>
22804  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22805  * header in the HTTP response must be set to "text/xml".</em>
22806  * <p>
22807  * Example code:
22808  * <pre><code>
22809 var RecordDef = Roo.data.Record.create([
22810    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22811    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22812 ]);
22813 var myReader = new Roo.data.XmlReader({
22814    totalRecords: "results", // The element which contains the total dataset size (optional)
22815    record: "row",           // The repeated element which contains row information
22816    id: "id"                 // The element within the row that provides an ID for the record (optional)
22817 }, RecordDef);
22818 </code></pre>
22819  * <p>
22820  * This would consume an XML file like this:
22821  * <pre><code>
22822 &lt;?xml?>
22823 &lt;dataset>
22824  &lt;results>2&lt;/results>
22825  &lt;row>
22826    &lt;id>1&lt;/id>
22827    &lt;name>Bill&lt;/name>
22828    &lt;occupation>Gardener&lt;/occupation>
22829  &lt;/row>
22830  &lt;row>
22831    &lt;id>2&lt;/id>
22832    &lt;name>Ben&lt;/name>
22833    &lt;occupation>Horticulturalist&lt;/occupation>
22834  &lt;/row>
22835 &lt;/dataset>
22836 </code></pre>
22837  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22838  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22839  * paged from the remote server.
22840  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22841  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22842  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22843  * a record identifier value.
22844  * @constructor
22845  * Create a new XmlReader
22846  * @param {Object} meta Metadata configuration options
22847  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22848  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22849  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22850  */
22851 Roo.data.XmlReader = function(meta, recordType){
22852     meta = meta || {};
22853     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22854 };
22855 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22856     /**
22857      * This method is only used by a DataProxy which has retrieved data from a remote server.
22858          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22859          * to contain a method called 'responseXML' that returns an XML document object.
22860      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22861      * a cache of Roo.data.Records.
22862      */
22863     read : function(response){
22864         var doc = response.responseXML;
22865         if(!doc) {
22866             throw {message: "XmlReader.read: XML Document not available"};
22867         }
22868         return this.readRecords(doc);
22869     },
22870
22871     /**
22872      * Create a data block containing Roo.data.Records from an XML document.
22873          * @param {Object} doc A parsed XML document.
22874      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22875      * a cache of Roo.data.Records.
22876      */
22877     readRecords : function(doc){
22878         /**
22879          * After any data loads/reads, the raw XML Document is available for further custom processing.
22880          * @type XMLDocument
22881          */
22882         this.xmlData = doc;
22883         var root = doc.documentElement || doc;
22884         var q = Roo.DomQuery;
22885         var recordType = this.recordType, fields = recordType.prototype.fields;
22886         var sid = this.meta.id;
22887         var totalRecords = 0, success = true;
22888         if(this.meta.totalRecords){
22889             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22890         }
22891         
22892         if(this.meta.success){
22893             var sv = q.selectValue(this.meta.success, root, true);
22894             success = sv !== false && sv !== 'false';
22895         }
22896         var records = [];
22897         var ns = q.select(this.meta.record, root);
22898         for(var i = 0, len = ns.length; i < len; i++) {
22899                 var n = ns[i];
22900                 var values = {};
22901                 var id = sid ? q.selectValue(sid, n) : undefined;
22902                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22903                     var f = fields.items[j];
22904                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22905                     v = f.convert(v);
22906                     values[f.name] = v;
22907                 }
22908                 var record = new recordType(values, id);
22909                 record.node = n;
22910                 records[records.length] = record;
22911             }
22912
22913             return {
22914                 success : success,
22915                 records : records,
22916                 totalRecords : totalRecords || records.length
22917             };
22918     }
22919 });/*
22920  * Based on:
22921  * Ext JS Library 1.1.1
22922  * Copyright(c) 2006-2007, Ext JS, LLC.
22923  *
22924  * Originally Released Under LGPL - original licence link has changed is not relivant.
22925  *
22926  * Fork - LGPL
22927  * <script type="text/javascript">
22928  */
22929
22930 /**
22931  * @class Roo.data.ArrayReader
22932  * @extends Roo.data.DataReader
22933  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22934  * Each element of that Array represents a row of data fields. The
22935  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22936  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22937  * <p>
22938  * Example code:.
22939  * <pre><code>
22940 var RecordDef = Roo.data.Record.create([
22941     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22942     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22943 ]);
22944 var myReader = new Roo.data.ArrayReader({
22945     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22946 }, RecordDef);
22947 </code></pre>
22948  * <p>
22949  * This would consume an Array like this:
22950  * <pre><code>
22951 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22952   </code></pre>
22953  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22954  * @constructor
22955  * Create a new JsonReader
22956  * @param {Object} meta Metadata configuration options.
22957  * @param {Object} recordType Either an Array of field definition objects
22958  * as specified to {@link Roo.data.Record#create},
22959  * or an {@link Roo.data.Record} object
22960  * created using {@link Roo.data.Record#create}.
22961  */
22962 Roo.data.ArrayReader = function(meta, recordType){
22963     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22964 };
22965
22966 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22967     /**
22968      * Create a data block containing Roo.data.Records from an XML document.
22969      * @param {Object} o An Array of row objects which represents the dataset.
22970      * @return {Object} data A data block which is used by an Roo.data.Store object as
22971      * a cache of Roo.data.Records.
22972      */
22973     readRecords : function(o){
22974         var sid = this.meta ? this.meta.id : null;
22975         var recordType = this.recordType, fields = recordType.prototype.fields;
22976         var records = [];
22977         var root = o;
22978             for(var i = 0; i < root.length; i++){
22979                     var n = root[i];
22980                 var values = {};
22981                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22982                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22983                 var f = fields.items[j];
22984                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22985                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22986                 v = f.convert(v);
22987                 values[f.name] = v;
22988             }
22989                 var record = new recordType(values, id);
22990                 record.json = n;
22991                 records[records.length] = record;
22992             }
22993             return {
22994                 records : records,
22995                 totalRecords : records.length
22996             };
22997     }
22998 });/*
22999  * Based on:
23000  * Ext JS Library 1.1.1
23001  * Copyright(c) 2006-2007, Ext JS, LLC.
23002  *
23003  * Originally Released Under LGPL - original licence link has changed is not relivant.
23004  *
23005  * Fork - LGPL
23006  * <script type="text/javascript">
23007  */
23008
23009
23010 /**
23011  * @class Roo.data.Tree
23012  * @extends Roo.util.Observable
23013  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
23014  * in the tree have most standard DOM functionality.
23015  * @constructor
23016  * @param {Node} root (optional) The root node
23017  */
23018 Roo.data.Tree = function(root){
23019    this.nodeHash = {};
23020    /**
23021     * The root node for this tree
23022     * @type Node
23023     */
23024    this.root = null;
23025    if(root){
23026        this.setRootNode(root);
23027    }
23028    this.addEvents({
23029        /**
23030         * @event append
23031         * Fires when a new child node is appended to a node in this tree.
23032         * @param {Tree} tree The owner tree
23033         * @param {Node} parent The parent node
23034         * @param {Node} node The newly appended node
23035         * @param {Number} index The index of the newly appended node
23036         */
23037        "append" : true,
23038        /**
23039         * @event remove
23040         * Fires when a child node is removed from a node in this tree.
23041         * @param {Tree} tree The owner tree
23042         * @param {Node} parent The parent node
23043         * @param {Node} node The child node removed
23044         */
23045        "remove" : true,
23046        /**
23047         * @event move
23048         * Fires when a node is moved to a new location in the tree
23049         * @param {Tree} tree The owner tree
23050         * @param {Node} node The node moved
23051         * @param {Node} oldParent The old parent of this node
23052         * @param {Node} newParent The new parent of this node
23053         * @param {Number} index The index it was moved to
23054         */
23055        "move" : true,
23056        /**
23057         * @event insert
23058         * Fires when a new child node is inserted in a node in this tree.
23059         * @param {Tree} tree The owner tree
23060         * @param {Node} parent The parent node
23061         * @param {Node} node The child node inserted
23062         * @param {Node} refNode The child node the node was inserted before
23063         */
23064        "insert" : true,
23065        /**
23066         * @event beforeappend
23067         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23068         * @param {Tree} tree The owner tree
23069         * @param {Node} parent The parent node
23070         * @param {Node} node The child node to be appended
23071         */
23072        "beforeappend" : true,
23073        /**
23074         * @event beforeremove
23075         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23076         * @param {Tree} tree The owner tree
23077         * @param {Node} parent The parent node
23078         * @param {Node} node The child node to be removed
23079         */
23080        "beforeremove" : true,
23081        /**
23082         * @event beforemove
23083         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23084         * @param {Tree} tree The owner tree
23085         * @param {Node} node The node being moved
23086         * @param {Node} oldParent The parent of the node
23087         * @param {Node} newParent The new parent the node is moving to
23088         * @param {Number} index The index it is being moved to
23089         */
23090        "beforemove" : true,
23091        /**
23092         * @event beforeinsert
23093         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23094         * @param {Tree} tree The owner tree
23095         * @param {Node} parent The parent node
23096         * @param {Node} node The child node to be inserted
23097         * @param {Node} refNode The child node the node is being inserted before
23098         */
23099        "beforeinsert" : true
23100    });
23101
23102     Roo.data.Tree.superclass.constructor.call(this);
23103 };
23104
23105 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23106     pathSeparator: "/",
23107
23108     proxyNodeEvent : function(){
23109         return this.fireEvent.apply(this, arguments);
23110     },
23111
23112     /**
23113      * Returns the root node for this tree.
23114      * @return {Node}
23115      */
23116     getRootNode : function(){
23117         return this.root;
23118     },
23119
23120     /**
23121      * Sets the root node for this tree.
23122      * @param {Node} node
23123      * @return {Node}
23124      */
23125     setRootNode : function(node){
23126         this.root = node;
23127         node.ownerTree = this;
23128         node.isRoot = true;
23129         this.registerNode(node);
23130         return node;
23131     },
23132
23133     /**
23134      * Gets a node in this tree by its id.
23135      * @param {String} id
23136      * @return {Node}
23137      */
23138     getNodeById : function(id){
23139         return this.nodeHash[id];
23140     },
23141
23142     registerNode : function(node){
23143         this.nodeHash[node.id] = node;
23144     },
23145
23146     unregisterNode : function(node){
23147         delete this.nodeHash[node.id];
23148     },
23149
23150     toString : function(){
23151         return "[Tree"+(this.id?" "+this.id:"")+"]";
23152     }
23153 });
23154
23155 /**
23156  * @class Roo.data.Node
23157  * @extends Roo.util.Observable
23158  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23159  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23160  * @constructor
23161  * @param {Object} attributes The attributes/config for the node
23162  */
23163 Roo.data.Node = function(attributes){
23164     /**
23165      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23166      * @type {Object}
23167      */
23168     this.attributes = attributes || {};
23169     this.leaf = this.attributes.leaf;
23170     /**
23171      * The node id. @type String
23172      */
23173     this.id = this.attributes.id;
23174     if(!this.id){
23175         this.id = Roo.id(null, "ynode-");
23176         this.attributes.id = this.id;
23177     }
23178      
23179     
23180     /**
23181      * All child nodes of this node. @type Array
23182      */
23183     this.childNodes = [];
23184     if(!this.childNodes.indexOf){ // indexOf is a must
23185         this.childNodes.indexOf = function(o){
23186             for(var i = 0, len = this.length; i < len; i++){
23187                 if(this[i] == o) {
23188                     return i;
23189                 }
23190             }
23191             return -1;
23192         };
23193     }
23194     /**
23195      * The parent node for this node. @type Node
23196      */
23197     this.parentNode = null;
23198     /**
23199      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23200      */
23201     this.firstChild = null;
23202     /**
23203      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23204      */
23205     this.lastChild = null;
23206     /**
23207      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23208      */
23209     this.previousSibling = null;
23210     /**
23211      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23212      */
23213     this.nextSibling = null;
23214
23215     this.addEvents({
23216        /**
23217         * @event append
23218         * Fires when a new child node is appended
23219         * @param {Tree} tree The owner tree
23220         * @param {Node} this This node
23221         * @param {Node} node The newly appended node
23222         * @param {Number} index The index of the newly appended node
23223         */
23224        "append" : true,
23225        /**
23226         * @event remove
23227         * Fires when a child node is removed
23228         * @param {Tree} tree The owner tree
23229         * @param {Node} this This node
23230         * @param {Node} node The removed node
23231         */
23232        "remove" : true,
23233        /**
23234         * @event move
23235         * Fires when this node is moved to a new location in the tree
23236         * @param {Tree} tree The owner tree
23237         * @param {Node} this This node
23238         * @param {Node} oldParent The old parent of this node
23239         * @param {Node} newParent The new parent of this node
23240         * @param {Number} index The index it was moved to
23241         */
23242        "move" : true,
23243        /**
23244         * @event insert
23245         * Fires when a new child node is inserted.
23246         * @param {Tree} tree The owner tree
23247         * @param {Node} this This node
23248         * @param {Node} node The child node inserted
23249         * @param {Node} refNode The child node the node was inserted before
23250         */
23251        "insert" : true,
23252        /**
23253         * @event beforeappend
23254         * Fires before a new child is appended, return false to cancel the append.
23255         * @param {Tree} tree The owner tree
23256         * @param {Node} this This node
23257         * @param {Node} node The child node to be appended
23258         */
23259        "beforeappend" : true,
23260        /**
23261         * @event beforeremove
23262         * Fires before a child is removed, return false to cancel the remove.
23263         * @param {Tree} tree The owner tree
23264         * @param {Node} this This node
23265         * @param {Node} node The child node to be removed
23266         */
23267        "beforeremove" : true,
23268        /**
23269         * @event beforemove
23270         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23271         * @param {Tree} tree The owner tree
23272         * @param {Node} this This node
23273         * @param {Node} oldParent The parent of this node
23274         * @param {Node} newParent The new parent this node is moving to
23275         * @param {Number} index The index it is being moved to
23276         */
23277        "beforemove" : true,
23278        /**
23279         * @event beforeinsert
23280         * Fires before a new child is inserted, return false to cancel the insert.
23281         * @param {Tree} tree The owner tree
23282         * @param {Node} this This node
23283         * @param {Node} node The child node to be inserted
23284         * @param {Node} refNode The child node the node is being inserted before
23285         */
23286        "beforeinsert" : true
23287    });
23288     this.listeners = this.attributes.listeners;
23289     Roo.data.Node.superclass.constructor.call(this);
23290 };
23291
23292 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23293     fireEvent : function(evtName){
23294         // first do standard event for this node
23295         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23296             return false;
23297         }
23298         // then bubble it up to the tree if the event wasn't cancelled
23299         var ot = this.getOwnerTree();
23300         if(ot){
23301             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23302                 return false;
23303             }
23304         }
23305         return true;
23306     },
23307
23308     /**
23309      * Returns true if this node is a leaf
23310      * @return {Boolean}
23311      */
23312     isLeaf : function(){
23313         return this.leaf === true;
23314     },
23315
23316     // private
23317     setFirstChild : function(node){
23318         this.firstChild = node;
23319     },
23320
23321     //private
23322     setLastChild : function(node){
23323         this.lastChild = node;
23324     },
23325
23326
23327     /**
23328      * Returns true if this node is the last child of its parent
23329      * @return {Boolean}
23330      */
23331     isLast : function(){
23332        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23333     },
23334
23335     /**
23336      * Returns true if this node is the first child of its parent
23337      * @return {Boolean}
23338      */
23339     isFirst : function(){
23340        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23341     },
23342
23343     hasChildNodes : function(){
23344         return !this.isLeaf() && this.childNodes.length > 0;
23345     },
23346
23347     /**
23348      * Insert node(s) as the last child node of this node.
23349      * @param {Node/Array} node The node or Array of nodes to append
23350      * @return {Node} The appended node if single append, or null if an array was passed
23351      */
23352     appendChild : function(node){
23353         var multi = false;
23354         if(node instanceof Array){
23355             multi = node;
23356         }else if(arguments.length > 1){
23357             multi = arguments;
23358         }
23359         // if passed an array or multiple args do them one by one
23360         if(multi){
23361             for(var i = 0, len = multi.length; i < len; i++) {
23362                 this.appendChild(multi[i]);
23363             }
23364         }else{
23365             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23366                 return false;
23367             }
23368             var index = this.childNodes.length;
23369             var oldParent = node.parentNode;
23370             // it's a move, make sure we move it cleanly
23371             if(oldParent){
23372                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23373                     return false;
23374                 }
23375                 oldParent.removeChild(node);
23376             }
23377             index = this.childNodes.length;
23378             if(index == 0){
23379                 this.setFirstChild(node);
23380             }
23381             this.childNodes.push(node);
23382             node.parentNode = this;
23383             var ps = this.childNodes[index-1];
23384             if(ps){
23385                 node.previousSibling = ps;
23386                 ps.nextSibling = node;
23387             }else{
23388                 node.previousSibling = null;
23389             }
23390             node.nextSibling = null;
23391             this.setLastChild(node);
23392             node.setOwnerTree(this.getOwnerTree());
23393             this.fireEvent("append", this.ownerTree, this, node, index);
23394             if(oldParent){
23395                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23396             }
23397             return node;
23398         }
23399     },
23400
23401     /**
23402      * Removes a child node from this node.
23403      * @param {Node} node The node to remove
23404      * @return {Node} The removed node
23405      */
23406     removeChild : function(node){
23407         var index = this.childNodes.indexOf(node);
23408         if(index == -1){
23409             return false;
23410         }
23411         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23412             return false;
23413         }
23414
23415         // remove it from childNodes collection
23416         this.childNodes.splice(index, 1);
23417
23418         // update siblings
23419         if(node.previousSibling){
23420             node.previousSibling.nextSibling = node.nextSibling;
23421         }
23422         if(node.nextSibling){
23423             node.nextSibling.previousSibling = node.previousSibling;
23424         }
23425
23426         // update child refs
23427         if(this.firstChild == node){
23428             this.setFirstChild(node.nextSibling);
23429         }
23430         if(this.lastChild == node){
23431             this.setLastChild(node.previousSibling);
23432         }
23433
23434         node.setOwnerTree(null);
23435         // clear any references from the node
23436         node.parentNode = null;
23437         node.previousSibling = null;
23438         node.nextSibling = null;
23439         this.fireEvent("remove", this.ownerTree, this, node);
23440         return node;
23441     },
23442
23443     /**
23444      * Inserts the first node before the second node in this nodes childNodes collection.
23445      * @param {Node} node The node to insert
23446      * @param {Node} refNode The node to insert before (if null the node is appended)
23447      * @return {Node} The inserted node
23448      */
23449     insertBefore : function(node, refNode){
23450         if(!refNode){ // like standard Dom, refNode can be null for append
23451             return this.appendChild(node);
23452         }
23453         // nothing to do
23454         if(node == refNode){
23455             return false;
23456         }
23457
23458         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23459             return false;
23460         }
23461         var index = this.childNodes.indexOf(refNode);
23462         var oldParent = node.parentNode;
23463         var refIndex = index;
23464
23465         // when moving internally, indexes will change after remove
23466         if(oldParent == this && this.childNodes.indexOf(node) < index){
23467             refIndex--;
23468         }
23469
23470         // it's a move, make sure we move it cleanly
23471         if(oldParent){
23472             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23473                 return false;
23474             }
23475             oldParent.removeChild(node);
23476         }
23477         if(refIndex == 0){
23478             this.setFirstChild(node);
23479         }
23480         this.childNodes.splice(refIndex, 0, node);
23481         node.parentNode = this;
23482         var ps = this.childNodes[refIndex-1];
23483         if(ps){
23484             node.previousSibling = ps;
23485             ps.nextSibling = node;
23486         }else{
23487             node.previousSibling = null;
23488         }
23489         node.nextSibling = refNode;
23490         refNode.previousSibling = node;
23491         node.setOwnerTree(this.getOwnerTree());
23492         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23493         if(oldParent){
23494             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23495         }
23496         return node;
23497     },
23498
23499     /**
23500      * Returns the child node at the specified index.
23501      * @param {Number} index
23502      * @return {Node}
23503      */
23504     item : function(index){
23505         return this.childNodes[index];
23506     },
23507
23508     /**
23509      * Replaces one child node in this node with another.
23510      * @param {Node} newChild The replacement node
23511      * @param {Node} oldChild The node to replace
23512      * @return {Node} The replaced node
23513      */
23514     replaceChild : function(newChild, oldChild){
23515         this.insertBefore(newChild, oldChild);
23516         this.removeChild(oldChild);
23517         return oldChild;
23518     },
23519
23520     /**
23521      * Returns the index of a child node
23522      * @param {Node} node
23523      * @return {Number} The index of the node or -1 if it was not found
23524      */
23525     indexOf : function(child){
23526         return this.childNodes.indexOf(child);
23527     },
23528
23529     /**
23530      * Returns the tree this node is in.
23531      * @return {Tree}
23532      */
23533     getOwnerTree : function(){
23534         // if it doesn't have one, look for one
23535         if(!this.ownerTree){
23536             var p = this;
23537             while(p){
23538                 if(p.ownerTree){
23539                     this.ownerTree = p.ownerTree;
23540                     break;
23541                 }
23542                 p = p.parentNode;
23543             }
23544         }
23545         return this.ownerTree;
23546     },
23547
23548     /**
23549      * Returns depth of this node (the root node has a depth of 0)
23550      * @return {Number}
23551      */
23552     getDepth : function(){
23553         var depth = 0;
23554         var p = this;
23555         while(p.parentNode){
23556             ++depth;
23557             p = p.parentNode;
23558         }
23559         return depth;
23560     },
23561
23562     // private
23563     setOwnerTree : function(tree){
23564         // if it's move, we need to update everyone
23565         if(tree != this.ownerTree){
23566             if(this.ownerTree){
23567                 this.ownerTree.unregisterNode(this);
23568             }
23569             this.ownerTree = tree;
23570             var cs = this.childNodes;
23571             for(var i = 0, len = cs.length; i < len; i++) {
23572                 cs[i].setOwnerTree(tree);
23573             }
23574             if(tree){
23575                 tree.registerNode(this);
23576             }
23577         }
23578     },
23579
23580     /**
23581      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23582      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23583      * @return {String} The path
23584      */
23585     getPath : function(attr){
23586         attr = attr || "id";
23587         var p = this.parentNode;
23588         var b = [this.attributes[attr]];
23589         while(p){
23590             b.unshift(p.attributes[attr]);
23591             p = p.parentNode;
23592         }
23593         var sep = this.getOwnerTree().pathSeparator;
23594         return sep + b.join(sep);
23595     },
23596
23597     /**
23598      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23599      * function call will be the scope provided or the current node. The arguments to the function
23600      * will be the args provided or the current node. If the function returns false at any point,
23601      * the bubble is stopped.
23602      * @param {Function} fn The function to call
23603      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23604      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23605      */
23606     bubble : function(fn, scope, args){
23607         var p = this;
23608         while(p){
23609             if(fn.call(scope || p, args || p) === false){
23610                 break;
23611             }
23612             p = p.parentNode;
23613         }
23614     },
23615
23616     /**
23617      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23618      * function call will be the scope provided or the current node. The arguments to the function
23619      * will be the args provided or the current node. If the function returns false at any point,
23620      * the cascade is stopped on that branch.
23621      * @param {Function} fn The function to call
23622      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23623      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23624      */
23625     cascade : function(fn, scope, args){
23626         if(fn.call(scope || this, args || this) !== false){
23627             var cs = this.childNodes;
23628             for(var i = 0, len = cs.length; i < len; i++) {
23629                 cs[i].cascade(fn, scope, args);
23630             }
23631         }
23632     },
23633
23634     /**
23635      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23636      * function call will be the scope provided or the current node. The arguments to the function
23637      * will be the args provided or the current node. If the function returns false at any point,
23638      * the iteration stops.
23639      * @param {Function} fn The function to call
23640      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23641      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23642      */
23643     eachChild : function(fn, scope, args){
23644         var cs = this.childNodes;
23645         for(var i = 0, len = cs.length; i < len; i++) {
23646                 if(fn.call(scope || this, args || cs[i]) === false){
23647                     break;
23648                 }
23649         }
23650     },
23651
23652     /**
23653      * Finds the first child that has the attribute with the specified value.
23654      * @param {String} attribute The attribute name
23655      * @param {Mixed} value The value to search for
23656      * @return {Node} The found child or null if none was found
23657      */
23658     findChild : function(attribute, value){
23659         var cs = this.childNodes;
23660         for(var i = 0, len = cs.length; i < len; i++) {
23661                 if(cs[i].attributes[attribute] == value){
23662                     return cs[i];
23663                 }
23664         }
23665         return null;
23666     },
23667
23668     /**
23669      * Finds the first child by a custom function. The child matches if the function passed
23670      * returns true.
23671      * @param {Function} fn
23672      * @param {Object} scope (optional)
23673      * @return {Node} The found child or null if none was found
23674      */
23675     findChildBy : function(fn, scope){
23676         var cs = this.childNodes;
23677         for(var i = 0, len = cs.length; i < len; i++) {
23678                 if(fn.call(scope||cs[i], cs[i]) === true){
23679                     return cs[i];
23680                 }
23681         }
23682         return null;
23683     },
23684
23685     /**
23686      * Sorts this nodes children using the supplied sort function
23687      * @param {Function} fn
23688      * @param {Object} scope (optional)
23689      */
23690     sort : function(fn, scope){
23691         var cs = this.childNodes;
23692         var len = cs.length;
23693         if(len > 0){
23694             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23695             cs.sort(sortFn);
23696             for(var i = 0; i < len; i++){
23697                 var n = cs[i];
23698                 n.previousSibling = cs[i-1];
23699                 n.nextSibling = cs[i+1];
23700                 if(i == 0){
23701                     this.setFirstChild(n);
23702                 }
23703                 if(i == len-1){
23704                     this.setLastChild(n);
23705                 }
23706             }
23707         }
23708     },
23709
23710     /**
23711      * Returns true if this node is an ancestor (at any point) of the passed node.
23712      * @param {Node} node
23713      * @return {Boolean}
23714      */
23715     contains : function(node){
23716         return node.isAncestor(this);
23717     },
23718
23719     /**
23720      * Returns true if the passed node is an ancestor (at any point) of this node.
23721      * @param {Node} node
23722      * @return {Boolean}
23723      */
23724     isAncestor : function(node){
23725         var p = this.parentNode;
23726         while(p){
23727             if(p == node){
23728                 return true;
23729             }
23730             p = p.parentNode;
23731         }
23732         return false;
23733     },
23734
23735     toString : function(){
23736         return "[Node"+(this.id?" "+this.id:"")+"]";
23737     }
23738 });/*
23739  * Based on:
23740  * Ext JS Library 1.1.1
23741  * Copyright(c) 2006-2007, Ext JS, LLC.
23742  *
23743  * Originally Released Under LGPL - original licence link has changed is not relivant.
23744  *
23745  * Fork - LGPL
23746  * <script type="text/javascript">
23747  */
23748  (function(){ 
23749 /**
23750  * @class Roo.Layer
23751  * @extends Roo.Element
23752  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23753  * automatic maintaining of shadow/shim positions.
23754  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23755  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23756  * you can pass a string with a CSS class name. False turns off the shadow.
23757  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23758  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23759  * @cfg {String} cls CSS class to add to the element
23760  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23761  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23762  * @constructor
23763  * @param {Object} config An object with config options.
23764  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23765  */
23766
23767 Roo.Layer = function(config, existingEl){
23768     config = config || {};
23769     var dh = Roo.DomHelper;
23770     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23771     if(existingEl){
23772         this.dom = Roo.getDom(existingEl);
23773     }
23774     if(!this.dom){
23775         var o = config.dh || {tag: "div", cls: "x-layer"};
23776         this.dom = dh.append(pel, o);
23777     }
23778     if(config.cls){
23779         this.addClass(config.cls);
23780     }
23781     this.constrain = config.constrain !== false;
23782     this.visibilityMode = Roo.Element.VISIBILITY;
23783     if(config.id){
23784         this.id = this.dom.id = config.id;
23785     }else{
23786         this.id = Roo.id(this.dom);
23787     }
23788     this.zindex = config.zindex || this.getZIndex();
23789     this.position("absolute", this.zindex);
23790     if(config.shadow){
23791         this.shadowOffset = config.shadowOffset || 4;
23792         this.shadow = new Roo.Shadow({
23793             offset : this.shadowOffset,
23794             mode : config.shadow
23795         });
23796     }else{
23797         this.shadowOffset = 0;
23798     }
23799     this.useShim = config.shim !== false && Roo.useShims;
23800     this.useDisplay = config.useDisplay;
23801     this.hide();
23802 };
23803
23804 var supr = Roo.Element.prototype;
23805
23806 // shims are shared among layer to keep from having 100 iframes
23807 var shims = [];
23808
23809 Roo.extend(Roo.Layer, Roo.Element, {
23810
23811     getZIndex : function(){
23812         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23813     },
23814
23815     getShim : function(){
23816         if(!this.useShim){
23817             return null;
23818         }
23819         if(this.shim){
23820             return this.shim;
23821         }
23822         var shim = shims.shift();
23823         if(!shim){
23824             shim = this.createShim();
23825             shim.enableDisplayMode('block');
23826             shim.dom.style.display = 'none';
23827             shim.dom.style.visibility = 'visible';
23828         }
23829         var pn = this.dom.parentNode;
23830         if(shim.dom.parentNode != pn){
23831             pn.insertBefore(shim.dom, this.dom);
23832         }
23833         shim.setStyle('z-index', this.getZIndex()-2);
23834         this.shim = shim;
23835         return shim;
23836     },
23837
23838     hideShim : function(){
23839         if(this.shim){
23840             this.shim.setDisplayed(false);
23841             shims.push(this.shim);
23842             delete this.shim;
23843         }
23844     },
23845
23846     disableShadow : function(){
23847         if(this.shadow){
23848             this.shadowDisabled = true;
23849             this.shadow.hide();
23850             this.lastShadowOffset = this.shadowOffset;
23851             this.shadowOffset = 0;
23852         }
23853     },
23854
23855     enableShadow : function(show){
23856         if(this.shadow){
23857             this.shadowDisabled = false;
23858             this.shadowOffset = this.lastShadowOffset;
23859             delete this.lastShadowOffset;
23860             if(show){
23861                 this.sync(true);
23862             }
23863         }
23864     },
23865
23866     // private
23867     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23868     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23869     sync : function(doShow){
23870         var sw = this.shadow;
23871         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23872             var sh = this.getShim();
23873
23874             var w = this.getWidth(),
23875                 h = this.getHeight();
23876
23877             var l = this.getLeft(true),
23878                 t = this.getTop(true);
23879
23880             if(sw && !this.shadowDisabled){
23881                 if(doShow && !sw.isVisible()){
23882                     sw.show(this);
23883                 }else{
23884                     sw.realign(l, t, w, h);
23885                 }
23886                 if(sh){
23887                     if(doShow){
23888                        sh.show();
23889                     }
23890                     // fit the shim behind the shadow, so it is shimmed too
23891                     var a = sw.adjusts, s = sh.dom.style;
23892                     s.left = (Math.min(l, l+a.l))+"px";
23893                     s.top = (Math.min(t, t+a.t))+"px";
23894                     s.width = (w+a.w)+"px";
23895                     s.height = (h+a.h)+"px";
23896                 }
23897             }else if(sh){
23898                 if(doShow){
23899                    sh.show();
23900                 }
23901                 sh.setSize(w, h);
23902                 sh.setLeftTop(l, t);
23903             }
23904             
23905         }
23906     },
23907
23908     // private
23909     destroy : function(){
23910         this.hideShim();
23911         if(this.shadow){
23912             this.shadow.hide();
23913         }
23914         this.removeAllListeners();
23915         var pn = this.dom.parentNode;
23916         if(pn){
23917             pn.removeChild(this.dom);
23918         }
23919         Roo.Element.uncache(this.id);
23920     },
23921
23922     remove : function(){
23923         this.destroy();
23924     },
23925
23926     // private
23927     beginUpdate : function(){
23928         this.updating = true;
23929     },
23930
23931     // private
23932     endUpdate : function(){
23933         this.updating = false;
23934         this.sync(true);
23935     },
23936
23937     // private
23938     hideUnders : function(negOffset){
23939         if(this.shadow){
23940             this.shadow.hide();
23941         }
23942         this.hideShim();
23943     },
23944
23945     // private
23946     constrainXY : function(){
23947         if(this.constrain){
23948             var vw = Roo.lib.Dom.getViewWidth(),
23949                 vh = Roo.lib.Dom.getViewHeight();
23950             var s = Roo.get(document).getScroll();
23951
23952             var xy = this.getXY();
23953             var x = xy[0], y = xy[1];   
23954             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23955             // only move it if it needs it
23956             var moved = false;
23957             // first validate right/bottom
23958             if((x + w) > vw+s.left){
23959                 x = vw - w - this.shadowOffset;
23960                 moved = true;
23961             }
23962             if((y + h) > vh+s.top){
23963                 y = vh - h - this.shadowOffset;
23964                 moved = true;
23965             }
23966             // then make sure top/left isn't negative
23967             if(x < s.left){
23968                 x = s.left;
23969                 moved = true;
23970             }
23971             if(y < s.top){
23972                 y = s.top;
23973                 moved = true;
23974             }
23975             if(moved){
23976                 if(this.avoidY){
23977                     var ay = this.avoidY;
23978                     if(y <= ay && (y+h) >= ay){
23979                         y = ay-h-5;   
23980                     }
23981                 }
23982                 xy = [x, y];
23983                 this.storeXY(xy);
23984                 supr.setXY.call(this, xy);
23985                 this.sync();
23986             }
23987         }
23988     },
23989
23990     isVisible : function(){
23991         return this.visible;    
23992     },
23993
23994     // private
23995     showAction : function(){
23996         this.visible = true; // track visibility to prevent getStyle calls
23997         if(this.useDisplay === true){
23998             this.setDisplayed("");
23999         }else if(this.lastXY){
24000             supr.setXY.call(this, this.lastXY);
24001         }else if(this.lastLT){
24002             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
24003         }
24004     },
24005
24006     // private
24007     hideAction : function(){
24008         this.visible = false;
24009         if(this.useDisplay === true){
24010             this.setDisplayed(false);
24011         }else{
24012             this.setLeftTop(-10000,-10000);
24013         }
24014     },
24015
24016     // overridden Element method
24017     setVisible : function(v, a, d, c, e){
24018         if(v){
24019             this.showAction();
24020         }
24021         if(a && v){
24022             var cb = function(){
24023                 this.sync(true);
24024                 if(c){
24025                     c();
24026                 }
24027             }.createDelegate(this);
24028             supr.setVisible.call(this, true, true, d, cb, e);
24029         }else{
24030             if(!v){
24031                 this.hideUnders(true);
24032             }
24033             var cb = c;
24034             if(a){
24035                 cb = function(){
24036                     this.hideAction();
24037                     if(c){
24038                         c();
24039                     }
24040                 }.createDelegate(this);
24041             }
24042             supr.setVisible.call(this, v, a, d, cb, e);
24043             if(v){
24044                 this.sync(true);
24045             }else if(!a){
24046                 this.hideAction();
24047             }
24048         }
24049     },
24050
24051     storeXY : function(xy){
24052         delete this.lastLT;
24053         this.lastXY = xy;
24054     },
24055
24056     storeLeftTop : function(left, top){
24057         delete this.lastXY;
24058         this.lastLT = [left, top];
24059     },
24060
24061     // private
24062     beforeFx : function(){
24063         this.beforeAction();
24064         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24065     },
24066
24067     // private
24068     afterFx : function(){
24069         Roo.Layer.superclass.afterFx.apply(this, arguments);
24070         this.sync(this.isVisible());
24071     },
24072
24073     // private
24074     beforeAction : function(){
24075         if(!this.updating && this.shadow){
24076             this.shadow.hide();
24077         }
24078     },
24079
24080     // overridden Element method
24081     setLeft : function(left){
24082         this.storeLeftTop(left, this.getTop(true));
24083         supr.setLeft.apply(this, arguments);
24084         this.sync();
24085     },
24086
24087     setTop : function(top){
24088         this.storeLeftTop(this.getLeft(true), top);
24089         supr.setTop.apply(this, arguments);
24090         this.sync();
24091     },
24092
24093     setLeftTop : function(left, top){
24094         this.storeLeftTop(left, top);
24095         supr.setLeftTop.apply(this, arguments);
24096         this.sync();
24097     },
24098
24099     setXY : function(xy, a, d, c, e){
24100         this.fixDisplay();
24101         this.beforeAction();
24102         this.storeXY(xy);
24103         var cb = this.createCB(c);
24104         supr.setXY.call(this, xy, a, d, cb, e);
24105         if(!a){
24106             cb();
24107         }
24108     },
24109
24110     // private
24111     createCB : function(c){
24112         var el = this;
24113         return function(){
24114             el.constrainXY();
24115             el.sync(true);
24116             if(c){
24117                 c();
24118             }
24119         };
24120     },
24121
24122     // overridden Element method
24123     setX : function(x, a, d, c, e){
24124         this.setXY([x, this.getY()], a, d, c, e);
24125     },
24126
24127     // overridden Element method
24128     setY : function(y, a, d, c, e){
24129         this.setXY([this.getX(), y], a, d, c, e);
24130     },
24131
24132     // overridden Element method
24133     setSize : function(w, h, a, d, c, e){
24134         this.beforeAction();
24135         var cb = this.createCB(c);
24136         supr.setSize.call(this, w, h, a, d, cb, e);
24137         if(!a){
24138             cb();
24139         }
24140     },
24141
24142     // overridden Element method
24143     setWidth : function(w, a, d, c, e){
24144         this.beforeAction();
24145         var cb = this.createCB(c);
24146         supr.setWidth.call(this, w, a, d, cb, e);
24147         if(!a){
24148             cb();
24149         }
24150     },
24151
24152     // overridden Element method
24153     setHeight : function(h, a, d, c, e){
24154         this.beforeAction();
24155         var cb = this.createCB(c);
24156         supr.setHeight.call(this, h, a, d, cb, e);
24157         if(!a){
24158             cb();
24159         }
24160     },
24161
24162     // overridden Element method
24163     setBounds : function(x, y, w, h, a, d, c, e){
24164         this.beforeAction();
24165         var cb = this.createCB(c);
24166         if(!a){
24167             this.storeXY([x, y]);
24168             supr.setXY.call(this, [x, y]);
24169             supr.setSize.call(this, w, h, a, d, cb, e);
24170             cb();
24171         }else{
24172             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24173         }
24174         return this;
24175     },
24176     
24177     /**
24178      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24179      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24180      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24181      * @param {Number} zindex The new z-index to set
24182      * @return {this} The Layer
24183      */
24184     setZIndex : function(zindex){
24185         this.zindex = zindex;
24186         this.setStyle("z-index", zindex + 2);
24187         if(this.shadow){
24188             this.shadow.setZIndex(zindex + 1);
24189         }
24190         if(this.shim){
24191             this.shim.setStyle("z-index", zindex);
24192         }
24193     }
24194 });
24195 })();/*
24196  * Based on:
24197  * Ext JS Library 1.1.1
24198  * Copyright(c) 2006-2007, Ext JS, LLC.
24199  *
24200  * Originally Released Under LGPL - original licence link has changed is not relivant.
24201  *
24202  * Fork - LGPL
24203  * <script type="text/javascript">
24204  */
24205
24206
24207 /**
24208  * @class Roo.Shadow
24209  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24210  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24211  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24212  * @constructor
24213  * Create a new Shadow
24214  * @param {Object} config The config object
24215  */
24216 Roo.Shadow = function(config){
24217     Roo.apply(this, config);
24218     if(typeof this.mode != "string"){
24219         this.mode = this.defaultMode;
24220     }
24221     var o = this.offset, a = {h: 0};
24222     var rad = Math.floor(this.offset/2);
24223     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24224         case "drop":
24225             a.w = 0;
24226             a.l = a.t = o;
24227             a.t -= 1;
24228             if(Roo.isIE){
24229                 a.l -= this.offset + rad;
24230                 a.t -= this.offset + rad;
24231                 a.w -= rad;
24232                 a.h -= rad;
24233                 a.t += 1;
24234             }
24235         break;
24236         case "sides":
24237             a.w = (o*2);
24238             a.l = -o;
24239             a.t = o-1;
24240             if(Roo.isIE){
24241                 a.l -= (this.offset - rad);
24242                 a.t -= this.offset + rad;
24243                 a.l += 1;
24244                 a.w -= (this.offset - rad)*2;
24245                 a.w -= rad + 1;
24246                 a.h -= 1;
24247             }
24248         break;
24249         case "frame":
24250             a.w = a.h = (o*2);
24251             a.l = a.t = -o;
24252             a.t += 1;
24253             a.h -= 2;
24254             if(Roo.isIE){
24255                 a.l -= (this.offset - rad);
24256                 a.t -= (this.offset - rad);
24257                 a.l += 1;
24258                 a.w -= (this.offset + rad + 1);
24259                 a.h -= (this.offset + rad);
24260                 a.h += 1;
24261             }
24262         break;
24263     };
24264
24265     this.adjusts = a;
24266 };
24267
24268 Roo.Shadow.prototype = {
24269     /**
24270      * @cfg {String} mode
24271      * The shadow display mode.  Supports the following options:<br />
24272      * sides: Shadow displays on both sides and bottom only<br />
24273      * frame: Shadow displays equally on all four sides<br />
24274      * drop: Traditional bottom-right drop shadow (default)
24275      */
24276     /**
24277      * @cfg {String} offset
24278      * The number of pixels to offset the shadow from the element (defaults to 4)
24279      */
24280     offset: 4,
24281
24282     // private
24283     defaultMode: "drop",
24284
24285     /**
24286      * Displays the shadow under the target element
24287      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24288      */
24289     show : function(target){
24290         target = Roo.get(target);
24291         if(!this.el){
24292             this.el = Roo.Shadow.Pool.pull();
24293             if(this.el.dom.nextSibling != target.dom){
24294                 this.el.insertBefore(target);
24295             }
24296         }
24297         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24298         if(Roo.isIE){
24299             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24300         }
24301         this.realign(
24302             target.getLeft(true),
24303             target.getTop(true),
24304             target.getWidth(),
24305             target.getHeight()
24306         );
24307         this.el.dom.style.display = "block";
24308     },
24309
24310     /**
24311      * Returns true if the shadow is visible, else false
24312      */
24313     isVisible : function(){
24314         return this.el ? true : false;  
24315     },
24316
24317     /**
24318      * Direct alignment when values are already available. Show must be called at least once before
24319      * calling this method to ensure it is initialized.
24320      * @param {Number} left The target element left position
24321      * @param {Number} top The target element top position
24322      * @param {Number} width The target element width
24323      * @param {Number} height The target element height
24324      */
24325     realign : function(l, t, w, h){
24326         if(!this.el){
24327             return;
24328         }
24329         var a = this.adjusts, d = this.el.dom, s = d.style;
24330         var iea = 0;
24331         s.left = (l+a.l)+"px";
24332         s.top = (t+a.t)+"px";
24333         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24334  
24335         if(s.width != sws || s.height != shs){
24336             s.width = sws;
24337             s.height = shs;
24338             if(!Roo.isIE){
24339                 var cn = d.childNodes;
24340                 var sww = Math.max(0, (sw-12))+"px";
24341                 cn[0].childNodes[1].style.width = sww;
24342                 cn[1].childNodes[1].style.width = sww;
24343                 cn[2].childNodes[1].style.width = sww;
24344                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24345             }
24346         }
24347     },
24348
24349     /**
24350      * Hides this shadow
24351      */
24352     hide : function(){
24353         if(this.el){
24354             this.el.dom.style.display = "none";
24355             Roo.Shadow.Pool.push(this.el);
24356             delete this.el;
24357         }
24358     },
24359
24360     /**
24361      * Adjust the z-index of this shadow
24362      * @param {Number} zindex The new z-index
24363      */
24364     setZIndex : function(z){
24365         this.zIndex = z;
24366         if(this.el){
24367             this.el.setStyle("z-index", z);
24368         }
24369     }
24370 };
24371
24372 // Private utility class that manages the internal Shadow cache
24373 Roo.Shadow.Pool = function(){
24374     var p = [];
24375     var markup = Roo.isIE ?
24376                  '<div class="x-ie-shadow"></div>' :
24377                  '<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>';
24378     return {
24379         pull : function(){
24380             var sh = p.shift();
24381             if(!sh){
24382                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24383                 sh.autoBoxAdjust = false;
24384             }
24385             return sh;
24386         },
24387
24388         push : function(sh){
24389             p.push(sh);
24390         }
24391     };
24392 }();/*
24393  * Based on:
24394  * Ext JS Library 1.1.1
24395  * Copyright(c) 2006-2007, Ext JS, LLC.
24396  *
24397  * Originally Released Under LGPL - original licence link has changed is not relivant.
24398  *
24399  * Fork - LGPL
24400  * <script type="text/javascript">
24401  */
24402
24403
24404 /**
24405  * @class Roo.SplitBar
24406  * @extends Roo.util.Observable
24407  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24408  * <br><br>
24409  * Usage:
24410  * <pre><code>
24411 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24412                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24413 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24414 split.minSize = 100;
24415 split.maxSize = 600;
24416 split.animate = true;
24417 split.on('moved', splitterMoved);
24418 </code></pre>
24419  * @constructor
24420  * Create a new SplitBar
24421  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24422  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24423  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24424  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24425                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24426                         position of the SplitBar).
24427  */
24428 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24429     
24430     /** @private */
24431     this.el = Roo.get(dragElement, true);
24432     this.el.dom.unselectable = "on";
24433     /** @private */
24434     this.resizingEl = Roo.get(resizingElement, true);
24435
24436     /**
24437      * @private
24438      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24439      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24440      * @type Number
24441      */
24442     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24443     
24444     /**
24445      * The minimum size of the resizing element. (Defaults to 0)
24446      * @type Number
24447      */
24448     this.minSize = 0;
24449     
24450     /**
24451      * The maximum size of the resizing element. (Defaults to 2000)
24452      * @type Number
24453      */
24454     this.maxSize = 2000;
24455     
24456     /**
24457      * Whether to animate the transition to the new size
24458      * @type Boolean
24459      */
24460     this.animate = false;
24461     
24462     /**
24463      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24464      * @type Boolean
24465      */
24466     this.useShim = false;
24467     
24468     /** @private */
24469     this.shim = null;
24470     
24471     if(!existingProxy){
24472         /** @private */
24473         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24474     }else{
24475         this.proxy = Roo.get(existingProxy).dom;
24476     }
24477     /** @private */
24478     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24479     
24480     /** @private */
24481     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24482     
24483     /** @private */
24484     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24485     
24486     /** @private */
24487     this.dragSpecs = {};
24488     
24489     /**
24490      * @private The adapter to use to positon and resize elements
24491      */
24492     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24493     this.adapter.init(this);
24494     
24495     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24496         /** @private */
24497         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24498         this.el.addClass("x-splitbar-h");
24499     }else{
24500         /** @private */
24501         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24502         this.el.addClass("x-splitbar-v");
24503     }
24504     
24505     this.addEvents({
24506         /**
24507          * @event resize
24508          * Fires when the splitter is moved (alias for {@link #event-moved})
24509          * @param {Roo.SplitBar} this
24510          * @param {Number} newSize the new width or height
24511          */
24512         "resize" : true,
24513         /**
24514          * @event moved
24515          * Fires when the splitter is moved
24516          * @param {Roo.SplitBar} this
24517          * @param {Number} newSize the new width or height
24518          */
24519         "moved" : true,
24520         /**
24521          * @event beforeresize
24522          * Fires before the splitter is dragged
24523          * @param {Roo.SplitBar} this
24524          */
24525         "beforeresize" : true,
24526
24527         "beforeapply" : true
24528     });
24529
24530     Roo.util.Observable.call(this);
24531 };
24532
24533 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24534     onStartProxyDrag : function(x, y){
24535         this.fireEvent("beforeresize", this);
24536         if(!this.overlay){
24537             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24538             o.unselectable();
24539             o.enableDisplayMode("block");
24540             // all splitbars share the same overlay
24541             Roo.SplitBar.prototype.overlay = o;
24542         }
24543         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24544         this.overlay.show();
24545         Roo.get(this.proxy).setDisplayed("block");
24546         var size = this.adapter.getElementSize(this);
24547         this.activeMinSize = this.getMinimumSize();;
24548         this.activeMaxSize = this.getMaximumSize();;
24549         var c1 = size - this.activeMinSize;
24550         var c2 = Math.max(this.activeMaxSize - size, 0);
24551         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24552             this.dd.resetConstraints();
24553             this.dd.setXConstraint(
24554                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24555                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24556             );
24557             this.dd.setYConstraint(0, 0);
24558         }else{
24559             this.dd.resetConstraints();
24560             this.dd.setXConstraint(0, 0);
24561             this.dd.setYConstraint(
24562                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24563                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24564             );
24565          }
24566         this.dragSpecs.startSize = size;
24567         this.dragSpecs.startPoint = [x, y];
24568         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24569     },
24570     
24571     /** 
24572      * @private Called after the drag operation by the DDProxy
24573      */
24574     onEndProxyDrag : function(e){
24575         Roo.get(this.proxy).setDisplayed(false);
24576         var endPoint = Roo.lib.Event.getXY(e);
24577         if(this.overlay){
24578             this.overlay.hide();
24579         }
24580         var newSize;
24581         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24582             newSize = this.dragSpecs.startSize + 
24583                 (this.placement == Roo.SplitBar.LEFT ?
24584                     endPoint[0] - this.dragSpecs.startPoint[0] :
24585                     this.dragSpecs.startPoint[0] - endPoint[0]
24586                 );
24587         }else{
24588             newSize = this.dragSpecs.startSize + 
24589                 (this.placement == Roo.SplitBar.TOP ?
24590                     endPoint[1] - this.dragSpecs.startPoint[1] :
24591                     this.dragSpecs.startPoint[1] - endPoint[1]
24592                 );
24593         }
24594         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24595         if(newSize != this.dragSpecs.startSize){
24596             if(this.fireEvent('beforeapply', this, newSize) !== false){
24597                 this.adapter.setElementSize(this, newSize);
24598                 this.fireEvent("moved", this, newSize);
24599                 this.fireEvent("resize", this, newSize);
24600             }
24601         }
24602     },
24603     
24604     /**
24605      * Get the adapter this SplitBar uses
24606      * @return The adapter object
24607      */
24608     getAdapter : function(){
24609         return this.adapter;
24610     },
24611     
24612     /**
24613      * Set the adapter this SplitBar uses
24614      * @param {Object} adapter A SplitBar adapter object
24615      */
24616     setAdapter : function(adapter){
24617         this.adapter = adapter;
24618         this.adapter.init(this);
24619     },
24620     
24621     /**
24622      * Gets the minimum size for the resizing element
24623      * @return {Number} The minimum size
24624      */
24625     getMinimumSize : function(){
24626         return this.minSize;
24627     },
24628     
24629     /**
24630      * Sets the minimum size for the resizing element
24631      * @param {Number} minSize The minimum size
24632      */
24633     setMinimumSize : function(minSize){
24634         this.minSize = minSize;
24635     },
24636     
24637     /**
24638      * Gets the maximum size for the resizing element
24639      * @return {Number} The maximum size
24640      */
24641     getMaximumSize : function(){
24642         return this.maxSize;
24643     },
24644     
24645     /**
24646      * Sets the maximum size for the resizing element
24647      * @param {Number} maxSize The maximum size
24648      */
24649     setMaximumSize : function(maxSize){
24650         this.maxSize = maxSize;
24651     },
24652     
24653     /**
24654      * Sets the initialize size for the resizing element
24655      * @param {Number} size The initial size
24656      */
24657     setCurrentSize : function(size){
24658         var oldAnimate = this.animate;
24659         this.animate = false;
24660         this.adapter.setElementSize(this, size);
24661         this.animate = oldAnimate;
24662     },
24663     
24664     /**
24665      * Destroy this splitbar. 
24666      * @param {Boolean} removeEl True to remove the element
24667      */
24668     destroy : function(removeEl){
24669         if(this.shim){
24670             this.shim.remove();
24671         }
24672         this.dd.unreg();
24673         this.proxy.parentNode.removeChild(this.proxy);
24674         if(removeEl){
24675             this.el.remove();
24676         }
24677     }
24678 });
24679
24680 /**
24681  * @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.
24682  */
24683 Roo.SplitBar.createProxy = function(dir){
24684     var proxy = new Roo.Element(document.createElement("div"));
24685     proxy.unselectable();
24686     var cls = 'x-splitbar-proxy';
24687     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24688     document.body.appendChild(proxy.dom);
24689     return proxy.dom;
24690 };
24691
24692 /** 
24693  * @class Roo.SplitBar.BasicLayoutAdapter
24694  * Default Adapter. It assumes the splitter and resizing element are not positioned
24695  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24696  */
24697 Roo.SplitBar.BasicLayoutAdapter = function(){
24698 };
24699
24700 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24701     // do nothing for now
24702     init : function(s){
24703     
24704     },
24705     /**
24706      * Called before drag operations to get the current size of the resizing element. 
24707      * @param {Roo.SplitBar} s The SplitBar using this adapter
24708      */
24709      getElementSize : function(s){
24710         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24711             return s.resizingEl.getWidth();
24712         }else{
24713             return s.resizingEl.getHeight();
24714         }
24715     },
24716     
24717     /**
24718      * Called after drag operations to set the size of the resizing element.
24719      * @param {Roo.SplitBar} s The SplitBar using this adapter
24720      * @param {Number} newSize The new size to set
24721      * @param {Function} onComplete A function to be invoked when resizing is complete
24722      */
24723     setElementSize : function(s, newSize, onComplete){
24724         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24725             if(!s.animate){
24726                 s.resizingEl.setWidth(newSize);
24727                 if(onComplete){
24728                     onComplete(s, newSize);
24729                 }
24730             }else{
24731                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24732             }
24733         }else{
24734             
24735             if(!s.animate){
24736                 s.resizingEl.setHeight(newSize);
24737                 if(onComplete){
24738                     onComplete(s, newSize);
24739                 }
24740             }else{
24741                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24742             }
24743         }
24744     }
24745 };
24746
24747 /** 
24748  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24749  * @extends Roo.SplitBar.BasicLayoutAdapter
24750  * Adapter that  moves the splitter element to align with the resized sizing element. 
24751  * Used with an absolute positioned SplitBar.
24752  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24753  * document.body, make sure you assign an id to the body element.
24754  */
24755 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24756     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24757     this.container = Roo.get(container);
24758 };
24759
24760 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24761     init : function(s){
24762         this.basic.init(s);
24763     },
24764     
24765     getElementSize : function(s){
24766         return this.basic.getElementSize(s);
24767     },
24768     
24769     setElementSize : function(s, newSize, onComplete){
24770         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24771     },
24772     
24773     moveSplitter : function(s){
24774         var yes = Roo.SplitBar;
24775         switch(s.placement){
24776             case yes.LEFT:
24777                 s.el.setX(s.resizingEl.getRight());
24778                 break;
24779             case yes.RIGHT:
24780                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24781                 break;
24782             case yes.TOP:
24783                 s.el.setY(s.resizingEl.getBottom());
24784                 break;
24785             case yes.BOTTOM:
24786                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24787                 break;
24788         }
24789     }
24790 };
24791
24792 /**
24793  * Orientation constant - Create a vertical SplitBar
24794  * @static
24795  * @type Number
24796  */
24797 Roo.SplitBar.VERTICAL = 1;
24798
24799 /**
24800  * Orientation constant - Create a horizontal SplitBar
24801  * @static
24802  * @type Number
24803  */
24804 Roo.SplitBar.HORIZONTAL = 2;
24805
24806 /**
24807  * Placement constant - The resizing element is to the left of the splitter element
24808  * @static
24809  * @type Number
24810  */
24811 Roo.SplitBar.LEFT = 1;
24812
24813 /**
24814  * Placement constant - The resizing element is to the right of the splitter element
24815  * @static
24816  * @type Number
24817  */
24818 Roo.SplitBar.RIGHT = 2;
24819
24820 /**
24821  * Placement constant - The resizing element is positioned above the splitter element
24822  * @static
24823  * @type Number
24824  */
24825 Roo.SplitBar.TOP = 3;
24826
24827 /**
24828  * Placement constant - The resizing element is positioned under splitter element
24829  * @static
24830  * @type Number
24831  */
24832 Roo.SplitBar.BOTTOM = 4;
24833 /*
24834  * Based on:
24835  * Ext JS Library 1.1.1
24836  * Copyright(c) 2006-2007, Ext JS, LLC.
24837  *
24838  * Originally Released Under LGPL - original licence link has changed is not relivant.
24839  *
24840  * Fork - LGPL
24841  * <script type="text/javascript">
24842  */
24843
24844 /**
24845  * @class Roo.View
24846  * @extends Roo.util.Observable
24847  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24848  * This class also supports single and multi selection modes. <br>
24849  * Create a data model bound view:
24850  <pre><code>
24851  var store = new Roo.data.Store(...);
24852
24853  var view = new Roo.View({
24854     el : "my-element",
24855     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24856  
24857     singleSelect: true,
24858     selectedClass: "ydataview-selected",
24859     store: store
24860  });
24861
24862  // listen for node click?
24863  view.on("click", function(vw, index, node, e){
24864  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24865  });
24866
24867  // load XML data
24868  dataModel.load("foobar.xml");
24869  </code></pre>
24870  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24871  * <br><br>
24872  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24873  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24874  * 
24875  * Note: old style constructor is still suported (container, template, config)
24876  * 
24877  * @constructor
24878  * Create a new View
24879  * @param {Object} config The config object
24880  * 
24881  */
24882 Roo.View = function(config, depreciated_tpl, depreciated_config){
24883     
24884     this.parent = false;
24885     
24886     if (typeof(depreciated_tpl) == 'undefined') {
24887         // new way.. - universal constructor.
24888         Roo.apply(this, config);
24889         this.el  = Roo.get(this.el);
24890     } else {
24891         // old format..
24892         this.el  = Roo.get(config);
24893         this.tpl = depreciated_tpl;
24894         Roo.apply(this, depreciated_config);
24895     }
24896     this.wrapEl  = this.el.wrap().wrap();
24897     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24898     
24899     
24900     if(typeof(this.tpl) == "string"){
24901         this.tpl = new Roo.Template(this.tpl);
24902     } else {
24903         // support xtype ctors..
24904         this.tpl = new Roo.factory(this.tpl, Roo);
24905     }
24906     
24907     
24908     this.tpl.compile();
24909     
24910     /** @private */
24911     this.addEvents({
24912         /**
24913          * @event beforeclick
24914          * Fires before a click is processed. Returns false to cancel the default action.
24915          * @param {Roo.View} this
24916          * @param {Number} index The index of the target node
24917          * @param {HTMLElement} node The target node
24918          * @param {Roo.EventObject} e The raw event object
24919          */
24920             "beforeclick" : true,
24921         /**
24922          * @event click
24923          * Fires when a template node is clicked.
24924          * @param {Roo.View} this
24925          * @param {Number} index The index of the target node
24926          * @param {HTMLElement} node The target node
24927          * @param {Roo.EventObject} e The raw event object
24928          */
24929             "click" : true,
24930         /**
24931          * @event dblclick
24932          * Fires when a template node is double clicked.
24933          * @param {Roo.View} this
24934          * @param {Number} index The index of the target node
24935          * @param {HTMLElement} node The target node
24936          * @param {Roo.EventObject} e The raw event object
24937          */
24938             "dblclick" : true,
24939         /**
24940          * @event contextmenu
24941          * Fires when a template node is right clicked.
24942          * @param {Roo.View} this
24943          * @param {Number} index The index of the target node
24944          * @param {HTMLElement} node The target node
24945          * @param {Roo.EventObject} e The raw event object
24946          */
24947             "contextmenu" : true,
24948         /**
24949          * @event selectionchange
24950          * Fires when the selected nodes change.
24951          * @param {Roo.View} this
24952          * @param {Array} selections Array of the selected nodes
24953          */
24954             "selectionchange" : true,
24955     
24956         /**
24957          * @event beforeselect
24958          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24959          * @param {Roo.View} this
24960          * @param {HTMLElement} node The node to be selected
24961          * @param {Array} selections Array of currently selected nodes
24962          */
24963             "beforeselect" : true,
24964         /**
24965          * @event preparedata
24966          * Fires on every row to render, to allow you to change the data.
24967          * @param {Roo.View} this
24968          * @param {Object} data to be rendered (change this)
24969          */
24970           "preparedata" : true
24971           
24972           
24973         });
24974
24975
24976
24977     this.el.on({
24978         "click": this.onClick,
24979         "dblclick": this.onDblClick,
24980         "contextmenu": this.onContextMenu,
24981         scope:this
24982     });
24983
24984     this.selections = [];
24985     this.nodes = [];
24986     this.cmp = new Roo.CompositeElementLite([]);
24987     if(this.store){
24988         this.store = Roo.factory(this.store, Roo.data);
24989         this.setStore(this.store, true);
24990     }
24991     
24992     if ( this.footer && this.footer.xtype) {
24993            
24994          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24995         
24996         this.footer.dataSource = this.store
24997         this.footer.container = fctr;
24998         this.footer = Roo.factory(this.footer, Roo);
24999         fctr.insertFirst(this.el);
25000         
25001         // this is a bit insane - as the paging toolbar seems to detach the el..
25002 //        dom.parentNode.parentNode.parentNode
25003          // they get detached?
25004     }
25005     
25006     
25007     Roo.View.superclass.constructor.call(this);
25008     
25009     
25010 };
25011
25012 Roo.extend(Roo.View, Roo.util.Observable, {
25013     
25014      /**
25015      * @cfg {Roo.data.Store} store Data store to load data from.
25016      */
25017     store : false,
25018     
25019     /**
25020      * @cfg {String|Roo.Element} el The container element.
25021      */
25022     el : '',
25023     
25024     /**
25025      * @cfg {String|Roo.Template} tpl The template used by this View 
25026      */
25027     tpl : false,
25028     /**
25029      * @cfg {String} dataName the named area of the template to use as the data area
25030      *                          Works with domtemplates roo-name="name"
25031      */
25032     dataName: false,
25033     /**
25034      * @cfg {String} selectedClass The css class to add to selected nodes
25035      */
25036     selectedClass : "x-view-selected",
25037      /**
25038      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25039      */
25040     emptyText : "",
25041     
25042     /**
25043      * @cfg {String} text to display on mask (default Loading)
25044      */
25045     mask : false,
25046     /**
25047      * @cfg {Boolean} multiSelect Allow multiple selection
25048      */
25049     multiSelect : false,
25050     /**
25051      * @cfg {Boolean} singleSelect Allow single selection
25052      */
25053     singleSelect:  false,
25054     
25055     /**
25056      * @cfg {Boolean} toggleSelect - selecting 
25057      */
25058     toggleSelect : false,
25059     
25060     /**
25061      * @cfg {Boolean} tickable - selecting 
25062      */
25063     tickable : false,
25064     
25065     /**
25066      * Returns the element this view is bound to.
25067      * @return {Roo.Element}
25068      */
25069     getEl : function(){
25070         return this.wrapEl;
25071     },
25072     
25073     
25074
25075     /**
25076      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25077      */
25078     refresh : function(){
25079         //Roo.log('refresh');
25080         var t = this.tpl;
25081         
25082         // if we are using something like 'domtemplate', then
25083         // the what gets used is:
25084         // t.applySubtemplate(NAME, data, wrapping data..)
25085         // the outer template then get' applied with
25086         //     the store 'extra data'
25087         // and the body get's added to the
25088         //      roo-name="data" node?
25089         //      <span class='roo-tpl-{name}'></span> ?????
25090         
25091         
25092         
25093         this.clearSelections();
25094         this.el.update("");
25095         var html = [];
25096         var records = this.store.getRange();
25097         if(records.length < 1) {
25098             
25099             // is this valid??  = should it render a template??
25100             
25101             this.el.update(this.emptyText);
25102             return;
25103         }
25104         var el = this.el;
25105         if (this.dataName) {
25106             this.el.update(t.apply(this.store.meta)); //????
25107             el = this.el.child('.roo-tpl-' + this.dataName);
25108         }
25109         
25110         for(var i = 0, len = records.length; i < len; i++){
25111             var data = this.prepareData(records[i].data, i, records[i]);
25112             this.fireEvent("preparedata", this, data, i, records[i]);
25113             
25114             var d = Roo.apply({}, data);
25115             
25116             if(this.tickable){
25117                 Roo.apply(d, {'roo-id' : Roo.id()});
25118                 
25119                 var _this = this;
25120             
25121                 Roo.each(this.parent.item, function(item){
25122                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25123                         return;
25124                     }
25125                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25126                 });
25127             }
25128             
25129             html[html.length] = Roo.util.Format.trim(
25130                 this.dataName ?
25131                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25132                     t.apply(d)
25133             );
25134         }
25135         
25136         
25137         
25138         el.update(html.join(""));
25139         this.nodes = el.dom.childNodes;
25140         this.updateIndexes(0);
25141     },
25142     
25143
25144     /**
25145      * Function to override to reformat the data that is sent to
25146      * the template for each node.
25147      * DEPRICATED - use the preparedata event handler.
25148      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25149      * a JSON object for an UpdateManager bound view).
25150      */
25151     prepareData : function(data, index, record)
25152     {
25153         this.fireEvent("preparedata", this, data, index, record);
25154         return data;
25155     },
25156
25157     onUpdate : function(ds, record){
25158         // Roo.log('on update');   
25159         this.clearSelections();
25160         var index = this.store.indexOf(record);
25161         var n = this.nodes[index];
25162         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25163         n.parentNode.removeChild(n);
25164         this.updateIndexes(index, index);
25165     },
25166
25167     
25168     
25169 // --------- FIXME     
25170     onAdd : function(ds, records, index)
25171     {
25172         //Roo.log(['on Add', ds, records, index] );        
25173         this.clearSelections();
25174         if(this.nodes.length == 0){
25175             this.refresh();
25176             return;
25177         }
25178         var n = this.nodes[index];
25179         for(var i = 0, len = records.length; i < len; i++){
25180             var d = this.prepareData(records[i].data, i, records[i]);
25181             if(n){
25182                 this.tpl.insertBefore(n, d);
25183             }else{
25184                 
25185                 this.tpl.append(this.el, d);
25186             }
25187         }
25188         this.updateIndexes(index);
25189     },
25190
25191     onRemove : function(ds, record, index){
25192        // Roo.log('onRemove');
25193         this.clearSelections();
25194         var el = this.dataName  ?
25195             this.el.child('.roo-tpl-' + this.dataName) :
25196             this.el; 
25197         
25198         el.dom.removeChild(this.nodes[index]);
25199         this.updateIndexes(index);
25200     },
25201
25202     /**
25203      * Refresh an individual node.
25204      * @param {Number} index
25205      */
25206     refreshNode : function(index){
25207         this.onUpdate(this.store, this.store.getAt(index));
25208     },
25209
25210     updateIndexes : function(startIndex, endIndex){
25211         var ns = this.nodes;
25212         startIndex = startIndex || 0;
25213         endIndex = endIndex || ns.length - 1;
25214         for(var i = startIndex; i <= endIndex; i++){
25215             ns[i].nodeIndex = i;
25216         }
25217     },
25218
25219     /**
25220      * Changes the data store this view uses and refresh the view.
25221      * @param {Store} store
25222      */
25223     setStore : function(store, initial){
25224         if(!initial && this.store){
25225             this.store.un("datachanged", this.refresh);
25226             this.store.un("add", this.onAdd);
25227             this.store.un("remove", this.onRemove);
25228             this.store.un("update", this.onUpdate);
25229             this.store.un("clear", this.refresh);
25230             this.store.un("beforeload", this.onBeforeLoad);
25231             this.store.un("load", this.onLoad);
25232             this.store.un("loadexception", this.onLoad);
25233         }
25234         if(store){
25235           
25236             store.on("datachanged", this.refresh, this);
25237             store.on("add", this.onAdd, this);
25238             store.on("remove", this.onRemove, this);
25239             store.on("update", this.onUpdate, this);
25240             store.on("clear", this.refresh, this);
25241             store.on("beforeload", this.onBeforeLoad, this);
25242             store.on("load", this.onLoad, this);
25243             store.on("loadexception", this.onLoad, this);
25244         }
25245         
25246         if(store){
25247             this.refresh();
25248         }
25249     },
25250     /**
25251      * onbeforeLoad - masks the loading area.
25252      *
25253      */
25254     onBeforeLoad : function(store,opts)
25255     {
25256          //Roo.log('onBeforeLoad');   
25257         if (!opts.add) {
25258             this.el.update("");
25259         }
25260         this.el.mask(this.mask ? this.mask : "Loading" ); 
25261     },
25262     onLoad : function ()
25263     {
25264         this.el.unmask();
25265     },
25266     
25267
25268     /**
25269      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25270      * @param {HTMLElement} node
25271      * @return {HTMLElement} The template node
25272      */
25273     findItemFromChild : function(node){
25274         var el = this.dataName  ?
25275             this.el.child('.roo-tpl-' + this.dataName,true) :
25276             this.el.dom; 
25277         
25278         if(!node || node.parentNode == el){
25279                     return node;
25280             }
25281             var p = node.parentNode;
25282             while(p && p != el){
25283             if(p.parentNode == el){
25284                 return p;
25285             }
25286             p = p.parentNode;
25287         }
25288             return null;
25289     },
25290
25291     /** @ignore */
25292     onClick : function(e){
25293         var item = this.findItemFromChild(e.getTarget());
25294         if(item){
25295             var index = this.indexOf(item);
25296             if(this.onItemClick(item, index, e) !== false){
25297                 this.fireEvent("click", this, index, item, e);
25298             }
25299         }else{
25300             this.clearSelections();
25301         }
25302     },
25303
25304     /** @ignore */
25305     onContextMenu : function(e){
25306         var item = this.findItemFromChild(e.getTarget());
25307         if(item){
25308             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25309         }
25310     },
25311
25312     /** @ignore */
25313     onDblClick : function(e){
25314         var item = this.findItemFromChild(e.getTarget());
25315         if(item){
25316             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25317         }
25318     },
25319
25320     onItemClick : function(item, index, e)
25321     {
25322         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25323             return false;
25324         }
25325         if (this.toggleSelect) {
25326             var m = this.isSelected(item) ? 'unselect' : 'select';
25327             //Roo.log(m);
25328             var _t = this;
25329             _t[m](item, true, false);
25330             return true;
25331         }
25332         if(this.multiSelect || this.singleSelect){
25333             if(this.multiSelect && e.shiftKey && this.lastSelection){
25334                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25335             }else{
25336                 this.select(item, this.multiSelect && e.ctrlKey);
25337                 this.lastSelection = item;
25338             }
25339             
25340             if(!this.tickable){
25341                 e.preventDefault();
25342             }
25343             
25344         }
25345         return true;
25346     },
25347
25348     /**
25349      * Get the number of selected nodes.
25350      * @return {Number}
25351      */
25352     getSelectionCount : function(){
25353         return this.selections.length;
25354     },
25355
25356     /**
25357      * Get the currently selected nodes.
25358      * @return {Array} An array of HTMLElements
25359      */
25360     getSelectedNodes : function(){
25361         return this.selections;
25362     },
25363
25364     /**
25365      * Get the indexes of the selected nodes.
25366      * @return {Array}
25367      */
25368     getSelectedIndexes : function(){
25369         var indexes = [], s = this.selections;
25370         for(var i = 0, len = s.length; i < len; i++){
25371             indexes.push(s[i].nodeIndex);
25372         }
25373         return indexes;
25374     },
25375
25376     /**
25377      * Clear all selections
25378      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25379      */
25380     clearSelections : function(suppressEvent){
25381         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25382             this.cmp.elements = this.selections;
25383             this.cmp.removeClass(this.selectedClass);
25384             this.selections = [];
25385             if(!suppressEvent){
25386                 this.fireEvent("selectionchange", this, this.selections);
25387             }
25388         }
25389     },
25390
25391     /**
25392      * Returns true if the passed node is selected
25393      * @param {HTMLElement/Number} node The node or node index
25394      * @return {Boolean}
25395      */
25396     isSelected : function(node){
25397         var s = this.selections;
25398         if(s.length < 1){
25399             return false;
25400         }
25401         node = this.getNode(node);
25402         return s.indexOf(node) !== -1;
25403     },
25404
25405     /**
25406      * Selects nodes.
25407      * @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
25408      * @param {Boolean} keepExisting (optional) true to keep existing selections
25409      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25410      */
25411     select : function(nodeInfo, keepExisting, suppressEvent){
25412         if(nodeInfo instanceof Array){
25413             if(!keepExisting){
25414                 this.clearSelections(true);
25415             }
25416             for(var i = 0, len = nodeInfo.length; i < len; i++){
25417                 this.select(nodeInfo[i], true, true);
25418             }
25419             return;
25420         } 
25421         var node = this.getNode(nodeInfo);
25422         if(!node || this.isSelected(node)){
25423             return; // already selected.
25424         }
25425         if(!keepExisting){
25426             this.clearSelections(true);
25427         }
25428         
25429         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25430             Roo.fly(node).addClass(this.selectedClass);
25431             this.selections.push(node);
25432             if(!suppressEvent){
25433                 this.fireEvent("selectionchange", this, this.selections);
25434             }
25435         }
25436         
25437         
25438     },
25439       /**
25440      * Unselects nodes.
25441      * @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
25442      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25443      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25444      */
25445     unselect : function(nodeInfo, keepExisting, suppressEvent)
25446     {
25447         if(nodeInfo instanceof Array){
25448             Roo.each(this.selections, function(s) {
25449                 this.unselect(s, nodeInfo);
25450             }, this);
25451             return;
25452         }
25453         var node = this.getNode(nodeInfo);
25454         if(!node || !this.isSelected(node)){
25455             //Roo.log("not selected");
25456             return; // not selected.
25457         }
25458         // fireevent???
25459         var ns = [];
25460         Roo.each(this.selections, function(s) {
25461             if (s == node ) {
25462                 Roo.fly(node).removeClass(this.selectedClass);
25463
25464                 return;
25465             }
25466             ns.push(s);
25467         },this);
25468         
25469         this.selections= ns;
25470         this.fireEvent("selectionchange", this, this.selections);
25471     },
25472
25473     /**
25474      * Gets a template node.
25475      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25476      * @return {HTMLElement} The node or null if it wasn't found
25477      */
25478     getNode : function(nodeInfo){
25479         if(typeof nodeInfo == "string"){
25480             return document.getElementById(nodeInfo);
25481         }else if(typeof nodeInfo == "number"){
25482             return this.nodes[nodeInfo];
25483         }
25484         return nodeInfo;
25485     },
25486
25487     /**
25488      * Gets a range template nodes.
25489      * @param {Number} startIndex
25490      * @param {Number} endIndex
25491      * @return {Array} An array of nodes
25492      */
25493     getNodes : function(start, end){
25494         var ns = this.nodes;
25495         start = start || 0;
25496         end = typeof end == "undefined" ? ns.length - 1 : end;
25497         var nodes = [];
25498         if(start <= end){
25499             for(var i = start; i <= end; i++){
25500                 nodes.push(ns[i]);
25501             }
25502         } else{
25503             for(var i = start; i >= end; i--){
25504                 nodes.push(ns[i]);
25505             }
25506         }
25507         return nodes;
25508     },
25509
25510     /**
25511      * Finds the index of the passed node
25512      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25513      * @return {Number} The index of the node or -1
25514      */
25515     indexOf : function(node){
25516         node = this.getNode(node);
25517         if(typeof node.nodeIndex == "number"){
25518             return node.nodeIndex;
25519         }
25520         var ns = this.nodes;
25521         for(var i = 0, len = ns.length; i < len; i++){
25522             if(ns[i] == node){
25523                 return i;
25524             }
25525         }
25526         return -1;
25527     }
25528 });
25529 /*
25530  * Based on:
25531  * Ext JS Library 1.1.1
25532  * Copyright(c) 2006-2007, Ext JS, LLC.
25533  *
25534  * Originally Released Under LGPL - original licence link has changed is not relivant.
25535  *
25536  * Fork - LGPL
25537  * <script type="text/javascript">
25538  */
25539
25540 /**
25541  * @class Roo.JsonView
25542  * @extends Roo.View
25543  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25544 <pre><code>
25545 var view = new Roo.JsonView({
25546     container: "my-element",
25547     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25548     multiSelect: true, 
25549     jsonRoot: "data" 
25550 });
25551
25552 // listen for node click?
25553 view.on("click", function(vw, index, node, e){
25554     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25555 });
25556
25557 // direct load of JSON data
25558 view.load("foobar.php");
25559
25560 // Example from my blog list
25561 var tpl = new Roo.Template(
25562     '&lt;div class="entry"&gt;' +
25563     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25564     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25565     "&lt;/div&gt;&lt;hr /&gt;"
25566 );
25567
25568 var moreView = new Roo.JsonView({
25569     container :  "entry-list", 
25570     template : tpl,
25571     jsonRoot: "posts"
25572 });
25573 moreView.on("beforerender", this.sortEntries, this);
25574 moreView.load({
25575     url: "/blog/get-posts.php",
25576     params: "allposts=true",
25577     text: "Loading Blog Entries..."
25578 });
25579 </code></pre>
25580
25581 * Note: old code is supported with arguments : (container, template, config)
25582
25583
25584  * @constructor
25585  * Create a new JsonView
25586  * 
25587  * @param {Object} config The config object
25588  * 
25589  */
25590 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25591     
25592     
25593     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25594
25595     var um = this.el.getUpdateManager();
25596     um.setRenderer(this);
25597     um.on("update", this.onLoad, this);
25598     um.on("failure", this.onLoadException, this);
25599
25600     /**
25601      * @event beforerender
25602      * Fires before rendering of the downloaded JSON data.
25603      * @param {Roo.JsonView} this
25604      * @param {Object} data The JSON data loaded
25605      */
25606     /**
25607      * @event load
25608      * Fires when data is loaded.
25609      * @param {Roo.JsonView} this
25610      * @param {Object} data The JSON data loaded
25611      * @param {Object} response The raw Connect response object
25612      */
25613     /**
25614      * @event loadexception
25615      * Fires when loading fails.
25616      * @param {Roo.JsonView} this
25617      * @param {Object} response The raw Connect response object
25618      */
25619     this.addEvents({
25620         'beforerender' : true,
25621         'load' : true,
25622         'loadexception' : true
25623     });
25624 };
25625 Roo.extend(Roo.JsonView, Roo.View, {
25626     /**
25627      * @type {String} The root property in the loaded JSON object that contains the data
25628      */
25629     jsonRoot : "",
25630
25631     /**
25632      * Refreshes the view.
25633      */
25634     refresh : function(){
25635         this.clearSelections();
25636         this.el.update("");
25637         var html = [];
25638         var o = this.jsonData;
25639         if(o && o.length > 0){
25640             for(var i = 0, len = o.length; i < len; i++){
25641                 var data = this.prepareData(o[i], i, o);
25642                 html[html.length] = this.tpl.apply(data);
25643             }
25644         }else{
25645             html.push(this.emptyText);
25646         }
25647         this.el.update(html.join(""));
25648         this.nodes = this.el.dom.childNodes;
25649         this.updateIndexes(0);
25650     },
25651
25652     /**
25653      * 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.
25654      * @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:
25655      <pre><code>
25656      view.load({
25657          url: "your-url.php",
25658          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25659          callback: yourFunction,
25660          scope: yourObject, //(optional scope)
25661          discardUrl: false,
25662          nocache: false,
25663          text: "Loading...",
25664          timeout: 30,
25665          scripts: false
25666      });
25667      </code></pre>
25668      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25669      * 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.
25670      * @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}
25671      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25672      * @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.
25673      */
25674     load : function(){
25675         var um = this.el.getUpdateManager();
25676         um.update.apply(um, arguments);
25677     },
25678
25679     render : function(el, response){
25680         this.clearSelections();
25681         this.el.update("");
25682         var o;
25683         try{
25684             o = Roo.util.JSON.decode(response.responseText);
25685             if(this.jsonRoot){
25686                 
25687                 o = o[this.jsonRoot];
25688             }
25689         } catch(e){
25690         }
25691         /**
25692          * The current JSON data or null
25693          */
25694         this.jsonData = o;
25695         this.beforeRender();
25696         this.refresh();
25697     },
25698
25699 /**
25700  * Get the number of records in the current JSON dataset
25701  * @return {Number}
25702  */
25703     getCount : function(){
25704         return this.jsonData ? this.jsonData.length : 0;
25705     },
25706
25707 /**
25708  * Returns the JSON object for the specified node(s)
25709  * @param {HTMLElement/Array} node The node or an array of nodes
25710  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25711  * you get the JSON object for the node
25712  */
25713     getNodeData : function(node){
25714         if(node instanceof Array){
25715             var data = [];
25716             for(var i = 0, len = node.length; i < len; i++){
25717                 data.push(this.getNodeData(node[i]));
25718             }
25719             return data;
25720         }
25721         return this.jsonData[this.indexOf(node)] || null;
25722     },
25723
25724     beforeRender : function(){
25725         this.snapshot = this.jsonData;
25726         if(this.sortInfo){
25727             this.sort.apply(this, this.sortInfo);
25728         }
25729         this.fireEvent("beforerender", this, this.jsonData);
25730     },
25731
25732     onLoad : function(el, o){
25733         this.fireEvent("load", this, this.jsonData, o);
25734     },
25735
25736     onLoadException : function(el, o){
25737         this.fireEvent("loadexception", this, o);
25738     },
25739
25740 /**
25741  * Filter the data by a specific property.
25742  * @param {String} property A property on your JSON objects
25743  * @param {String/RegExp} value Either string that the property values
25744  * should start with, or a RegExp to test against the property
25745  */
25746     filter : function(property, value){
25747         if(this.jsonData){
25748             var data = [];
25749             var ss = this.snapshot;
25750             if(typeof value == "string"){
25751                 var vlen = value.length;
25752                 if(vlen == 0){
25753                     this.clearFilter();
25754                     return;
25755                 }
25756                 value = value.toLowerCase();
25757                 for(var i = 0, len = ss.length; i < len; i++){
25758                     var o = ss[i];
25759                     if(o[property].substr(0, vlen).toLowerCase() == value){
25760                         data.push(o);
25761                     }
25762                 }
25763             } else if(value.exec){ // regex?
25764                 for(var i = 0, len = ss.length; i < len; i++){
25765                     var o = ss[i];
25766                     if(value.test(o[property])){
25767                         data.push(o);
25768                     }
25769                 }
25770             } else{
25771                 return;
25772             }
25773             this.jsonData = data;
25774             this.refresh();
25775         }
25776     },
25777
25778 /**
25779  * Filter by a function. The passed function will be called with each
25780  * object in the current dataset. If the function returns true the value is kept,
25781  * otherwise it is filtered.
25782  * @param {Function} fn
25783  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25784  */
25785     filterBy : function(fn, scope){
25786         if(this.jsonData){
25787             var data = [];
25788             var ss = this.snapshot;
25789             for(var i = 0, len = ss.length; i < len; i++){
25790                 var o = ss[i];
25791                 if(fn.call(scope || this, o)){
25792                     data.push(o);
25793                 }
25794             }
25795             this.jsonData = data;
25796             this.refresh();
25797         }
25798     },
25799
25800 /**
25801  * Clears the current filter.
25802  */
25803     clearFilter : function(){
25804         if(this.snapshot && this.jsonData != this.snapshot){
25805             this.jsonData = this.snapshot;
25806             this.refresh();
25807         }
25808     },
25809
25810
25811 /**
25812  * Sorts the data for this view and refreshes it.
25813  * @param {String} property A property on your JSON objects to sort on
25814  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25815  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25816  */
25817     sort : function(property, dir, sortType){
25818         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25819         if(this.jsonData){
25820             var p = property;
25821             var dsc = dir && dir.toLowerCase() == "desc";
25822             var f = function(o1, o2){
25823                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25824                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25825                 ;
25826                 if(v1 < v2){
25827                     return dsc ? +1 : -1;
25828                 } else if(v1 > v2){
25829                     return dsc ? -1 : +1;
25830                 } else{
25831                     return 0;
25832                 }
25833             };
25834             this.jsonData.sort(f);
25835             this.refresh();
25836             if(this.jsonData != this.snapshot){
25837                 this.snapshot.sort(f);
25838             }
25839         }
25840     }
25841 });/*
25842  * Based on:
25843  * Ext JS Library 1.1.1
25844  * Copyright(c) 2006-2007, Ext JS, LLC.
25845  *
25846  * Originally Released Under LGPL - original licence link has changed is not relivant.
25847  *
25848  * Fork - LGPL
25849  * <script type="text/javascript">
25850  */
25851  
25852
25853 /**
25854  * @class Roo.ColorPalette
25855  * @extends Roo.Component
25856  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25857  * Here's an example of typical usage:
25858  * <pre><code>
25859 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25860 cp.render('my-div');
25861
25862 cp.on('select', function(palette, selColor){
25863     // do something with selColor
25864 });
25865 </code></pre>
25866  * @constructor
25867  * Create a new ColorPalette
25868  * @param {Object} config The config object
25869  */
25870 Roo.ColorPalette = function(config){
25871     Roo.ColorPalette.superclass.constructor.call(this, config);
25872     this.addEvents({
25873         /**
25874              * @event select
25875              * Fires when a color is selected
25876              * @param {ColorPalette} this
25877              * @param {String} color The 6-digit color hex code (without the # symbol)
25878              */
25879         select: true
25880     });
25881
25882     if(this.handler){
25883         this.on("select", this.handler, this.scope, true);
25884     }
25885 };
25886 Roo.extend(Roo.ColorPalette, Roo.Component, {
25887     /**
25888      * @cfg {String} itemCls
25889      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25890      */
25891     itemCls : "x-color-palette",
25892     /**
25893      * @cfg {String} value
25894      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25895      * the hex codes are case-sensitive.
25896      */
25897     value : null,
25898     clickEvent:'click',
25899     // private
25900     ctype: "Roo.ColorPalette",
25901
25902     /**
25903      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25904      */
25905     allowReselect : false,
25906
25907     /**
25908      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25909      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25910      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25911      * of colors with the width setting until the box is symmetrical.</p>
25912      * <p>You can override individual colors if needed:</p>
25913      * <pre><code>
25914 var cp = new Roo.ColorPalette();
25915 cp.colors[0] = "FF0000";  // change the first box to red
25916 </code></pre>
25917
25918 Or you can provide a custom array of your own for complete control:
25919 <pre><code>
25920 var cp = new Roo.ColorPalette();
25921 cp.colors = ["000000", "993300", "333300"];
25922 </code></pre>
25923      * @type Array
25924      */
25925     colors : [
25926         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25927         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25928         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25929         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25930         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25931     ],
25932
25933     // private
25934     onRender : function(container, position){
25935         var t = new Roo.MasterTemplate(
25936             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25937         );
25938         var c = this.colors;
25939         for(var i = 0, len = c.length; i < len; i++){
25940             t.add([c[i]]);
25941         }
25942         var el = document.createElement("div");
25943         el.className = this.itemCls;
25944         t.overwrite(el);
25945         container.dom.insertBefore(el, position);
25946         this.el = Roo.get(el);
25947         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25948         if(this.clickEvent != 'click'){
25949             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25950         }
25951     },
25952
25953     // private
25954     afterRender : function(){
25955         Roo.ColorPalette.superclass.afterRender.call(this);
25956         if(this.value){
25957             var s = this.value;
25958             this.value = null;
25959             this.select(s);
25960         }
25961     },
25962
25963     // private
25964     handleClick : function(e, t){
25965         e.preventDefault();
25966         if(!this.disabled){
25967             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25968             this.select(c.toUpperCase());
25969         }
25970     },
25971
25972     /**
25973      * Selects the specified color in the palette (fires the select event)
25974      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25975      */
25976     select : function(color){
25977         color = color.replace("#", "");
25978         if(color != this.value || this.allowReselect){
25979             var el = this.el;
25980             if(this.value){
25981                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25982             }
25983             el.child("a.color-"+color).addClass("x-color-palette-sel");
25984             this.value = color;
25985             this.fireEvent("select", this, color);
25986         }
25987     }
25988 });/*
25989  * Based on:
25990  * Ext JS Library 1.1.1
25991  * Copyright(c) 2006-2007, Ext JS, LLC.
25992  *
25993  * Originally Released Under LGPL - original licence link has changed is not relivant.
25994  *
25995  * Fork - LGPL
25996  * <script type="text/javascript">
25997  */
25998  
25999 /**
26000  * @class Roo.DatePicker
26001  * @extends Roo.Component
26002  * Simple date picker class.
26003  * @constructor
26004  * Create a new DatePicker
26005  * @param {Object} config The config object
26006  */
26007 Roo.DatePicker = function(config){
26008     Roo.DatePicker.superclass.constructor.call(this, config);
26009
26010     this.value = config && config.value ?
26011                  config.value.clearTime() : new Date().clearTime();
26012
26013     this.addEvents({
26014         /**
26015              * @event select
26016              * Fires when a date is selected
26017              * @param {DatePicker} this
26018              * @param {Date} date The selected date
26019              */
26020         'select': true,
26021         /**
26022              * @event monthchange
26023              * Fires when the displayed month changes 
26024              * @param {DatePicker} this
26025              * @param {Date} date The selected month
26026              */
26027         'monthchange': true
26028     });
26029
26030     if(this.handler){
26031         this.on("select", this.handler,  this.scope || this);
26032     }
26033     // build the disabledDatesRE
26034     if(!this.disabledDatesRE && this.disabledDates){
26035         var dd = this.disabledDates;
26036         var re = "(?:";
26037         for(var i = 0; i < dd.length; i++){
26038             re += dd[i];
26039             if(i != dd.length-1) re += "|";
26040         }
26041         this.disabledDatesRE = new RegExp(re + ")");
26042     }
26043 };
26044
26045 Roo.extend(Roo.DatePicker, Roo.Component, {
26046     /**
26047      * @cfg {String} todayText
26048      * The text to display on the button that selects the current date (defaults to "Today")
26049      */
26050     todayText : "Today",
26051     /**
26052      * @cfg {String} okText
26053      * The text to display on the ok button
26054      */
26055     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26056     /**
26057      * @cfg {String} cancelText
26058      * The text to display on the cancel button
26059      */
26060     cancelText : "Cancel",
26061     /**
26062      * @cfg {String} todayTip
26063      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26064      */
26065     todayTip : "{0} (Spacebar)",
26066     /**
26067      * @cfg {Date} minDate
26068      * Minimum allowable date (JavaScript date object, defaults to null)
26069      */
26070     minDate : null,
26071     /**
26072      * @cfg {Date} maxDate
26073      * Maximum allowable date (JavaScript date object, defaults to null)
26074      */
26075     maxDate : null,
26076     /**
26077      * @cfg {String} minText
26078      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26079      */
26080     minText : "This date is before the minimum date",
26081     /**
26082      * @cfg {String} maxText
26083      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26084      */
26085     maxText : "This date is after the maximum date",
26086     /**
26087      * @cfg {String} format
26088      * The default date format string which can be overriden for localization support.  The format must be
26089      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26090      */
26091     format : "m/d/y",
26092     /**
26093      * @cfg {Array} disabledDays
26094      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26095      */
26096     disabledDays : null,
26097     /**
26098      * @cfg {String} disabledDaysText
26099      * The tooltip to display when the date falls on a disabled day (defaults to "")
26100      */
26101     disabledDaysText : "",
26102     /**
26103      * @cfg {RegExp} disabledDatesRE
26104      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26105      */
26106     disabledDatesRE : null,
26107     /**
26108      * @cfg {String} disabledDatesText
26109      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26110      */
26111     disabledDatesText : "",
26112     /**
26113      * @cfg {Boolean} constrainToViewport
26114      * True to constrain the date picker to the viewport (defaults to true)
26115      */
26116     constrainToViewport : true,
26117     /**
26118      * @cfg {Array} monthNames
26119      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26120      */
26121     monthNames : Date.monthNames,
26122     /**
26123      * @cfg {Array} dayNames
26124      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26125      */
26126     dayNames : Date.dayNames,
26127     /**
26128      * @cfg {String} nextText
26129      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26130      */
26131     nextText: 'Next Month (Control+Right)',
26132     /**
26133      * @cfg {String} prevText
26134      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26135      */
26136     prevText: 'Previous Month (Control+Left)',
26137     /**
26138      * @cfg {String} monthYearText
26139      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26140      */
26141     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26142     /**
26143      * @cfg {Number} startDay
26144      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26145      */
26146     startDay : 0,
26147     /**
26148      * @cfg {Bool} showClear
26149      * Show a clear button (usefull for date form elements that can be blank.)
26150      */
26151     
26152     showClear: false,
26153     
26154     /**
26155      * Sets the value of the date field
26156      * @param {Date} value The date to set
26157      */
26158     setValue : function(value){
26159         var old = this.value;
26160         
26161         if (typeof(value) == 'string') {
26162          
26163             value = Date.parseDate(value, this.format);
26164         }
26165         if (!value) {
26166             value = new Date();
26167         }
26168         
26169         this.value = value.clearTime(true);
26170         if(this.el){
26171             this.update(this.value);
26172         }
26173     },
26174
26175     /**
26176      * Gets the current selected value of the date field
26177      * @return {Date} The selected date
26178      */
26179     getValue : function(){
26180         return this.value;
26181     },
26182
26183     // private
26184     focus : function(){
26185         if(this.el){
26186             this.update(this.activeDate);
26187         }
26188     },
26189
26190     // privateval
26191     onRender : function(container, position){
26192         
26193         var m = [
26194              '<table cellspacing="0">',
26195                 '<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>',
26196                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26197         var dn = this.dayNames;
26198         for(var i = 0; i < 7; i++){
26199             var d = this.startDay+i;
26200             if(d > 6){
26201                 d = d-7;
26202             }
26203             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26204         }
26205         m[m.length] = "</tr></thead><tbody><tr>";
26206         for(var i = 0; i < 42; i++) {
26207             if(i % 7 == 0 && i != 0){
26208                 m[m.length] = "</tr><tr>";
26209             }
26210             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26211         }
26212         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26213             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26214
26215         var el = document.createElement("div");
26216         el.className = "x-date-picker";
26217         el.innerHTML = m.join("");
26218
26219         container.dom.insertBefore(el, position);
26220
26221         this.el = Roo.get(el);
26222         this.eventEl = Roo.get(el.firstChild);
26223
26224         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26225             handler: this.showPrevMonth,
26226             scope: this,
26227             preventDefault:true,
26228             stopDefault:true
26229         });
26230
26231         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26232             handler: this.showNextMonth,
26233             scope: this,
26234             preventDefault:true,
26235             stopDefault:true
26236         });
26237
26238         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26239
26240         this.monthPicker = this.el.down('div.x-date-mp');
26241         this.monthPicker.enableDisplayMode('block');
26242         
26243         var kn = new Roo.KeyNav(this.eventEl, {
26244             "left" : function(e){
26245                 e.ctrlKey ?
26246                     this.showPrevMonth() :
26247                     this.update(this.activeDate.add("d", -1));
26248             },
26249
26250             "right" : function(e){
26251                 e.ctrlKey ?
26252                     this.showNextMonth() :
26253                     this.update(this.activeDate.add("d", 1));
26254             },
26255
26256             "up" : function(e){
26257                 e.ctrlKey ?
26258                     this.showNextYear() :
26259                     this.update(this.activeDate.add("d", -7));
26260             },
26261
26262             "down" : function(e){
26263                 e.ctrlKey ?
26264                     this.showPrevYear() :
26265                     this.update(this.activeDate.add("d", 7));
26266             },
26267
26268             "pageUp" : function(e){
26269                 this.showNextMonth();
26270             },
26271
26272             "pageDown" : function(e){
26273                 this.showPrevMonth();
26274             },
26275
26276             "enter" : function(e){
26277                 e.stopPropagation();
26278                 return true;
26279             },
26280
26281             scope : this
26282         });
26283
26284         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26285
26286         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26287
26288         this.el.unselectable();
26289         
26290         this.cells = this.el.select("table.x-date-inner tbody td");
26291         this.textNodes = this.el.query("table.x-date-inner tbody span");
26292
26293         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26294             text: "&#160;",
26295             tooltip: this.monthYearText
26296         });
26297
26298         this.mbtn.on('click', this.showMonthPicker, this);
26299         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26300
26301
26302         var today = (new Date()).dateFormat(this.format);
26303         
26304         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26305         if (this.showClear) {
26306             baseTb.add( new Roo.Toolbar.Fill());
26307         }
26308         baseTb.add({
26309             text: String.format(this.todayText, today),
26310             tooltip: String.format(this.todayTip, today),
26311             handler: this.selectToday,
26312             scope: this
26313         });
26314         
26315         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26316             
26317         //});
26318         if (this.showClear) {
26319             
26320             baseTb.add( new Roo.Toolbar.Fill());
26321             baseTb.add({
26322                 text: '&#160;',
26323                 cls: 'x-btn-icon x-btn-clear',
26324                 handler: function() {
26325                     //this.value = '';
26326                     this.fireEvent("select", this, '');
26327                 },
26328                 scope: this
26329             });
26330         }
26331         
26332         
26333         if(Roo.isIE){
26334             this.el.repaint();
26335         }
26336         this.update(this.value);
26337     },
26338
26339     createMonthPicker : function(){
26340         if(!this.monthPicker.dom.firstChild){
26341             var buf = ['<table border="0" cellspacing="0">'];
26342             for(var i = 0; i < 6; i++){
26343                 buf.push(
26344                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26345                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26346                     i == 0 ?
26347                     '<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>' :
26348                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26349                 );
26350             }
26351             buf.push(
26352                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26353                     this.okText,
26354                     '</button><button type="button" class="x-date-mp-cancel">',
26355                     this.cancelText,
26356                     '</button></td></tr>',
26357                 '</table>'
26358             );
26359             this.monthPicker.update(buf.join(''));
26360             this.monthPicker.on('click', this.onMonthClick, this);
26361             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26362
26363             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26364             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26365
26366             this.mpMonths.each(function(m, a, i){
26367                 i += 1;
26368                 if((i%2) == 0){
26369                     m.dom.xmonth = 5 + Math.round(i * .5);
26370                 }else{
26371                     m.dom.xmonth = Math.round((i-1) * .5);
26372                 }
26373             });
26374         }
26375     },
26376
26377     showMonthPicker : function(){
26378         this.createMonthPicker();
26379         var size = this.el.getSize();
26380         this.monthPicker.setSize(size);
26381         this.monthPicker.child('table').setSize(size);
26382
26383         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26384         this.updateMPMonth(this.mpSelMonth);
26385         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26386         this.updateMPYear(this.mpSelYear);
26387
26388         this.monthPicker.slideIn('t', {duration:.2});
26389     },
26390
26391     updateMPYear : function(y){
26392         this.mpyear = y;
26393         var ys = this.mpYears.elements;
26394         for(var i = 1; i <= 10; i++){
26395             var td = ys[i-1], y2;
26396             if((i%2) == 0){
26397                 y2 = y + Math.round(i * .5);
26398                 td.firstChild.innerHTML = y2;
26399                 td.xyear = y2;
26400             }else{
26401                 y2 = y - (5-Math.round(i * .5));
26402                 td.firstChild.innerHTML = y2;
26403                 td.xyear = y2;
26404             }
26405             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26406         }
26407     },
26408
26409     updateMPMonth : function(sm){
26410         this.mpMonths.each(function(m, a, i){
26411             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26412         });
26413     },
26414
26415     selectMPMonth: function(m){
26416         
26417     },
26418
26419     onMonthClick : function(e, t){
26420         e.stopEvent();
26421         var el = new Roo.Element(t), pn;
26422         if(el.is('button.x-date-mp-cancel')){
26423             this.hideMonthPicker();
26424         }
26425         else if(el.is('button.x-date-mp-ok')){
26426             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26427             this.hideMonthPicker();
26428         }
26429         else if(pn = el.up('td.x-date-mp-month', 2)){
26430             this.mpMonths.removeClass('x-date-mp-sel');
26431             pn.addClass('x-date-mp-sel');
26432             this.mpSelMonth = pn.dom.xmonth;
26433         }
26434         else if(pn = el.up('td.x-date-mp-year', 2)){
26435             this.mpYears.removeClass('x-date-mp-sel');
26436             pn.addClass('x-date-mp-sel');
26437             this.mpSelYear = pn.dom.xyear;
26438         }
26439         else if(el.is('a.x-date-mp-prev')){
26440             this.updateMPYear(this.mpyear-10);
26441         }
26442         else if(el.is('a.x-date-mp-next')){
26443             this.updateMPYear(this.mpyear+10);
26444         }
26445     },
26446
26447     onMonthDblClick : function(e, t){
26448         e.stopEvent();
26449         var el = new Roo.Element(t), pn;
26450         if(pn = el.up('td.x-date-mp-month', 2)){
26451             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26452             this.hideMonthPicker();
26453         }
26454         else if(pn = el.up('td.x-date-mp-year', 2)){
26455             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26456             this.hideMonthPicker();
26457         }
26458     },
26459
26460     hideMonthPicker : function(disableAnim){
26461         if(this.monthPicker){
26462             if(disableAnim === true){
26463                 this.monthPicker.hide();
26464             }else{
26465                 this.monthPicker.slideOut('t', {duration:.2});
26466             }
26467         }
26468     },
26469
26470     // private
26471     showPrevMonth : function(e){
26472         this.update(this.activeDate.add("mo", -1));
26473     },
26474
26475     // private
26476     showNextMonth : function(e){
26477         this.update(this.activeDate.add("mo", 1));
26478     },
26479
26480     // private
26481     showPrevYear : function(){
26482         this.update(this.activeDate.add("y", -1));
26483     },
26484
26485     // private
26486     showNextYear : function(){
26487         this.update(this.activeDate.add("y", 1));
26488     },
26489
26490     // private
26491     handleMouseWheel : function(e){
26492         var delta = e.getWheelDelta();
26493         if(delta > 0){
26494             this.showPrevMonth();
26495             e.stopEvent();
26496         } else if(delta < 0){
26497             this.showNextMonth();
26498             e.stopEvent();
26499         }
26500     },
26501
26502     // private
26503     handleDateClick : function(e, t){
26504         e.stopEvent();
26505         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26506             this.setValue(new Date(t.dateValue));
26507             this.fireEvent("select", this, this.value);
26508         }
26509     },
26510
26511     // private
26512     selectToday : function(){
26513         this.setValue(new Date().clearTime());
26514         this.fireEvent("select", this, this.value);
26515     },
26516
26517     // private
26518     update : function(date)
26519     {
26520         var vd = this.activeDate;
26521         this.activeDate = date;
26522         if(vd && this.el){
26523             var t = date.getTime();
26524             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26525                 this.cells.removeClass("x-date-selected");
26526                 this.cells.each(function(c){
26527                    if(c.dom.firstChild.dateValue == t){
26528                        c.addClass("x-date-selected");
26529                        setTimeout(function(){
26530                             try{c.dom.firstChild.focus();}catch(e){}
26531                        }, 50);
26532                        return false;
26533                    }
26534                 });
26535                 return;
26536             }
26537         }
26538         
26539         var days = date.getDaysInMonth();
26540         var firstOfMonth = date.getFirstDateOfMonth();
26541         var startingPos = firstOfMonth.getDay()-this.startDay;
26542
26543         if(startingPos <= this.startDay){
26544             startingPos += 7;
26545         }
26546
26547         var pm = date.add("mo", -1);
26548         var prevStart = pm.getDaysInMonth()-startingPos;
26549
26550         var cells = this.cells.elements;
26551         var textEls = this.textNodes;
26552         days += startingPos;
26553
26554         // convert everything to numbers so it's fast
26555         var day = 86400000;
26556         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26557         var today = new Date().clearTime().getTime();
26558         var sel = date.clearTime().getTime();
26559         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26560         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26561         var ddMatch = this.disabledDatesRE;
26562         var ddText = this.disabledDatesText;
26563         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26564         var ddaysText = this.disabledDaysText;
26565         var format = this.format;
26566
26567         var setCellClass = function(cal, cell){
26568             cell.title = "";
26569             var t = d.getTime();
26570             cell.firstChild.dateValue = t;
26571             if(t == today){
26572                 cell.className += " x-date-today";
26573                 cell.title = cal.todayText;
26574             }
26575             if(t == sel){
26576                 cell.className += " x-date-selected";
26577                 setTimeout(function(){
26578                     try{cell.firstChild.focus();}catch(e){}
26579                 }, 50);
26580             }
26581             // disabling
26582             if(t < min) {
26583                 cell.className = " x-date-disabled";
26584                 cell.title = cal.minText;
26585                 return;
26586             }
26587             if(t > max) {
26588                 cell.className = " x-date-disabled";
26589                 cell.title = cal.maxText;
26590                 return;
26591             }
26592             if(ddays){
26593                 if(ddays.indexOf(d.getDay()) != -1){
26594                     cell.title = ddaysText;
26595                     cell.className = " x-date-disabled";
26596                 }
26597             }
26598             if(ddMatch && format){
26599                 var fvalue = d.dateFormat(format);
26600                 if(ddMatch.test(fvalue)){
26601                     cell.title = ddText.replace("%0", fvalue);
26602                     cell.className = " x-date-disabled";
26603                 }
26604             }
26605         };
26606
26607         var i = 0;
26608         for(; i < startingPos; i++) {
26609             textEls[i].innerHTML = (++prevStart);
26610             d.setDate(d.getDate()+1);
26611             cells[i].className = "x-date-prevday";
26612             setCellClass(this, cells[i]);
26613         }
26614         for(; i < days; i++){
26615             intDay = i - startingPos + 1;
26616             textEls[i].innerHTML = (intDay);
26617             d.setDate(d.getDate()+1);
26618             cells[i].className = "x-date-active";
26619             setCellClass(this, cells[i]);
26620         }
26621         var extraDays = 0;
26622         for(; i < 42; i++) {
26623              textEls[i].innerHTML = (++extraDays);
26624              d.setDate(d.getDate()+1);
26625              cells[i].className = "x-date-nextday";
26626              setCellClass(this, cells[i]);
26627         }
26628
26629         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26630         this.fireEvent('monthchange', this, date);
26631         
26632         if(!this.internalRender){
26633             var main = this.el.dom.firstChild;
26634             var w = main.offsetWidth;
26635             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26636             Roo.fly(main).setWidth(w);
26637             this.internalRender = true;
26638             // opera does not respect the auto grow header center column
26639             // then, after it gets a width opera refuses to recalculate
26640             // without a second pass
26641             if(Roo.isOpera && !this.secondPass){
26642                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26643                 this.secondPass = true;
26644                 this.update.defer(10, this, [date]);
26645             }
26646         }
26647         
26648         
26649     }
26650 });        /*
26651  * Based on:
26652  * Ext JS Library 1.1.1
26653  * Copyright(c) 2006-2007, Ext JS, LLC.
26654  *
26655  * Originally Released Under LGPL - original licence link has changed is not relivant.
26656  *
26657  * Fork - LGPL
26658  * <script type="text/javascript">
26659  */
26660 /**
26661  * @class Roo.TabPanel
26662  * @extends Roo.util.Observable
26663  * A lightweight tab container.
26664  * <br><br>
26665  * Usage:
26666  * <pre><code>
26667 // basic tabs 1, built from existing content
26668 var tabs = new Roo.TabPanel("tabs1");
26669 tabs.addTab("script", "View Script");
26670 tabs.addTab("markup", "View Markup");
26671 tabs.activate("script");
26672
26673 // more advanced tabs, built from javascript
26674 var jtabs = new Roo.TabPanel("jtabs");
26675 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26676
26677 // set up the UpdateManager
26678 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26679 var updater = tab2.getUpdateManager();
26680 updater.setDefaultUrl("ajax1.htm");
26681 tab2.on('activate', updater.refresh, updater, true);
26682
26683 // Use setUrl for Ajax loading
26684 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26685 tab3.setUrl("ajax2.htm", null, true);
26686
26687 // Disabled tab
26688 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26689 tab4.disable();
26690
26691 jtabs.activate("jtabs-1");
26692  * </code></pre>
26693  * @constructor
26694  * Create a new TabPanel.
26695  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26696  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26697  */
26698 Roo.TabPanel = function(container, config){
26699     /**
26700     * The container element for this TabPanel.
26701     * @type Roo.Element
26702     */
26703     this.el = Roo.get(container, true);
26704     if(config){
26705         if(typeof config == "boolean"){
26706             this.tabPosition = config ? "bottom" : "top";
26707         }else{
26708             Roo.apply(this, config);
26709         }
26710     }
26711     if(this.tabPosition == "bottom"){
26712         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26713         this.el.addClass("x-tabs-bottom");
26714     }
26715     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26716     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26717     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26718     if(Roo.isIE){
26719         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26720     }
26721     if(this.tabPosition != "bottom"){
26722         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26723          * @type Roo.Element
26724          */
26725         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26726         this.el.addClass("x-tabs-top");
26727     }
26728     this.items = [];
26729
26730     this.bodyEl.setStyle("position", "relative");
26731
26732     this.active = null;
26733     this.activateDelegate = this.activate.createDelegate(this);
26734
26735     this.addEvents({
26736         /**
26737          * @event tabchange
26738          * Fires when the active tab changes
26739          * @param {Roo.TabPanel} this
26740          * @param {Roo.TabPanelItem} activePanel The new active tab
26741          */
26742         "tabchange": true,
26743         /**
26744          * @event beforetabchange
26745          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26746          * @param {Roo.TabPanel} this
26747          * @param {Object} e Set cancel to true on this object to cancel the tab change
26748          * @param {Roo.TabPanelItem} tab The tab being changed to
26749          */
26750         "beforetabchange" : true
26751     });
26752
26753     Roo.EventManager.onWindowResize(this.onResize, this);
26754     this.cpad = this.el.getPadding("lr");
26755     this.hiddenCount = 0;
26756
26757
26758     // toolbar on the tabbar support...
26759     if (this.toolbar) {
26760         var tcfg = this.toolbar;
26761         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26762         this.toolbar = new Roo.Toolbar(tcfg);
26763         if (Roo.isSafari) {
26764             var tbl = tcfg.container.child('table', true);
26765             tbl.setAttribute('width', '100%');
26766         }
26767         
26768     }
26769    
26770
26771
26772     Roo.TabPanel.superclass.constructor.call(this);
26773 };
26774
26775 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26776     /*
26777      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26778      */
26779     tabPosition : "top",
26780     /*
26781      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26782      */
26783     currentTabWidth : 0,
26784     /*
26785      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26786      */
26787     minTabWidth : 40,
26788     /*
26789      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26790      */
26791     maxTabWidth : 250,
26792     /*
26793      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26794      */
26795     preferredTabWidth : 175,
26796     /*
26797      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26798      */
26799     resizeTabs : false,
26800     /*
26801      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26802      */
26803     monitorResize : true,
26804     /*
26805      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26806      */
26807     toolbar : false,
26808
26809     /**
26810      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26811      * @param {String} id The id of the div to use <b>or create</b>
26812      * @param {String} text The text for the tab
26813      * @param {String} content (optional) Content to put in the TabPanelItem body
26814      * @param {Boolean} closable (optional) True to create a close icon on the tab
26815      * @return {Roo.TabPanelItem} The created TabPanelItem
26816      */
26817     addTab : function(id, text, content, closable){
26818         var item = new Roo.TabPanelItem(this, id, text, closable);
26819         this.addTabItem(item);
26820         if(content){
26821             item.setContent(content);
26822         }
26823         return item;
26824     },
26825
26826     /**
26827      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26828      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26829      * @return {Roo.TabPanelItem}
26830      */
26831     getTab : function(id){
26832         return this.items[id];
26833     },
26834
26835     /**
26836      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26837      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26838      */
26839     hideTab : function(id){
26840         var t = this.items[id];
26841         if(!t.isHidden()){
26842            t.setHidden(true);
26843            this.hiddenCount++;
26844            this.autoSizeTabs();
26845         }
26846     },
26847
26848     /**
26849      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26850      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26851      */
26852     unhideTab : function(id){
26853         var t = this.items[id];
26854         if(t.isHidden()){
26855            t.setHidden(false);
26856            this.hiddenCount--;
26857            this.autoSizeTabs();
26858         }
26859     },
26860
26861     /**
26862      * Adds an existing {@link Roo.TabPanelItem}.
26863      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26864      */
26865     addTabItem : function(item){
26866         this.items[item.id] = item;
26867         this.items.push(item);
26868         if(this.resizeTabs){
26869            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26870            this.autoSizeTabs();
26871         }else{
26872             item.autoSize();
26873         }
26874     },
26875
26876     /**
26877      * Removes a {@link Roo.TabPanelItem}.
26878      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26879      */
26880     removeTab : function(id){
26881         var items = this.items;
26882         var tab = items[id];
26883         if(!tab) { return; }
26884         var index = items.indexOf(tab);
26885         if(this.active == tab && items.length > 1){
26886             var newTab = this.getNextAvailable(index);
26887             if(newTab) {
26888                 newTab.activate();
26889             }
26890         }
26891         this.stripEl.dom.removeChild(tab.pnode.dom);
26892         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26893             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26894         }
26895         items.splice(index, 1);
26896         delete this.items[tab.id];
26897         tab.fireEvent("close", tab);
26898         tab.purgeListeners();
26899         this.autoSizeTabs();
26900     },
26901
26902     getNextAvailable : function(start){
26903         var items = this.items;
26904         var index = start;
26905         // look for a next tab that will slide over to
26906         // replace the one being removed
26907         while(index < items.length){
26908             var item = items[++index];
26909             if(item && !item.isHidden()){
26910                 return item;
26911             }
26912         }
26913         // if one isn't found select the previous tab (on the left)
26914         index = start;
26915         while(index >= 0){
26916             var item = items[--index];
26917             if(item && !item.isHidden()){
26918                 return item;
26919             }
26920         }
26921         return null;
26922     },
26923
26924     /**
26925      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26926      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26927      */
26928     disableTab : function(id){
26929         var tab = this.items[id];
26930         if(tab && this.active != tab){
26931             tab.disable();
26932         }
26933     },
26934
26935     /**
26936      * Enables a {@link Roo.TabPanelItem} that is disabled.
26937      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26938      */
26939     enableTab : function(id){
26940         var tab = this.items[id];
26941         tab.enable();
26942     },
26943
26944     /**
26945      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26946      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26947      * @return {Roo.TabPanelItem} The TabPanelItem.
26948      */
26949     activate : function(id){
26950         var tab = this.items[id];
26951         if(!tab){
26952             return null;
26953         }
26954         if(tab == this.active || tab.disabled){
26955             return tab;
26956         }
26957         var e = {};
26958         this.fireEvent("beforetabchange", this, e, tab);
26959         if(e.cancel !== true && !tab.disabled){
26960             if(this.active){
26961                 this.active.hide();
26962             }
26963             this.active = this.items[id];
26964             this.active.show();
26965             this.fireEvent("tabchange", this, this.active);
26966         }
26967         return tab;
26968     },
26969
26970     /**
26971      * Gets the active {@link Roo.TabPanelItem}.
26972      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26973      */
26974     getActiveTab : function(){
26975         return this.active;
26976     },
26977
26978     /**
26979      * Updates the tab body element to fit the height of the container element
26980      * for overflow scrolling
26981      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26982      */
26983     syncHeight : function(targetHeight){
26984         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26985         var bm = this.bodyEl.getMargins();
26986         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26987         this.bodyEl.setHeight(newHeight);
26988         return newHeight;
26989     },
26990
26991     onResize : function(){
26992         if(this.monitorResize){
26993             this.autoSizeTabs();
26994         }
26995     },
26996
26997     /**
26998      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26999      */
27000     beginUpdate : function(){
27001         this.updating = true;
27002     },
27003
27004     /**
27005      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
27006      */
27007     endUpdate : function(){
27008         this.updating = false;
27009         this.autoSizeTabs();
27010     },
27011
27012     /**
27013      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
27014      */
27015     autoSizeTabs : function(){
27016         var count = this.items.length;
27017         var vcount = count - this.hiddenCount;
27018         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
27019         var w = Math.max(this.el.getWidth() - this.cpad, 10);
27020         var availWidth = Math.floor(w / vcount);
27021         var b = this.stripBody;
27022         if(b.getWidth() > w){
27023             var tabs = this.items;
27024             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
27025             if(availWidth < this.minTabWidth){
27026                 /*if(!this.sleft){    // incomplete scrolling code
27027                     this.createScrollButtons();
27028                 }
27029                 this.showScroll();
27030                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27031             }
27032         }else{
27033             if(this.currentTabWidth < this.preferredTabWidth){
27034                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27035             }
27036         }
27037     },
27038
27039     /**
27040      * Returns the number of tabs in this TabPanel.
27041      * @return {Number}
27042      */
27043      getCount : function(){
27044          return this.items.length;
27045      },
27046
27047     /**
27048      * Resizes all the tabs to the passed width
27049      * @param {Number} The new width
27050      */
27051     setTabWidth : function(width){
27052         this.currentTabWidth = width;
27053         for(var i = 0, len = this.items.length; i < len; i++) {
27054                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27055         }
27056     },
27057
27058     /**
27059      * Destroys this TabPanel
27060      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27061      */
27062     destroy : function(removeEl){
27063         Roo.EventManager.removeResizeListener(this.onResize, this);
27064         for(var i = 0, len = this.items.length; i < len; i++){
27065             this.items[i].purgeListeners();
27066         }
27067         if(removeEl === true){
27068             this.el.update("");
27069             this.el.remove();
27070         }
27071     }
27072 });
27073
27074 /**
27075  * @class Roo.TabPanelItem
27076  * @extends Roo.util.Observable
27077  * Represents an individual item (tab plus body) in a TabPanel.
27078  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27079  * @param {String} id The id of this TabPanelItem
27080  * @param {String} text The text for the tab of this TabPanelItem
27081  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27082  */
27083 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27084     /**
27085      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27086      * @type Roo.TabPanel
27087      */
27088     this.tabPanel = tabPanel;
27089     /**
27090      * The id for this TabPanelItem
27091      * @type String
27092      */
27093     this.id = id;
27094     /** @private */
27095     this.disabled = false;
27096     /** @private */
27097     this.text = text;
27098     /** @private */
27099     this.loaded = false;
27100     this.closable = closable;
27101
27102     /**
27103      * The body element for this TabPanelItem.
27104      * @type Roo.Element
27105      */
27106     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27107     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27108     this.bodyEl.setStyle("display", "block");
27109     this.bodyEl.setStyle("zoom", "1");
27110     this.hideAction();
27111
27112     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27113     /** @private */
27114     this.el = Roo.get(els.el, true);
27115     this.inner = Roo.get(els.inner, true);
27116     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27117     this.pnode = Roo.get(els.el.parentNode, true);
27118     this.el.on("mousedown", this.onTabMouseDown, this);
27119     this.el.on("click", this.onTabClick, this);
27120     /** @private */
27121     if(closable){
27122         var c = Roo.get(els.close, true);
27123         c.dom.title = this.closeText;
27124         c.addClassOnOver("close-over");
27125         c.on("click", this.closeClick, this);
27126      }
27127
27128     this.addEvents({
27129          /**
27130          * @event activate
27131          * Fires when this tab becomes the active tab.
27132          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27133          * @param {Roo.TabPanelItem} this
27134          */
27135         "activate": true,
27136         /**
27137          * @event beforeclose
27138          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27139          * @param {Roo.TabPanelItem} this
27140          * @param {Object} e Set cancel to true on this object to cancel the close.
27141          */
27142         "beforeclose": true,
27143         /**
27144          * @event close
27145          * Fires when this tab is closed.
27146          * @param {Roo.TabPanelItem} this
27147          */
27148          "close": true,
27149         /**
27150          * @event deactivate
27151          * Fires when this tab is no longer the active tab.
27152          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27153          * @param {Roo.TabPanelItem} this
27154          */
27155          "deactivate" : true
27156     });
27157     this.hidden = false;
27158
27159     Roo.TabPanelItem.superclass.constructor.call(this);
27160 };
27161
27162 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27163     purgeListeners : function(){
27164        Roo.util.Observable.prototype.purgeListeners.call(this);
27165        this.el.removeAllListeners();
27166     },
27167     /**
27168      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27169      */
27170     show : function(){
27171         this.pnode.addClass("on");
27172         this.showAction();
27173         if(Roo.isOpera){
27174             this.tabPanel.stripWrap.repaint();
27175         }
27176         this.fireEvent("activate", this.tabPanel, this);
27177     },
27178
27179     /**
27180      * Returns true if this tab is the active tab.
27181      * @return {Boolean}
27182      */
27183     isActive : function(){
27184         return this.tabPanel.getActiveTab() == this;
27185     },
27186
27187     /**
27188      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27189      */
27190     hide : function(){
27191         this.pnode.removeClass("on");
27192         this.hideAction();
27193         this.fireEvent("deactivate", this.tabPanel, this);
27194     },
27195
27196     hideAction : function(){
27197         this.bodyEl.hide();
27198         this.bodyEl.setStyle("position", "absolute");
27199         this.bodyEl.setLeft("-20000px");
27200         this.bodyEl.setTop("-20000px");
27201     },
27202
27203     showAction : function(){
27204         this.bodyEl.setStyle("position", "relative");
27205         this.bodyEl.setTop("");
27206         this.bodyEl.setLeft("");
27207         this.bodyEl.show();
27208     },
27209
27210     /**
27211      * Set the tooltip for the tab.
27212      * @param {String} tooltip The tab's tooltip
27213      */
27214     setTooltip : function(text){
27215         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27216             this.textEl.dom.qtip = text;
27217             this.textEl.dom.removeAttribute('title');
27218         }else{
27219             this.textEl.dom.title = text;
27220         }
27221     },
27222
27223     onTabClick : function(e){
27224         e.preventDefault();
27225         this.tabPanel.activate(this.id);
27226     },
27227
27228     onTabMouseDown : function(e){
27229         e.preventDefault();
27230         this.tabPanel.activate(this.id);
27231     },
27232
27233     getWidth : function(){
27234         return this.inner.getWidth();
27235     },
27236
27237     setWidth : function(width){
27238         var iwidth = width - this.pnode.getPadding("lr");
27239         this.inner.setWidth(iwidth);
27240         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27241         this.pnode.setWidth(width);
27242     },
27243
27244     /**
27245      * Show or hide the tab
27246      * @param {Boolean} hidden True to hide or false to show.
27247      */
27248     setHidden : function(hidden){
27249         this.hidden = hidden;
27250         this.pnode.setStyle("display", hidden ? "none" : "");
27251     },
27252
27253     /**
27254      * Returns true if this tab is "hidden"
27255      * @return {Boolean}
27256      */
27257     isHidden : function(){
27258         return this.hidden;
27259     },
27260
27261     /**
27262      * Returns the text for this tab
27263      * @return {String}
27264      */
27265     getText : function(){
27266         return this.text;
27267     },
27268
27269     autoSize : function(){
27270         //this.el.beginMeasure();
27271         this.textEl.setWidth(1);
27272         /*
27273          *  #2804 [new] Tabs in Roojs
27274          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27275          */
27276         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27277         //this.el.endMeasure();
27278     },
27279
27280     /**
27281      * Sets the text for the tab (Note: this also sets the tooltip text)
27282      * @param {String} text The tab's text and tooltip
27283      */
27284     setText : function(text){
27285         this.text = text;
27286         this.textEl.update(text);
27287         this.setTooltip(text);
27288         if(!this.tabPanel.resizeTabs){
27289             this.autoSize();
27290         }
27291     },
27292     /**
27293      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27294      */
27295     activate : function(){
27296         this.tabPanel.activate(this.id);
27297     },
27298
27299     /**
27300      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27301      */
27302     disable : function(){
27303         if(this.tabPanel.active != this){
27304             this.disabled = true;
27305             this.pnode.addClass("disabled");
27306         }
27307     },
27308
27309     /**
27310      * Enables this TabPanelItem if it was previously disabled.
27311      */
27312     enable : function(){
27313         this.disabled = false;
27314         this.pnode.removeClass("disabled");
27315     },
27316
27317     /**
27318      * Sets the content for this TabPanelItem.
27319      * @param {String} content The content
27320      * @param {Boolean} loadScripts true to look for and load scripts
27321      */
27322     setContent : function(content, loadScripts){
27323         this.bodyEl.update(content, loadScripts);
27324     },
27325
27326     /**
27327      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27328      * @return {Roo.UpdateManager} The UpdateManager
27329      */
27330     getUpdateManager : function(){
27331         return this.bodyEl.getUpdateManager();
27332     },
27333
27334     /**
27335      * Set a URL to be used to load the content for this TabPanelItem.
27336      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27337      * @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)
27338      * @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)
27339      * @return {Roo.UpdateManager} The UpdateManager
27340      */
27341     setUrl : function(url, params, loadOnce){
27342         if(this.refreshDelegate){
27343             this.un('activate', this.refreshDelegate);
27344         }
27345         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27346         this.on("activate", this.refreshDelegate);
27347         return this.bodyEl.getUpdateManager();
27348     },
27349
27350     /** @private */
27351     _handleRefresh : function(url, params, loadOnce){
27352         if(!loadOnce || !this.loaded){
27353             var updater = this.bodyEl.getUpdateManager();
27354             updater.update(url, params, this._setLoaded.createDelegate(this));
27355         }
27356     },
27357
27358     /**
27359      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27360      *   Will fail silently if the setUrl method has not been called.
27361      *   This does not activate the panel, just updates its content.
27362      */
27363     refresh : function(){
27364         if(this.refreshDelegate){
27365            this.loaded = false;
27366            this.refreshDelegate();
27367         }
27368     },
27369
27370     /** @private */
27371     _setLoaded : function(){
27372         this.loaded = true;
27373     },
27374
27375     /** @private */
27376     closeClick : function(e){
27377         var o = {};
27378         e.stopEvent();
27379         this.fireEvent("beforeclose", this, o);
27380         if(o.cancel !== true){
27381             this.tabPanel.removeTab(this.id);
27382         }
27383     },
27384     /**
27385      * The text displayed in the tooltip for the close icon.
27386      * @type String
27387      */
27388     closeText : "Close this tab"
27389 });
27390
27391 /** @private */
27392 Roo.TabPanel.prototype.createStrip = function(container){
27393     var strip = document.createElement("div");
27394     strip.className = "x-tabs-wrap";
27395     container.appendChild(strip);
27396     return strip;
27397 };
27398 /** @private */
27399 Roo.TabPanel.prototype.createStripList = function(strip){
27400     // div wrapper for retard IE
27401     // returns the "tr" element.
27402     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27403         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27404         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27405     return strip.firstChild.firstChild.firstChild.firstChild;
27406 };
27407 /** @private */
27408 Roo.TabPanel.prototype.createBody = function(container){
27409     var body = document.createElement("div");
27410     Roo.id(body, "tab-body");
27411     Roo.fly(body).addClass("x-tabs-body");
27412     container.appendChild(body);
27413     return body;
27414 };
27415 /** @private */
27416 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27417     var body = Roo.getDom(id);
27418     if(!body){
27419         body = document.createElement("div");
27420         body.id = id;
27421     }
27422     Roo.fly(body).addClass("x-tabs-item-body");
27423     bodyEl.insertBefore(body, bodyEl.firstChild);
27424     return body;
27425 };
27426 /** @private */
27427 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27428     var td = document.createElement("td");
27429     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27430     //stripEl.appendChild(td);
27431     if(closable){
27432         td.className = "x-tabs-closable";
27433         if(!this.closeTpl){
27434             this.closeTpl = new Roo.Template(
27435                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27436                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27437                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27438             );
27439         }
27440         var el = this.closeTpl.overwrite(td, {"text": text});
27441         var close = el.getElementsByTagName("div")[0];
27442         var inner = el.getElementsByTagName("em")[0];
27443         return {"el": el, "close": close, "inner": inner};
27444     } else {
27445         if(!this.tabTpl){
27446             this.tabTpl = new Roo.Template(
27447                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27448                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27449             );
27450         }
27451         var el = this.tabTpl.overwrite(td, {"text": text});
27452         var inner = el.getElementsByTagName("em")[0];
27453         return {"el": el, "inner": inner};
27454     }
27455 };/*
27456  * Based on:
27457  * Ext JS Library 1.1.1
27458  * Copyright(c) 2006-2007, Ext JS, LLC.
27459  *
27460  * Originally Released Under LGPL - original licence link has changed is not relivant.
27461  *
27462  * Fork - LGPL
27463  * <script type="text/javascript">
27464  */
27465
27466 /**
27467  * @class Roo.Button
27468  * @extends Roo.util.Observable
27469  * Simple Button class
27470  * @cfg {String} text The button text
27471  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27472  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27473  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27474  * @cfg {Object} scope The scope of the handler
27475  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27476  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27477  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27478  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27479  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27480  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27481    applies if enableToggle = true)
27482  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27483  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27484   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27485  * @constructor
27486  * Create a new button
27487  * @param {Object} config The config object
27488  */
27489 Roo.Button = function(renderTo, config)
27490 {
27491     if (!config) {
27492         config = renderTo;
27493         renderTo = config.renderTo || false;
27494     }
27495     
27496     Roo.apply(this, config);
27497     this.addEvents({
27498         /**
27499              * @event click
27500              * Fires when this button is clicked
27501              * @param {Button} this
27502              * @param {EventObject} e The click event
27503              */
27504             "click" : true,
27505         /**
27506              * @event toggle
27507              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27508              * @param {Button} this
27509              * @param {Boolean} pressed
27510              */
27511             "toggle" : true,
27512         /**
27513              * @event mouseover
27514              * Fires when the mouse hovers over the button
27515              * @param {Button} this
27516              * @param {Event} e The event object
27517              */
27518         'mouseover' : true,
27519         /**
27520              * @event mouseout
27521              * Fires when the mouse exits the button
27522              * @param {Button} this
27523              * @param {Event} e The event object
27524              */
27525         'mouseout': true,
27526          /**
27527              * @event render
27528              * Fires when the button is rendered
27529              * @param {Button} this
27530              */
27531         'render': true
27532     });
27533     if(this.menu){
27534         this.menu = Roo.menu.MenuMgr.get(this.menu);
27535     }
27536     // register listeners first!!  - so render can be captured..
27537     Roo.util.Observable.call(this);
27538     if(renderTo){
27539         this.render(renderTo);
27540     }
27541     
27542   
27543 };
27544
27545 Roo.extend(Roo.Button, Roo.util.Observable, {
27546     /**
27547      * 
27548      */
27549     
27550     /**
27551      * Read-only. True if this button is hidden
27552      * @type Boolean
27553      */
27554     hidden : false,
27555     /**
27556      * Read-only. True if this button is disabled
27557      * @type Boolean
27558      */
27559     disabled : false,
27560     /**
27561      * Read-only. True if this button is pressed (only if enableToggle = true)
27562      * @type Boolean
27563      */
27564     pressed : false,
27565
27566     /**
27567      * @cfg {Number} tabIndex 
27568      * The DOM tabIndex for this button (defaults to undefined)
27569      */
27570     tabIndex : undefined,
27571
27572     /**
27573      * @cfg {Boolean} enableToggle
27574      * True to enable pressed/not pressed toggling (defaults to false)
27575      */
27576     enableToggle: false,
27577     /**
27578      * @cfg {Mixed} menu
27579      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27580      */
27581     menu : undefined,
27582     /**
27583      * @cfg {String} menuAlign
27584      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27585      */
27586     menuAlign : "tl-bl?",
27587
27588     /**
27589      * @cfg {String} iconCls
27590      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27591      */
27592     iconCls : undefined,
27593     /**
27594      * @cfg {String} type
27595      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27596      */
27597     type : 'button',
27598
27599     // private
27600     menuClassTarget: 'tr',
27601
27602     /**
27603      * @cfg {String} clickEvent
27604      * The type of event to map to the button's event handler (defaults to 'click')
27605      */
27606     clickEvent : 'click',
27607
27608     /**
27609      * @cfg {Boolean} handleMouseEvents
27610      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27611      */
27612     handleMouseEvents : true,
27613
27614     /**
27615      * @cfg {String} tooltipType
27616      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27617      */
27618     tooltipType : 'qtip',
27619
27620     /**
27621      * @cfg {String} cls
27622      * A CSS class to apply to the button's main element.
27623      */
27624     
27625     /**
27626      * @cfg {Roo.Template} template (Optional)
27627      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27628      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27629      * require code modifications if required elements (e.g. a button) aren't present.
27630      */
27631
27632     // private
27633     render : function(renderTo){
27634         var btn;
27635         if(this.hideParent){
27636             this.parentEl = Roo.get(renderTo);
27637         }
27638         if(!this.dhconfig){
27639             if(!this.template){
27640                 if(!Roo.Button.buttonTemplate){
27641                     // hideous table template
27642                     Roo.Button.buttonTemplate = new Roo.Template(
27643                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27644                         '<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>',
27645                         "</tr></tbody></table>");
27646                 }
27647                 this.template = Roo.Button.buttonTemplate;
27648             }
27649             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27650             var btnEl = btn.child("button:first");
27651             btnEl.on('focus', this.onFocus, this);
27652             btnEl.on('blur', this.onBlur, this);
27653             if(this.cls){
27654                 btn.addClass(this.cls);
27655             }
27656             if(this.icon){
27657                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27658             }
27659             if(this.iconCls){
27660                 btnEl.addClass(this.iconCls);
27661                 if(!this.cls){
27662                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27663                 }
27664             }
27665             if(this.tabIndex !== undefined){
27666                 btnEl.dom.tabIndex = this.tabIndex;
27667             }
27668             if(this.tooltip){
27669                 if(typeof this.tooltip == 'object'){
27670                     Roo.QuickTips.tips(Roo.apply({
27671                           target: btnEl.id
27672                     }, this.tooltip));
27673                 } else {
27674                     btnEl.dom[this.tooltipType] = this.tooltip;
27675                 }
27676             }
27677         }else{
27678             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27679         }
27680         this.el = btn;
27681         if(this.id){
27682             this.el.dom.id = this.el.id = this.id;
27683         }
27684         if(this.menu){
27685             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27686             this.menu.on("show", this.onMenuShow, this);
27687             this.menu.on("hide", this.onMenuHide, this);
27688         }
27689         btn.addClass("x-btn");
27690         if(Roo.isIE && !Roo.isIE7){
27691             this.autoWidth.defer(1, this);
27692         }else{
27693             this.autoWidth();
27694         }
27695         if(this.handleMouseEvents){
27696             btn.on("mouseover", this.onMouseOver, this);
27697             btn.on("mouseout", this.onMouseOut, this);
27698             btn.on("mousedown", this.onMouseDown, this);
27699         }
27700         btn.on(this.clickEvent, this.onClick, this);
27701         //btn.on("mouseup", this.onMouseUp, this);
27702         if(this.hidden){
27703             this.hide();
27704         }
27705         if(this.disabled){
27706             this.disable();
27707         }
27708         Roo.ButtonToggleMgr.register(this);
27709         if(this.pressed){
27710             this.el.addClass("x-btn-pressed");
27711         }
27712         if(this.repeat){
27713             var repeater = new Roo.util.ClickRepeater(btn,
27714                 typeof this.repeat == "object" ? this.repeat : {}
27715             );
27716             repeater.on("click", this.onClick,  this);
27717         }
27718         
27719         this.fireEvent('render', this);
27720         
27721     },
27722     /**
27723      * Returns the button's underlying element
27724      * @return {Roo.Element} The element
27725      */
27726     getEl : function(){
27727         return this.el;  
27728     },
27729     
27730     /**
27731      * Destroys this Button and removes any listeners.
27732      */
27733     destroy : function(){
27734         Roo.ButtonToggleMgr.unregister(this);
27735         this.el.removeAllListeners();
27736         this.purgeListeners();
27737         this.el.remove();
27738     },
27739
27740     // private
27741     autoWidth : function(){
27742         if(this.el){
27743             this.el.setWidth("auto");
27744             if(Roo.isIE7 && Roo.isStrict){
27745                 var ib = this.el.child('button');
27746                 if(ib && ib.getWidth() > 20){
27747                     ib.clip();
27748                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27749                 }
27750             }
27751             if(this.minWidth){
27752                 if(this.hidden){
27753                     this.el.beginMeasure();
27754                 }
27755                 if(this.el.getWidth() < this.minWidth){
27756                     this.el.setWidth(this.minWidth);
27757                 }
27758                 if(this.hidden){
27759                     this.el.endMeasure();
27760                 }
27761             }
27762         }
27763     },
27764
27765     /**
27766      * Assigns this button's click handler
27767      * @param {Function} handler The function to call when the button is clicked
27768      * @param {Object} scope (optional) Scope for the function passed in
27769      */
27770     setHandler : function(handler, scope){
27771         this.handler = handler;
27772         this.scope = scope;  
27773     },
27774     
27775     /**
27776      * Sets this button's text
27777      * @param {String} text The button text
27778      */
27779     setText : function(text){
27780         this.text = text;
27781         if(this.el){
27782             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27783         }
27784         this.autoWidth();
27785     },
27786     
27787     /**
27788      * Gets the text for this button
27789      * @return {String} The button text
27790      */
27791     getText : function(){
27792         return this.text;  
27793     },
27794     
27795     /**
27796      * Show this button
27797      */
27798     show: function(){
27799         this.hidden = false;
27800         if(this.el){
27801             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27802         }
27803     },
27804     
27805     /**
27806      * Hide this button
27807      */
27808     hide: function(){
27809         this.hidden = true;
27810         if(this.el){
27811             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27812         }
27813     },
27814     
27815     /**
27816      * Convenience function for boolean show/hide
27817      * @param {Boolean} visible True to show, false to hide
27818      */
27819     setVisible: function(visible){
27820         if(visible) {
27821             this.show();
27822         }else{
27823             this.hide();
27824         }
27825     },
27826     
27827     /**
27828      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27829      * @param {Boolean} state (optional) Force a particular state
27830      */
27831     toggle : function(state){
27832         state = state === undefined ? !this.pressed : state;
27833         if(state != this.pressed){
27834             if(state){
27835                 this.el.addClass("x-btn-pressed");
27836                 this.pressed = true;
27837                 this.fireEvent("toggle", this, true);
27838             }else{
27839                 this.el.removeClass("x-btn-pressed");
27840                 this.pressed = false;
27841                 this.fireEvent("toggle", this, false);
27842             }
27843             if(this.toggleHandler){
27844                 this.toggleHandler.call(this.scope || this, this, state);
27845             }
27846         }
27847     },
27848     
27849     /**
27850      * Focus the button
27851      */
27852     focus : function(){
27853         this.el.child('button:first').focus();
27854     },
27855     
27856     /**
27857      * Disable this button
27858      */
27859     disable : function(){
27860         if(this.el){
27861             this.el.addClass("x-btn-disabled");
27862         }
27863         this.disabled = true;
27864     },
27865     
27866     /**
27867      * Enable this button
27868      */
27869     enable : function(){
27870         if(this.el){
27871             this.el.removeClass("x-btn-disabled");
27872         }
27873         this.disabled = false;
27874     },
27875
27876     /**
27877      * Convenience function for boolean enable/disable
27878      * @param {Boolean} enabled True to enable, false to disable
27879      */
27880     setDisabled : function(v){
27881         this[v !== true ? "enable" : "disable"]();
27882     },
27883
27884     // private
27885     onClick : function(e)
27886     {
27887         if(e){
27888             e.preventDefault();
27889         }
27890         if(e.button != 0){
27891             return;
27892         }
27893         if(!this.disabled){
27894             if(this.enableToggle){
27895                 this.toggle();
27896             }
27897             if(this.menu && !this.menu.isVisible()){
27898                 this.menu.show(this.el, this.menuAlign);
27899             }
27900             this.fireEvent("click", this, e);
27901             if(this.handler){
27902                 this.el.removeClass("x-btn-over");
27903                 this.handler.call(this.scope || this, this, e);
27904             }
27905         }
27906     },
27907     // private
27908     onMouseOver : function(e){
27909         if(!this.disabled){
27910             this.el.addClass("x-btn-over");
27911             this.fireEvent('mouseover', this, e);
27912         }
27913     },
27914     // private
27915     onMouseOut : function(e){
27916         if(!e.within(this.el,  true)){
27917             this.el.removeClass("x-btn-over");
27918             this.fireEvent('mouseout', this, e);
27919         }
27920     },
27921     // private
27922     onFocus : function(e){
27923         if(!this.disabled){
27924             this.el.addClass("x-btn-focus");
27925         }
27926     },
27927     // private
27928     onBlur : function(e){
27929         this.el.removeClass("x-btn-focus");
27930     },
27931     // private
27932     onMouseDown : function(e){
27933         if(!this.disabled && e.button == 0){
27934             this.el.addClass("x-btn-click");
27935             Roo.get(document).on('mouseup', this.onMouseUp, this);
27936         }
27937     },
27938     // private
27939     onMouseUp : function(e){
27940         if(e.button == 0){
27941             this.el.removeClass("x-btn-click");
27942             Roo.get(document).un('mouseup', this.onMouseUp, this);
27943         }
27944     },
27945     // private
27946     onMenuShow : function(e){
27947         this.el.addClass("x-btn-menu-active");
27948     },
27949     // private
27950     onMenuHide : function(e){
27951         this.el.removeClass("x-btn-menu-active");
27952     }   
27953 });
27954
27955 // Private utility class used by Button
27956 Roo.ButtonToggleMgr = function(){
27957    var groups = {};
27958    
27959    function toggleGroup(btn, state){
27960        if(state){
27961            var g = groups[btn.toggleGroup];
27962            for(var i = 0, l = g.length; i < l; i++){
27963                if(g[i] != btn){
27964                    g[i].toggle(false);
27965                }
27966            }
27967        }
27968    }
27969    
27970    return {
27971        register : function(btn){
27972            if(!btn.toggleGroup){
27973                return;
27974            }
27975            var g = groups[btn.toggleGroup];
27976            if(!g){
27977                g = groups[btn.toggleGroup] = [];
27978            }
27979            g.push(btn);
27980            btn.on("toggle", toggleGroup);
27981        },
27982        
27983        unregister : function(btn){
27984            if(!btn.toggleGroup){
27985                return;
27986            }
27987            var g = groups[btn.toggleGroup];
27988            if(g){
27989                g.remove(btn);
27990                btn.un("toggle", toggleGroup);
27991            }
27992        }
27993    };
27994 }();/*
27995  * Based on:
27996  * Ext JS Library 1.1.1
27997  * Copyright(c) 2006-2007, Ext JS, LLC.
27998  *
27999  * Originally Released Under LGPL - original licence link has changed is not relivant.
28000  *
28001  * Fork - LGPL
28002  * <script type="text/javascript">
28003  */
28004  
28005 /**
28006  * @class Roo.SplitButton
28007  * @extends Roo.Button
28008  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
28009  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
28010  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
28011  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
28012  * @cfg {String} arrowTooltip The title attribute of the arrow
28013  * @constructor
28014  * Create a new menu button
28015  * @param {String/HTMLElement/Element} renderTo The element to append the button to
28016  * @param {Object} config The config object
28017  */
28018 Roo.SplitButton = function(renderTo, config){
28019     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
28020     /**
28021      * @event arrowclick
28022      * Fires when this button's arrow is clicked
28023      * @param {SplitButton} this
28024      * @param {EventObject} e The click event
28025      */
28026     this.addEvents({"arrowclick":true});
28027 };
28028
28029 Roo.extend(Roo.SplitButton, Roo.Button, {
28030     render : function(renderTo){
28031         // this is one sweet looking template!
28032         var tpl = new Roo.Template(
28033             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28034             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28035             '<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>',
28036             "</tbody></table></td><td>",
28037             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28038             '<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>',
28039             "</tbody></table></td></tr></table>"
28040         );
28041         var btn = tpl.append(renderTo, [this.text, this.type], true);
28042         var btnEl = btn.child("button");
28043         if(this.cls){
28044             btn.addClass(this.cls);
28045         }
28046         if(this.icon){
28047             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28048         }
28049         if(this.iconCls){
28050             btnEl.addClass(this.iconCls);
28051             if(!this.cls){
28052                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28053             }
28054         }
28055         this.el = btn;
28056         if(this.handleMouseEvents){
28057             btn.on("mouseover", this.onMouseOver, this);
28058             btn.on("mouseout", this.onMouseOut, this);
28059             btn.on("mousedown", this.onMouseDown, this);
28060             btn.on("mouseup", this.onMouseUp, this);
28061         }
28062         btn.on(this.clickEvent, this.onClick, this);
28063         if(this.tooltip){
28064             if(typeof this.tooltip == 'object'){
28065                 Roo.QuickTips.tips(Roo.apply({
28066                       target: btnEl.id
28067                 }, this.tooltip));
28068             } else {
28069                 btnEl.dom[this.tooltipType] = this.tooltip;
28070             }
28071         }
28072         if(this.arrowTooltip){
28073             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28074         }
28075         if(this.hidden){
28076             this.hide();
28077         }
28078         if(this.disabled){
28079             this.disable();
28080         }
28081         if(this.pressed){
28082             this.el.addClass("x-btn-pressed");
28083         }
28084         if(Roo.isIE && !Roo.isIE7){
28085             this.autoWidth.defer(1, this);
28086         }else{
28087             this.autoWidth();
28088         }
28089         if(this.menu){
28090             this.menu.on("show", this.onMenuShow, this);
28091             this.menu.on("hide", this.onMenuHide, this);
28092         }
28093         this.fireEvent('render', this);
28094     },
28095
28096     // private
28097     autoWidth : function(){
28098         if(this.el){
28099             var tbl = this.el.child("table:first");
28100             var tbl2 = this.el.child("table:last");
28101             this.el.setWidth("auto");
28102             tbl.setWidth("auto");
28103             if(Roo.isIE7 && Roo.isStrict){
28104                 var ib = this.el.child('button:first');
28105                 if(ib && ib.getWidth() > 20){
28106                     ib.clip();
28107                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28108                 }
28109             }
28110             if(this.minWidth){
28111                 if(this.hidden){
28112                     this.el.beginMeasure();
28113                 }
28114                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28115                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28116                 }
28117                 if(this.hidden){
28118                     this.el.endMeasure();
28119                 }
28120             }
28121             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28122         } 
28123     },
28124     /**
28125      * Sets this button's click handler
28126      * @param {Function} handler The function to call when the button is clicked
28127      * @param {Object} scope (optional) Scope for the function passed above
28128      */
28129     setHandler : function(handler, scope){
28130         this.handler = handler;
28131         this.scope = scope;  
28132     },
28133     
28134     /**
28135      * Sets this button's arrow click handler
28136      * @param {Function} handler The function to call when the arrow is clicked
28137      * @param {Object} scope (optional) Scope for the function passed above
28138      */
28139     setArrowHandler : function(handler, scope){
28140         this.arrowHandler = handler;
28141         this.scope = scope;  
28142     },
28143     
28144     /**
28145      * Focus the button
28146      */
28147     focus : function(){
28148         if(this.el){
28149             this.el.child("button:first").focus();
28150         }
28151     },
28152
28153     // private
28154     onClick : function(e){
28155         e.preventDefault();
28156         if(!this.disabled){
28157             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28158                 if(this.menu && !this.menu.isVisible()){
28159                     this.menu.show(this.el, this.menuAlign);
28160                 }
28161                 this.fireEvent("arrowclick", this, e);
28162                 if(this.arrowHandler){
28163                     this.arrowHandler.call(this.scope || this, this, e);
28164                 }
28165             }else{
28166                 this.fireEvent("click", this, e);
28167                 if(this.handler){
28168                     this.handler.call(this.scope || this, this, e);
28169                 }
28170             }
28171         }
28172     },
28173     // private
28174     onMouseDown : function(e){
28175         if(!this.disabled){
28176             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28177         }
28178     },
28179     // private
28180     onMouseUp : function(e){
28181         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28182     }   
28183 });
28184
28185
28186 // backwards compat
28187 Roo.MenuButton = Roo.SplitButton;/*
28188  * Based on:
28189  * Ext JS Library 1.1.1
28190  * Copyright(c) 2006-2007, Ext JS, LLC.
28191  *
28192  * Originally Released Under LGPL - original licence link has changed is not relivant.
28193  *
28194  * Fork - LGPL
28195  * <script type="text/javascript">
28196  */
28197
28198 /**
28199  * @class Roo.Toolbar
28200  * Basic Toolbar class.
28201  * @constructor
28202  * Creates a new Toolbar
28203  * @param {Object} container The config object
28204  */ 
28205 Roo.Toolbar = function(container, buttons, config)
28206 {
28207     /// old consturctor format still supported..
28208     if(container instanceof Array){ // omit the container for later rendering
28209         buttons = container;
28210         config = buttons;
28211         container = null;
28212     }
28213     if (typeof(container) == 'object' && container.xtype) {
28214         config = container;
28215         container = config.container;
28216         buttons = config.buttons || []; // not really - use items!!
28217     }
28218     var xitems = [];
28219     if (config && config.items) {
28220         xitems = config.items;
28221         delete config.items;
28222     }
28223     Roo.apply(this, config);
28224     this.buttons = buttons;
28225     
28226     if(container){
28227         this.render(container);
28228     }
28229     this.xitems = xitems;
28230     Roo.each(xitems, function(b) {
28231         this.add(b);
28232     }, this);
28233     
28234 };
28235
28236 Roo.Toolbar.prototype = {
28237     /**
28238      * @cfg {Array} items
28239      * array of button configs or elements to add (will be converted to a MixedCollection)
28240      */
28241     
28242     /**
28243      * @cfg {String/HTMLElement/Element} container
28244      * The id or element that will contain the toolbar
28245      */
28246     // private
28247     render : function(ct){
28248         this.el = Roo.get(ct);
28249         if(this.cls){
28250             this.el.addClass(this.cls);
28251         }
28252         // using a table allows for vertical alignment
28253         // 100% width is needed by Safari...
28254         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28255         this.tr = this.el.child("tr", true);
28256         var autoId = 0;
28257         this.items = new Roo.util.MixedCollection(false, function(o){
28258             return o.id || ("item" + (++autoId));
28259         });
28260         if(this.buttons){
28261             this.add.apply(this, this.buttons);
28262             delete this.buttons;
28263         }
28264     },
28265
28266     /**
28267      * Adds element(s) to the toolbar -- this function takes a variable number of 
28268      * arguments of mixed type and adds them to the toolbar.
28269      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28270      * <ul>
28271      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28272      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28273      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28274      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28275      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28276      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28277      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28278      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28279      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28280      * </ul>
28281      * @param {Mixed} arg2
28282      * @param {Mixed} etc.
28283      */
28284     add : function(){
28285         var a = arguments, l = a.length;
28286         for(var i = 0; i < l; i++){
28287             this._add(a[i]);
28288         }
28289     },
28290     // private..
28291     _add : function(el) {
28292         
28293         if (el.xtype) {
28294             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28295         }
28296         
28297         if (el.applyTo){ // some kind of form field
28298             return this.addField(el);
28299         } 
28300         if (el.render){ // some kind of Toolbar.Item
28301             return this.addItem(el);
28302         }
28303         if (typeof el == "string"){ // string
28304             if(el == "separator" || el == "-"){
28305                 return this.addSeparator();
28306             }
28307             if (el == " "){
28308                 return this.addSpacer();
28309             }
28310             if(el == "->"){
28311                 return this.addFill();
28312             }
28313             return this.addText(el);
28314             
28315         }
28316         if(el.tagName){ // element
28317             return this.addElement(el);
28318         }
28319         if(typeof el == "object"){ // must be button config?
28320             return this.addButton(el);
28321         }
28322         // and now what?!?!
28323         return false;
28324         
28325     },
28326     
28327     /**
28328      * Add an Xtype element
28329      * @param {Object} xtype Xtype Object
28330      * @return {Object} created Object
28331      */
28332     addxtype : function(e){
28333         return this.add(e);  
28334     },
28335     
28336     /**
28337      * Returns the Element for this toolbar.
28338      * @return {Roo.Element}
28339      */
28340     getEl : function(){
28341         return this.el;  
28342     },
28343     
28344     /**
28345      * Adds a separator
28346      * @return {Roo.Toolbar.Item} The separator item
28347      */
28348     addSeparator : function(){
28349         return this.addItem(new Roo.Toolbar.Separator());
28350     },
28351
28352     /**
28353      * Adds a spacer element
28354      * @return {Roo.Toolbar.Spacer} The spacer item
28355      */
28356     addSpacer : function(){
28357         return this.addItem(new Roo.Toolbar.Spacer());
28358     },
28359
28360     /**
28361      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28362      * @return {Roo.Toolbar.Fill} The fill item
28363      */
28364     addFill : function(){
28365         return this.addItem(new Roo.Toolbar.Fill());
28366     },
28367
28368     /**
28369      * Adds any standard HTML element to the toolbar
28370      * @param {String/HTMLElement/Element} el The element or id of the element to add
28371      * @return {Roo.Toolbar.Item} The element's item
28372      */
28373     addElement : function(el){
28374         return this.addItem(new Roo.Toolbar.Item(el));
28375     },
28376     /**
28377      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28378      * @type Roo.util.MixedCollection  
28379      */
28380     items : false,
28381      
28382     /**
28383      * Adds any Toolbar.Item or subclass
28384      * @param {Roo.Toolbar.Item} item
28385      * @return {Roo.Toolbar.Item} The item
28386      */
28387     addItem : function(item){
28388         var td = this.nextBlock();
28389         item.render(td);
28390         this.items.add(item);
28391         return item;
28392     },
28393     
28394     /**
28395      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28396      * @param {Object/Array} config A button config or array of configs
28397      * @return {Roo.Toolbar.Button/Array}
28398      */
28399     addButton : function(config){
28400         if(config instanceof Array){
28401             var buttons = [];
28402             for(var i = 0, len = config.length; i < len; i++) {
28403                 buttons.push(this.addButton(config[i]));
28404             }
28405             return buttons;
28406         }
28407         var b = config;
28408         if(!(config instanceof Roo.Toolbar.Button)){
28409             b = config.split ?
28410                 new Roo.Toolbar.SplitButton(config) :
28411                 new Roo.Toolbar.Button(config);
28412         }
28413         var td = this.nextBlock();
28414         b.render(td);
28415         this.items.add(b);
28416         return b;
28417     },
28418     
28419     /**
28420      * Adds text to the toolbar
28421      * @param {String} text The text to add
28422      * @return {Roo.Toolbar.Item} The element's item
28423      */
28424     addText : function(text){
28425         return this.addItem(new Roo.Toolbar.TextItem(text));
28426     },
28427     
28428     /**
28429      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28430      * @param {Number} index The index where the item is to be inserted
28431      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28432      * @return {Roo.Toolbar.Button/Item}
28433      */
28434     insertButton : function(index, item){
28435         if(item instanceof Array){
28436             var buttons = [];
28437             for(var i = 0, len = item.length; i < len; i++) {
28438                buttons.push(this.insertButton(index + i, item[i]));
28439             }
28440             return buttons;
28441         }
28442         if (!(item instanceof Roo.Toolbar.Button)){
28443            item = new Roo.Toolbar.Button(item);
28444         }
28445         var td = document.createElement("td");
28446         this.tr.insertBefore(td, this.tr.childNodes[index]);
28447         item.render(td);
28448         this.items.insert(index, item);
28449         return item;
28450     },
28451     
28452     /**
28453      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28454      * @param {Object} config
28455      * @return {Roo.Toolbar.Item} The element's item
28456      */
28457     addDom : function(config, returnEl){
28458         var td = this.nextBlock();
28459         Roo.DomHelper.overwrite(td, config);
28460         var ti = new Roo.Toolbar.Item(td.firstChild);
28461         ti.render(td);
28462         this.items.add(ti);
28463         return ti;
28464     },
28465
28466     /**
28467      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28468      * @type Roo.util.MixedCollection  
28469      */
28470     fields : false,
28471     
28472     /**
28473      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28474      * Note: the field should not have been rendered yet. For a field that has already been
28475      * rendered, use {@link #addElement}.
28476      * @param {Roo.form.Field} field
28477      * @return {Roo.ToolbarItem}
28478      */
28479      
28480       
28481     addField : function(field) {
28482         if (!this.fields) {
28483             var autoId = 0;
28484             this.fields = new Roo.util.MixedCollection(false, function(o){
28485                 return o.id || ("item" + (++autoId));
28486             });
28487
28488         }
28489         
28490         var td = this.nextBlock();
28491         field.render(td);
28492         var ti = new Roo.Toolbar.Item(td.firstChild);
28493         ti.render(td);
28494         this.items.add(ti);
28495         this.fields.add(field);
28496         return ti;
28497     },
28498     /**
28499      * Hide the toolbar
28500      * @method hide
28501      */
28502      
28503       
28504     hide : function()
28505     {
28506         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28507         this.el.child('div').hide();
28508     },
28509     /**
28510      * Show the toolbar
28511      * @method show
28512      */
28513     show : function()
28514     {
28515         this.el.child('div').show();
28516     },
28517       
28518     // private
28519     nextBlock : function(){
28520         var td = document.createElement("td");
28521         this.tr.appendChild(td);
28522         return td;
28523     },
28524
28525     // private
28526     destroy : function(){
28527         if(this.items){ // rendered?
28528             Roo.destroy.apply(Roo, this.items.items);
28529         }
28530         if(this.fields){ // rendered?
28531             Roo.destroy.apply(Roo, this.fields.items);
28532         }
28533         Roo.Element.uncache(this.el, this.tr);
28534     }
28535 };
28536
28537 /**
28538  * @class Roo.Toolbar.Item
28539  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28540  * @constructor
28541  * Creates a new Item
28542  * @param {HTMLElement} el 
28543  */
28544 Roo.Toolbar.Item = function(el){
28545     var cfg = {};
28546     if (typeof (el.xtype) != 'undefined') {
28547         cfg = el;
28548         el = cfg.el;
28549     }
28550     
28551     this.el = Roo.getDom(el);
28552     this.id = Roo.id(this.el);
28553     this.hidden = false;
28554     
28555     this.addEvents({
28556          /**
28557              * @event render
28558              * Fires when the button is rendered
28559              * @param {Button} this
28560              */
28561         'render': true
28562     });
28563     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
28564 };
28565 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
28566 //Roo.Toolbar.Item.prototype = {
28567     
28568     /**
28569      * Get this item's HTML Element
28570      * @return {HTMLElement}
28571      */
28572     getEl : function(){
28573        return this.el;  
28574     },
28575
28576     // private
28577     render : function(td){
28578         
28579          this.td = td;
28580         td.appendChild(this.el);
28581         
28582         this.fireEvent('render', this);
28583     },
28584     
28585     /**
28586      * Removes and destroys this item.
28587      */
28588     destroy : function(){
28589         this.td.parentNode.removeChild(this.td);
28590     },
28591     
28592     /**
28593      * Shows this item.
28594      */
28595     show: function(){
28596         this.hidden = false;
28597         this.td.style.display = "";
28598     },
28599     
28600     /**
28601      * Hides this item.
28602      */
28603     hide: function(){
28604         this.hidden = true;
28605         this.td.style.display = "none";
28606     },
28607     
28608     /**
28609      * Convenience function for boolean show/hide.
28610      * @param {Boolean} visible true to show/false to hide
28611      */
28612     setVisible: function(visible){
28613         if(visible) {
28614             this.show();
28615         }else{
28616             this.hide();
28617         }
28618     },
28619     
28620     /**
28621      * Try to focus this item.
28622      */
28623     focus : function(){
28624         Roo.fly(this.el).focus();
28625     },
28626     
28627     /**
28628      * Disables this item.
28629      */
28630     disable : function(){
28631         Roo.fly(this.td).addClass("x-item-disabled");
28632         this.disabled = true;
28633         this.el.disabled = true;
28634     },
28635     
28636     /**
28637      * Enables this item.
28638      */
28639     enable : function(){
28640         Roo.fly(this.td).removeClass("x-item-disabled");
28641         this.disabled = false;
28642         this.el.disabled = false;
28643     }
28644 });
28645
28646
28647 /**
28648  * @class Roo.Toolbar.Separator
28649  * @extends Roo.Toolbar.Item
28650  * A simple toolbar separator class
28651  * @constructor
28652  * Creates a new Separator
28653  */
28654 Roo.Toolbar.Separator = function(cfg){
28655     
28656     var s = document.createElement("span");
28657     s.className = "ytb-sep";
28658     if (cfg) {
28659         cfg.el = s;
28660     }
28661     
28662     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
28663 };
28664 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28665     enable:Roo.emptyFn,
28666     disable:Roo.emptyFn,
28667     focus:Roo.emptyFn
28668 });
28669
28670 /**
28671  * @class Roo.Toolbar.Spacer
28672  * @extends Roo.Toolbar.Item
28673  * A simple element that adds extra horizontal space to a toolbar.
28674  * @constructor
28675  * Creates a new Spacer
28676  */
28677 Roo.Toolbar.Spacer = function(cfg){
28678     var s = document.createElement("div");
28679     s.className = "ytb-spacer";
28680     if (cfg) {
28681         cfg.el = s;
28682     }
28683     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
28684 };
28685 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28686     enable:Roo.emptyFn,
28687     disable:Roo.emptyFn,
28688     focus:Roo.emptyFn
28689 });
28690
28691 /**
28692  * @class Roo.Toolbar.Fill
28693  * @extends Roo.Toolbar.Spacer
28694  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28695  * @constructor
28696  * Creates a new Spacer
28697  */
28698 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28699     // private
28700     render : function(td){
28701         td.style.width = '100%';
28702         Roo.Toolbar.Fill.superclass.render.call(this, td);
28703     }
28704 });
28705
28706 /**
28707  * @class Roo.Toolbar.TextItem
28708  * @extends Roo.Toolbar.Item
28709  * A simple class that renders text directly into a toolbar.
28710  * @constructor
28711  * Creates a new TextItem
28712  * @param {String} text
28713  */
28714 Roo.Toolbar.TextItem = function(cfg){
28715     var  text = cfg || "";
28716     if (typeof(cfg) == 'object') {
28717         text = cfg.text || "";
28718     }  else {
28719         cfg = null;
28720     }
28721     var s = document.createElement("span");
28722     s.className = "ytb-text";
28723     s.innerHTML = text;
28724     if (cfg) {
28725         cfg.el  = s;
28726     }
28727     
28728     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
28729 };
28730 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28731     
28732      
28733     enable:Roo.emptyFn,
28734     disable:Roo.emptyFn,
28735     focus:Roo.emptyFn
28736 });
28737
28738 /**
28739  * @class Roo.Toolbar.Button
28740  * @extends Roo.Button
28741  * A button that renders into a toolbar.
28742  * @constructor
28743  * Creates a new Button
28744  * @param {Object} config A standard {@link Roo.Button} config object
28745  */
28746 Roo.Toolbar.Button = function(config){
28747     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28748 };
28749 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28750     render : function(td){
28751         this.td = td;
28752         Roo.Toolbar.Button.superclass.render.call(this, td);
28753     },
28754     
28755     /**
28756      * Removes and destroys this button
28757      */
28758     destroy : function(){
28759         Roo.Toolbar.Button.superclass.destroy.call(this);
28760         this.td.parentNode.removeChild(this.td);
28761     },
28762     
28763     /**
28764      * Shows this button
28765      */
28766     show: function(){
28767         this.hidden = false;
28768         this.td.style.display = "";
28769     },
28770     
28771     /**
28772      * Hides this button
28773      */
28774     hide: function(){
28775         this.hidden = true;
28776         this.td.style.display = "none";
28777     },
28778
28779     /**
28780      * Disables this item
28781      */
28782     disable : function(){
28783         Roo.fly(this.td).addClass("x-item-disabled");
28784         this.disabled = true;
28785     },
28786
28787     /**
28788      * Enables this item
28789      */
28790     enable : function(){
28791         Roo.fly(this.td).removeClass("x-item-disabled");
28792         this.disabled = false;
28793     }
28794 });
28795 // backwards compat
28796 Roo.ToolbarButton = Roo.Toolbar.Button;
28797
28798 /**
28799  * @class Roo.Toolbar.SplitButton
28800  * @extends Roo.SplitButton
28801  * A menu button that renders into a toolbar.
28802  * @constructor
28803  * Creates a new SplitButton
28804  * @param {Object} config A standard {@link Roo.SplitButton} config object
28805  */
28806 Roo.Toolbar.SplitButton = function(config){
28807     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28808 };
28809 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28810     render : function(td){
28811         this.td = td;
28812         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28813     },
28814     
28815     /**
28816      * Removes and destroys this button
28817      */
28818     destroy : function(){
28819         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28820         this.td.parentNode.removeChild(this.td);
28821     },
28822     
28823     /**
28824      * Shows this button
28825      */
28826     show: function(){
28827         this.hidden = false;
28828         this.td.style.display = "";
28829     },
28830     
28831     /**
28832      * Hides this button
28833      */
28834     hide: function(){
28835         this.hidden = true;
28836         this.td.style.display = "none";
28837     }
28838 });
28839
28840 // backwards compat
28841 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28842  * Based on:
28843  * Ext JS Library 1.1.1
28844  * Copyright(c) 2006-2007, Ext JS, LLC.
28845  *
28846  * Originally Released Under LGPL - original licence link has changed is not relivant.
28847  *
28848  * Fork - LGPL
28849  * <script type="text/javascript">
28850  */
28851  
28852 /**
28853  * @class Roo.PagingToolbar
28854  * @extends Roo.Toolbar
28855  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28856  * @constructor
28857  * Create a new PagingToolbar
28858  * @param {Object} config The config object
28859  */
28860 Roo.PagingToolbar = function(el, ds, config)
28861 {
28862     // old args format still supported... - xtype is prefered..
28863     if (typeof(el) == 'object' && el.xtype) {
28864         // created from xtype...
28865         config = el;
28866         ds = el.dataSource;
28867         el = config.container;
28868     }
28869     var items = [];
28870     if (config.items) {
28871         items = config.items;
28872         config.items = [];
28873     }
28874     
28875     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28876     this.ds = ds;
28877     this.cursor = 0;
28878     this.renderButtons(this.el);
28879     this.bind(ds);
28880     
28881     // supprot items array.
28882    
28883     Roo.each(items, function(e) {
28884         this.add(Roo.factory(e));
28885     },this);
28886     
28887 };
28888
28889 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28890     /**
28891      * @cfg {Roo.data.Store} dataSource
28892      * The underlying data store providing the paged data
28893      */
28894     /**
28895      * @cfg {String/HTMLElement/Element} container
28896      * container The id or element that will contain the toolbar
28897      */
28898     /**
28899      * @cfg {Boolean} displayInfo
28900      * True to display the displayMsg (defaults to false)
28901      */
28902     /**
28903      * @cfg {Number} pageSize
28904      * The number of records to display per page (defaults to 20)
28905      */
28906     pageSize: 20,
28907     /**
28908      * @cfg {String} displayMsg
28909      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28910      */
28911     displayMsg : 'Displaying {0} - {1} of {2}',
28912     /**
28913      * @cfg {String} emptyMsg
28914      * The message to display when no records are found (defaults to "No data to display")
28915      */
28916     emptyMsg : 'No data to display',
28917     /**
28918      * Customizable piece of the default paging text (defaults to "Page")
28919      * @type String
28920      */
28921     beforePageText : "Page",
28922     /**
28923      * Customizable piece of the default paging text (defaults to "of %0")
28924      * @type String
28925      */
28926     afterPageText : "of {0}",
28927     /**
28928      * Customizable piece of the default paging text (defaults to "First Page")
28929      * @type String
28930      */
28931     firstText : "First Page",
28932     /**
28933      * Customizable piece of the default paging text (defaults to "Previous Page")
28934      * @type String
28935      */
28936     prevText : "Previous Page",
28937     /**
28938      * Customizable piece of the default paging text (defaults to "Next Page")
28939      * @type String
28940      */
28941     nextText : "Next Page",
28942     /**
28943      * Customizable piece of the default paging text (defaults to "Last Page")
28944      * @type String
28945      */
28946     lastText : "Last Page",
28947     /**
28948      * Customizable piece of the default paging text (defaults to "Refresh")
28949      * @type String
28950      */
28951     refreshText : "Refresh",
28952
28953     // private
28954     renderButtons : function(el){
28955         Roo.PagingToolbar.superclass.render.call(this, el);
28956         this.first = this.addButton({
28957             tooltip: this.firstText,
28958             cls: "x-btn-icon x-grid-page-first",
28959             disabled: true,
28960             handler: this.onClick.createDelegate(this, ["first"])
28961         });
28962         this.prev = this.addButton({
28963             tooltip: this.prevText,
28964             cls: "x-btn-icon x-grid-page-prev",
28965             disabled: true,
28966             handler: this.onClick.createDelegate(this, ["prev"])
28967         });
28968         //this.addSeparator();
28969         this.add(this.beforePageText);
28970         this.field = Roo.get(this.addDom({
28971            tag: "input",
28972            type: "text",
28973            size: "3",
28974            value: "1",
28975            cls: "x-grid-page-number"
28976         }).el);
28977         this.field.on("keydown", this.onPagingKeydown, this);
28978         this.field.on("focus", function(){this.dom.select();});
28979         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28980         this.field.setHeight(18);
28981         //this.addSeparator();
28982         this.next = this.addButton({
28983             tooltip: this.nextText,
28984             cls: "x-btn-icon x-grid-page-next",
28985             disabled: true,
28986             handler: this.onClick.createDelegate(this, ["next"])
28987         });
28988         this.last = this.addButton({
28989             tooltip: this.lastText,
28990             cls: "x-btn-icon x-grid-page-last",
28991             disabled: true,
28992             handler: this.onClick.createDelegate(this, ["last"])
28993         });
28994         //this.addSeparator();
28995         this.loading = this.addButton({
28996             tooltip: this.refreshText,
28997             cls: "x-btn-icon x-grid-loading",
28998             handler: this.onClick.createDelegate(this, ["refresh"])
28999         });
29000
29001         if(this.displayInfo){
29002             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
29003         }
29004     },
29005
29006     // private
29007     updateInfo : function(){
29008         if(this.displayEl){
29009             var count = this.ds.getCount();
29010             var msg = count == 0 ?
29011                 this.emptyMsg :
29012                 String.format(
29013                     this.displayMsg,
29014                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
29015                 );
29016             this.displayEl.update(msg);
29017         }
29018     },
29019
29020     // private
29021     onLoad : function(ds, r, o){
29022        this.cursor = o.params ? o.params.start : 0;
29023        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
29024
29025        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
29026        this.field.dom.value = ap;
29027        this.first.setDisabled(ap == 1);
29028        this.prev.setDisabled(ap == 1);
29029        this.next.setDisabled(ap == ps);
29030        this.last.setDisabled(ap == ps);
29031        this.loading.enable();
29032        this.updateInfo();
29033     },
29034
29035     // private
29036     getPageData : function(){
29037         var total = this.ds.getTotalCount();
29038         return {
29039             total : total,
29040             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
29041             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
29042         };
29043     },
29044
29045     // private
29046     onLoadError : function(){
29047         this.loading.enable();
29048     },
29049
29050     // private
29051     onPagingKeydown : function(e){
29052         var k = e.getKey();
29053         var d = this.getPageData();
29054         if(k == e.RETURN){
29055             var v = this.field.dom.value, pageNum;
29056             if(!v || isNaN(pageNum = parseInt(v, 10))){
29057                 this.field.dom.value = d.activePage;
29058                 return;
29059             }
29060             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
29061             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29062             e.stopEvent();
29063         }
29064         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))
29065         {
29066           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29067           this.field.dom.value = pageNum;
29068           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29069           e.stopEvent();
29070         }
29071         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29072         {
29073           var v = this.field.dom.value, pageNum; 
29074           var increment = (e.shiftKey) ? 10 : 1;
29075           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29076             increment *= -1;
29077           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29078             this.field.dom.value = d.activePage;
29079             return;
29080           }
29081           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29082           {
29083             this.field.dom.value = parseInt(v, 10) + increment;
29084             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29085             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29086           }
29087           e.stopEvent();
29088         }
29089     },
29090
29091     // private
29092     beforeLoad : function(){
29093         if(this.loading){
29094             this.loading.disable();
29095         }
29096     },
29097
29098     // private
29099     onClick : function(which){
29100         var ds = this.ds;
29101         switch(which){
29102             case "first":
29103                 ds.load({params:{start: 0, limit: this.pageSize}});
29104             break;
29105             case "prev":
29106                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29107             break;
29108             case "next":
29109                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29110             break;
29111             case "last":
29112                 var total = ds.getTotalCount();
29113                 var extra = total % this.pageSize;
29114                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29115                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29116             break;
29117             case "refresh":
29118                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29119             break;
29120         }
29121     },
29122
29123     /**
29124      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29125      * @param {Roo.data.Store} store The data store to unbind
29126      */
29127     unbind : function(ds){
29128         ds.un("beforeload", this.beforeLoad, this);
29129         ds.un("load", this.onLoad, this);
29130         ds.un("loadexception", this.onLoadError, this);
29131         ds.un("remove", this.updateInfo, this);
29132         ds.un("add", this.updateInfo, this);
29133         this.ds = undefined;
29134     },
29135
29136     /**
29137      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29138      * @param {Roo.data.Store} store The data store to bind
29139      */
29140     bind : function(ds){
29141         ds.on("beforeload", this.beforeLoad, this);
29142         ds.on("load", this.onLoad, this);
29143         ds.on("loadexception", this.onLoadError, this);
29144         ds.on("remove", this.updateInfo, this);
29145         ds.on("add", this.updateInfo, this);
29146         this.ds = ds;
29147     }
29148 });/*
29149  * Based on:
29150  * Ext JS Library 1.1.1
29151  * Copyright(c) 2006-2007, Ext JS, LLC.
29152  *
29153  * Originally Released Under LGPL - original licence link has changed is not relivant.
29154  *
29155  * Fork - LGPL
29156  * <script type="text/javascript">
29157  */
29158
29159 /**
29160  * @class Roo.Resizable
29161  * @extends Roo.util.Observable
29162  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29163  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29164  * 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
29165  * the element will be wrapped for you automatically.</p>
29166  * <p>Here is the list of valid resize handles:</p>
29167  * <pre>
29168 Value   Description
29169 ------  -------------------
29170  'n'     north
29171  's'     south
29172  'e'     east
29173  'w'     west
29174  'nw'    northwest
29175  'sw'    southwest
29176  'se'    southeast
29177  'ne'    northeast
29178  'hd'    horizontal drag
29179  'all'   all
29180 </pre>
29181  * <p>Here's an example showing the creation of a typical Resizable:</p>
29182  * <pre><code>
29183 var resizer = new Roo.Resizable("element-id", {
29184     handles: 'all',
29185     minWidth: 200,
29186     minHeight: 100,
29187     maxWidth: 500,
29188     maxHeight: 400,
29189     pinned: true
29190 });
29191 resizer.on("resize", myHandler);
29192 </code></pre>
29193  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29194  * resizer.east.setDisplayed(false);</p>
29195  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29196  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29197  * resize operation's new size (defaults to [0, 0])
29198  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29199  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29200  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29201  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29202  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29203  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29204  * @cfg {Number} width The width of the element in pixels (defaults to null)
29205  * @cfg {Number} height The height of the element in pixels (defaults to null)
29206  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29207  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29208  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29209  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29210  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29211  * in favor of the handles config option (defaults to false)
29212  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29213  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29214  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29215  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29216  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29217  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29218  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29219  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29220  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29221  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29222  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29223  * @constructor
29224  * Create a new resizable component
29225  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29226  * @param {Object} config configuration options
29227   */
29228 Roo.Resizable = function(el, config)
29229 {
29230     this.el = Roo.get(el);
29231
29232     if(config && config.wrap){
29233         config.resizeChild = this.el;
29234         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29235         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29236         this.el.setStyle("overflow", "hidden");
29237         this.el.setPositioning(config.resizeChild.getPositioning());
29238         config.resizeChild.clearPositioning();
29239         if(!config.width || !config.height){
29240             var csize = config.resizeChild.getSize();
29241             this.el.setSize(csize.width, csize.height);
29242         }
29243         if(config.pinned && !config.adjustments){
29244             config.adjustments = "auto";
29245         }
29246     }
29247
29248     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29249     this.proxy.unselectable();
29250     this.proxy.enableDisplayMode('block');
29251
29252     Roo.apply(this, config);
29253
29254     if(this.pinned){
29255         this.disableTrackOver = true;
29256         this.el.addClass("x-resizable-pinned");
29257     }
29258     // if the element isn't positioned, make it relative
29259     var position = this.el.getStyle("position");
29260     if(position != "absolute" && position != "fixed"){
29261         this.el.setStyle("position", "relative");
29262     }
29263     if(!this.handles){ // no handles passed, must be legacy style
29264         this.handles = 's,e,se';
29265         if(this.multiDirectional){
29266             this.handles += ',n,w';
29267         }
29268     }
29269     if(this.handles == "all"){
29270         this.handles = "n s e w ne nw se sw";
29271     }
29272     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29273     var ps = Roo.Resizable.positions;
29274     for(var i = 0, len = hs.length; i < len; i++){
29275         if(hs[i] && ps[hs[i]]){
29276             var pos = ps[hs[i]];
29277             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29278         }
29279     }
29280     // legacy
29281     this.corner = this.southeast;
29282     
29283     // updateBox = the box can move..
29284     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29285         this.updateBox = true;
29286     }
29287
29288     this.activeHandle = null;
29289
29290     if(this.resizeChild){
29291         if(typeof this.resizeChild == "boolean"){
29292             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29293         }else{
29294             this.resizeChild = Roo.get(this.resizeChild, true);
29295         }
29296     }
29297     
29298     if(this.adjustments == "auto"){
29299         var rc = this.resizeChild;
29300         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29301         if(rc && (hw || hn)){
29302             rc.position("relative");
29303             rc.setLeft(hw ? hw.el.getWidth() : 0);
29304             rc.setTop(hn ? hn.el.getHeight() : 0);
29305         }
29306         this.adjustments = [
29307             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29308             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29309         ];
29310     }
29311
29312     if(this.draggable){
29313         this.dd = this.dynamic ?
29314             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29315         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29316     }
29317
29318     // public events
29319     this.addEvents({
29320         /**
29321          * @event beforeresize
29322          * Fired before resize is allowed. Set enabled to false to cancel resize.
29323          * @param {Roo.Resizable} this
29324          * @param {Roo.EventObject} e The mousedown event
29325          */
29326         "beforeresize" : true,
29327         /**
29328          * @event resizing
29329          * Fired a resizing.
29330          * @param {Roo.Resizable} this
29331          * @param {Number} x The new x position
29332          * @param {Number} y The new y position
29333          * @param {Number} w The new w width
29334          * @param {Number} h The new h hight
29335          * @param {Roo.EventObject} e The mouseup event
29336          */
29337         "resizing" : true,
29338         /**
29339          * @event resize
29340          * Fired after a resize.
29341          * @param {Roo.Resizable} this
29342          * @param {Number} width The new width
29343          * @param {Number} height The new height
29344          * @param {Roo.EventObject} e The mouseup event
29345          */
29346         "resize" : true
29347     });
29348
29349     if(this.width !== null && this.height !== null){
29350         this.resizeTo(this.width, this.height);
29351     }else{
29352         this.updateChildSize();
29353     }
29354     if(Roo.isIE){
29355         this.el.dom.style.zoom = 1;
29356     }
29357     Roo.Resizable.superclass.constructor.call(this);
29358 };
29359
29360 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29361         resizeChild : false,
29362         adjustments : [0, 0],
29363         minWidth : 5,
29364         minHeight : 5,
29365         maxWidth : 10000,
29366         maxHeight : 10000,
29367         enabled : true,
29368         animate : false,
29369         duration : .35,
29370         dynamic : false,
29371         handles : false,
29372         multiDirectional : false,
29373         disableTrackOver : false,
29374         easing : 'easeOutStrong',
29375         widthIncrement : 0,
29376         heightIncrement : 0,
29377         pinned : false,
29378         width : null,
29379         height : null,
29380         preserveRatio : false,
29381         transparent: false,
29382         minX: 0,
29383         minY: 0,
29384         draggable: false,
29385
29386         /**
29387          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29388          */
29389         constrainTo: undefined,
29390         /**
29391          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29392          */
29393         resizeRegion: undefined,
29394
29395
29396     /**
29397      * Perform a manual resize
29398      * @param {Number} width
29399      * @param {Number} height
29400      */
29401     resizeTo : function(width, height){
29402         this.el.setSize(width, height);
29403         this.updateChildSize();
29404         this.fireEvent("resize", this, width, height, null);
29405     },
29406
29407     // private
29408     startSizing : function(e, handle){
29409         this.fireEvent("beforeresize", this, e);
29410         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29411
29412             if(!this.overlay){
29413                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29414                 this.overlay.unselectable();
29415                 this.overlay.enableDisplayMode("block");
29416                 this.overlay.on("mousemove", this.onMouseMove, this);
29417                 this.overlay.on("mouseup", this.onMouseUp, this);
29418             }
29419             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29420
29421             this.resizing = true;
29422             this.startBox = this.el.getBox();
29423             this.startPoint = e.getXY();
29424             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29425                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29426
29427             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29428             this.overlay.show();
29429
29430             if(this.constrainTo) {
29431                 var ct = Roo.get(this.constrainTo);
29432                 this.resizeRegion = ct.getRegion().adjust(
29433                     ct.getFrameWidth('t'),
29434                     ct.getFrameWidth('l'),
29435                     -ct.getFrameWidth('b'),
29436                     -ct.getFrameWidth('r')
29437                 );
29438             }
29439
29440             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29441             this.proxy.show();
29442             this.proxy.setBox(this.startBox);
29443             if(!this.dynamic){
29444                 this.proxy.setStyle('visibility', 'visible');
29445             }
29446         }
29447     },
29448
29449     // private
29450     onMouseDown : function(handle, e){
29451         if(this.enabled){
29452             e.stopEvent();
29453             this.activeHandle = handle;
29454             this.startSizing(e, handle);
29455         }
29456     },
29457
29458     // private
29459     onMouseUp : function(e){
29460         var size = this.resizeElement();
29461         this.resizing = false;
29462         this.handleOut();
29463         this.overlay.hide();
29464         this.proxy.hide();
29465         this.fireEvent("resize", this, size.width, size.height, e);
29466     },
29467
29468     // private
29469     updateChildSize : function(){
29470         
29471         if(this.resizeChild){
29472             var el = this.el;
29473             var child = this.resizeChild;
29474             var adj = this.adjustments;
29475             if(el.dom.offsetWidth){
29476                 var b = el.getSize(true);
29477                 child.setSize(b.width+adj[0], b.height+adj[1]);
29478             }
29479             // Second call here for IE
29480             // The first call enables instant resizing and
29481             // the second call corrects scroll bars if they
29482             // exist
29483             if(Roo.isIE){
29484                 setTimeout(function(){
29485                     if(el.dom.offsetWidth){
29486                         var b = el.getSize(true);
29487                         child.setSize(b.width+adj[0], b.height+adj[1]);
29488                     }
29489                 }, 10);
29490             }
29491         }
29492     },
29493
29494     // private
29495     snap : function(value, inc, min){
29496         if(!inc || !value) return value;
29497         var newValue = value;
29498         var m = value % inc;
29499         if(m > 0){
29500             if(m > (inc/2)){
29501                 newValue = value + (inc-m);
29502             }else{
29503                 newValue = value - m;
29504             }
29505         }
29506         return Math.max(min, newValue);
29507     },
29508
29509     // private
29510     resizeElement : function(){
29511         var box = this.proxy.getBox();
29512         if(this.updateBox){
29513             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29514         }else{
29515             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29516         }
29517         this.updateChildSize();
29518         if(!this.dynamic){
29519             this.proxy.hide();
29520         }
29521         return box;
29522     },
29523
29524     // private
29525     constrain : function(v, diff, m, mx){
29526         if(v - diff < m){
29527             diff = v - m;
29528         }else if(v - diff > mx){
29529             diff = mx - v;
29530         }
29531         return diff;
29532     },
29533
29534     // private
29535     onMouseMove : function(e){
29536         
29537         if(this.enabled){
29538             try{// try catch so if something goes wrong the user doesn't get hung
29539
29540             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29541                 return;
29542             }
29543
29544             //var curXY = this.startPoint;
29545             var curSize = this.curSize || this.startBox;
29546             var x = this.startBox.x, y = this.startBox.y;
29547             var ox = x, oy = y;
29548             var w = curSize.width, h = curSize.height;
29549             var ow = w, oh = h;
29550             var mw = this.minWidth, mh = this.minHeight;
29551             var mxw = this.maxWidth, mxh = this.maxHeight;
29552             var wi = this.widthIncrement;
29553             var hi = this.heightIncrement;
29554
29555             var eventXY = e.getXY();
29556             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29557             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29558
29559             var pos = this.activeHandle.position;
29560
29561             switch(pos){
29562                 case "east":
29563                     w += diffX;
29564                     w = Math.min(Math.max(mw, w), mxw);
29565                     break;
29566              
29567                 case "south":
29568                     h += diffY;
29569                     h = Math.min(Math.max(mh, h), mxh);
29570                     break;
29571                 case "southeast":
29572                     w += diffX;
29573                     h += diffY;
29574                     w = Math.min(Math.max(mw, w), mxw);
29575                     h = Math.min(Math.max(mh, h), mxh);
29576                     break;
29577                 case "north":
29578                     diffY = this.constrain(h, diffY, mh, mxh);
29579                     y += diffY;
29580                     h -= diffY;
29581                     break;
29582                 case "hdrag":
29583                     
29584                     if (wi) {
29585                         var adiffX = Math.abs(diffX);
29586                         var sub = (adiffX % wi); // how much 
29587                         if (sub > (wi/2)) { // far enough to snap
29588                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29589                         } else {
29590                             // remove difference.. 
29591                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29592                         }
29593                     }
29594                     x += diffX;
29595                     x = Math.max(this.minX, x);
29596                     break;
29597                 case "west":
29598                     diffX = this.constrain(w, diffX, mw, mxw);
29599                     x += diffX;
29600                     w -= diffX;
29601                     break;
29602                 case "northeast":
29603                     w += diffX;
29604                     w = Math.min(Math.max(mw, w), mxw);
29605                     diffY = this.constrain(h, diffY, mh, mxh);
29606                     y += diffY;
29607                     h -= diffY;
29608                     break;
29609                 case "northwest":
29610                     diffX = this.constrain(w, diffX, mw, mxw);
29611                     diffY = this.constrain(h, diffY, mh, mxh);
29612                     y += diffY;
29613                     h -= diffY;
29614                     x += diffX;
29615                     w -= diffX;
29616                     break;
29617                case "southwest":
29618                     diffX = this.constrain(w, diffX, mw, mxw);
29619                     h += diffY;
29620                     h = Math.min(Math.max(mh, h), mxh);
29621                     x += diffX;
29622                     w -= diffX;
29623                     break;
29624             }
29625
29626             var sw = this.snap(w, wi, mw);
29627             var sh = this.snap(h, hi, mh);
29628             if(sw != w || sh != h){
29629                 switch(pos){
29630                     case "northeast":
29631                         y -= sh - h;
29632                     break;
29633                     case "north":
29634                         y -= sh - h;
29635                         break;
29636                     case "southwest":
29637                         x -= sw - w;
29638                     break;
29639                     case "west":
29640                         x -= sw - w;
29641                         break;
29642                     case "northwest":
29643                         x -= sw - w;
29644                         y -= sh - h;
29645                     break;
29646                 }
29647                 w = sw;
29648                 h = sh;
29649             }
29650
29651             if(this.preserveRatio){
29652                 switch(pos){
29653                     case "southeast":
29654                     case "east":
29655                         h = oh * (w/ow);
29656                         h = Math.min(Math.max(mh, h), mxh);
29657                         w = ow * (h/oh);
29658                        break;
29659                     case "south":
29660                         w = ow * (h/oh);
29661                         w = Math.min(Math.max(mw, w), mxw);
29662                         h = oh * (w/ow);
29663                         break;
29664                     case "northeast":
29665                         w = ow * (h/oh);
29666                         w = Math.min(Math.max(mw, w), mxw);
29667                         h = oh * (w/ow);
29668                     break;
29669                     case "north":
29670                         var tw = w;
29671                         w = ow * (h/oh);
29672                         w = Math.min(Math.max(mw, w), mxw);
29673                         h = oh * (w/ow);
29674                         x += (tw - w) / 2;
29675                         break;
29676                     case "southwest":
29677                         h = oh * (w/ow);
29678                         h = Math.min(Math.max(mh, h), mxh);
29679                         var tw = w;
29680                         w = ow * (h/oh);
29681                         x += tw - w;
29682                         break;
29683                     case "west":
29684                         var th = h;
29685                         h = oh * (w/ow);
29686                         h = Math.min(Math.max(mh, h), mxh);
29687                         y += (th - h) / 2;
29688                         var tw = w;
29689                         w = ow * (h/oh);
29690                         x += tw - w;
29691                        break;
29692                     case "northwest":
29693                         var tw = w;
29694                         var th = h;
29695                         h = oh * (w/ow);
29696                         h = Math.min(Math.max(mh, h), mxh);
29697                         w = ow * (h/oh);
29698                         y += th - h;
29699                         x += tw - w;
29700                        break;
29701
29702                 }
29703             }
29704             if (pos == 'hdrag') {
29705                 w = ow;
29706             }
29707             this.proxy.setBounds(x, y, w, h);
29708             if(this.dynamic){
29709                 this.resizeElement();
29710             }
29711             }catch(e){}
29712         }
29713         this.fireEvent("resizing", this, x, y, w, h, e);
29714     },
29715
29716     // private
29717     handleOver : function(){
29718         if(this.enabled){
29719             this.el.addClass("x-resizable-over");
29720         }
29721     },
29722
29723     // private
29724     handleOut : function(){
29725         if(!this.resizing){
29726             this.el.removeClass("x-resizable-over");
29727         }
29728     },
29729
29730     /**
29731      * Returns the element this component is bound to.
29732      * @return {Roo.Element}
29733      */
29734     getEl : function(){
29735         return this.el;
29736     },
29737
29738     /**
29739      * Returns the resizeChild element (or null).
29740      * @return {Roo.Element}
29741      */
29742     getResizeChild : function(){
29743         return this.resizeChild;
29744     },
29745     groupHandler : function()
29746     {
29747         
29748     },
29749     /**
29750      * Destroys this resizable. If the element was wrapped and
29751      * removeEl is not true then the element remains.
29752      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29753      */
29754     destroy : function(removeEl){
29755         this.proxy.remove();
29756         if(this.overlay){
29757             this.overlay.removeAllListeners();
29758             this.overlay.remove();
29759         }
29760         var ps = Roo.Resizable.positions;
29761         for(var k in ps){
29762             if(typeof ps[k] != "function" && this[ps[k]]){
29763                 var h = this[ps[k]];
29764                 h.el.removeAllListeners();
29765                 h.el.remove();
29766             }
29767         }
29768         if(removeEl){
29769             this.el.update("");
29770             this.el.remove();
29771         }
29772     }
29773 });
29774
29775 // private
29776 // hash to map config positions to true positions
29777 Roo.Resizable.positions = {
29778     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29779     hd: "hdrag"
29780 };
29781
29782 // private
29783 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29784     if(!this.tpl){
29785         // only initialize the template if resizable is used
29786         var tpl = Roo.DomHelper.createTemplate(
29787             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29788         );
29789         tpl.compile();
29790         Roo.Resizable.Handle.prototype.tpl = tpl;
29791     }
29792     this.position = pos;
29793     this.rz = rz;
29794     // show north drag fro topdra
29795     var handlepos = pos == 'hdrag' ? 'north' : pos;
29796     
29797     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29798     if (pos == 'hdrag') {
29799         this.el.setStyle('cursor', 'pointer');
29800     }
29801     this.el.unselectable();
29802     if(transparent){
29803         this.el.setOpacity(0);
29804     }
29805     this.el.on("mousedown", this.onMouseDown, this);
29806     if(!disableTrackOver){
29807         this.el.on("mouseover", this.onMouseOver, this);
29808         this.el.on("mouseout", this.onMouseOut, this);
29809     }
29810 };
29811
29812 // private
29813 Roo.Resizable.Handle.prototype = {
29814     afterResize : function(rz){
29815         Roo.log('after?');
29816         // do nothing
29817     },
29818     // private
29819     onMouseDown : function(e){
29820         this.rz.onMouseDown(this, e);
29821     },
29822     // private
29823     onMouseOver : function(e){
29824         this.rz.handleOver(this, e);
29825     },
29826     // private
29827     onMouseOut : function(e){
29828         this.rz.handleOut(this, e);
29829     }
29830 };/*
29831  * Based on:
29832  * Ext JS Library 1.1.1
29833  * Copyright(c) 2006-2007, Ext JS, LLC.
29834  *
29835  * Originally Released Under LGPL - original licence link has changed is not relivant.
29836  *
29837  * Fork - LGPL
29838  * <script type="text/javascript">
29839  */
29840
29841 /**
29842  * @class Roo.Editor
29843  * @extends Roo.Component
29844  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29845  * @constructor
29846  * Create a new Editor
29847  * @param {Roo.form.Field} field The Field object (or descendant)
29848  * @param {Object} config The config object
29849  */
29850 Roo.Editor = function(field, config){
29851     Roo.Editor.superclass.constructor.call(this, config);
29852     this.field = field;
29853     this.addEvents({
29854         /**
29855              * @event beforestartedit
29856              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29857              * false from the handler of this event.
29858              * @param {Editor} this
29859              * @param {Roo.Element} boundEl The underlying element bound to this editor
29860              * @param {Mixed} value The field value being set
29861              */
29862         "beforestartedit" : true,
29863         /**
29864              * @event startedit
29865              * Fires when this editor is displayed
29866              * @param {Roo.Element} boundEl The underlying element bound to this editor
29867              * @param {Mixed} value The starting field value
29868              */
29869         "startedit" : true,
29870         /**
29871              * @event beforecomplete
29872              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29873              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29874              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29875              * event will not fire since no edit actually occurred.
29876              * @param {Editor} this
29877              * @param {Mixed} value The current field value
29878              * @param {Mixed} startValue The original field value
29879              */
29880         "beforecomplete" : true,
29881         /**
29882              * @event complete
29883              * Fires after editing is complete and any changed value has been written to the underlying field.
29884              * @param {Editor} this
29885              * @param {Mixed} value The current field value
29886              * @param {Mixed} startValue The original field value
29887              */
29888         "complete" : true,
29889         /**
29890          * @event specialkey
29891          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29892          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29893          * @param {Roo.form.Field} this
29894          * @param {Roo.EventObject} e The event object
29895          */
29896         "specialkey" : true
29897     });
29898 };
29899
29900 Roo.extend(Roo.Editor, Roo.Component, {
29901     /**
29902      * @cfg {Boolean/String} autosize
29903      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29904      * or "height" to adopt the height only (defaults to false)
29905      */
29906     /**
29907      * @cfg {Boolean} revertInvalid
29908      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29909      * validation fails (defaults to true)
29910      */
29911     /**
29912      * @cfg {Boolean} ignoreNoChange
29913      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29914      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29915      * will never be ignored.
29916      */
29917     /**
29918      * @cfg {Boolean} hideEl
29919      * False to keep the bound element visible while the editor is displayed (defaults to true)
29920      */
29921     /**
29922      * @cfg {Mixed} value
29923      * The data value of the underlying field (defaults to "")
29924      */
29925     value : "",
29926     /**
29927      * @cfg {String} alignment
29928      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29929      */
29930     alignment: "c-c?",
29931     /**
29932      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29933      * for bottom-right shadow (defaults to "frame")
29934      */
29935     shadow : "frame",
29936     /**
29937      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29938      */
29939     constrain : false,
29940     /**
29941      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29942      */
29943     completeOnEnter : false,
29944     /**
29945      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29946      */
29947     cancelOnEsc : false,
29948     /**
29949      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29950      */
29951     updateEl : false,
29952
29953     // private
29954     onRender : function(ct, position){
29955         this.el = new Roo.Layer({
29956             shadow: this.shadow,
29957             cls: "x-editor",
29958             parentEl : ct,
29959             shim : this.shim,
29960             shadowOffset:4,
29961             id: this.id,
29962             constrain: this.constrain
29963         });
29964         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29965         if(this.field.msgTarget != 'title'){
29966             this.field.msgTarget = 'qtip';
29967         }
29968         this.field.render(this.el);
29969         if(Roo.isGecko){
29970             this.field.el.dom.setAttribute('autocomplete', 'off');
29971         }
29972         this.field.on("specialkey", this.onSpecialKey, this);
29973         if(this.swallowKeys){
29974             this.field.el.swallowEvent(['keydown','keypress']);
29975         }
29976         this.field.show();
29977         this.field.on("blur", this.onBlur, this);
29978         if(this.field.grow){
29979             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29980         }
29981     },
29982
29983     onSpecialKey : function(field, e)
29984     {
29985         //Roo.log('editor onSpecialKey');
29986         if(this.completeOnEnter && e.getKey() == e.ENTER){
29987             e.stopEvent();
29988             this.completeEdit();
29989             return;
29990         }
29991         // do not fire special key otherwise it might hide close the editor...
29992         if(e.getKey() == e.ENTER){    
29993             return;
29994         }
29995         if(this.cancelOnEsc && e.getKey() == e.ESC){
29996             this.cancelEdit();
29997             return;
29998         } 
29999         this.fireEvent('specialkey', field, e);
30000     
30001     },
30002
30003     /**
30004      * Starts the editing process and shows the editor.
30005      * @param {String/HTMLElement/Element} el The element to edit
30006      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
30007       * to the innerHTML of el.
30008      */
30009     startEdit : function(el, value){
30010         if(this.editing){
30011             this.completeEdit();
30012         }
30013         this.boundEl = Roo.get(el);
30014         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
30015         if(!this.rendered){
30016             this.render(this.parentEl || document.body);
30017         }
30018         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
30019             return;
30020         }
30021         this.startValue = v;
30022         this.field.setValue(v);
30023         if(this.autoSize){
30024             var sz = this.boundEl.getSize();
30025             switch(this.autoSize){
30026                 case "width":
30027                 this.setSize(sz.width,  "");
30028                 break;
30029                 case "height":
30030                 this.setSize("",  sz.height);
30031                 break;
30032                 default:
30033                 this.setSize(sz.width,  sz.height);
30034             }
30035         }
30036         this.el.alignTo(this.boundEl, this.alignment);
30037         this.editing = true;
30038         if(Roo.QuickTips){
30039             Roo.QuickTips.disable();
30040         }
30041         this.show();
30042     },
30043
30044     /**
30045      * Sets the height and width of this editor.
30046      * @param {Number} width The new width
30047      * @param {Number} height The new height
30048      */
30049     setSize : function(w, h){
30050         this.field.setSize(w, h);
30051         if(this.el){
30052             this.el.sync();
30053         }
30054     },
30055
30056     /**
30057      * Realigns the editor to the bound field based on the current alignment config value.
30058      */
30059     realign : function(){
30060         this.el.alignTo(this.boundEl, this.alignment);
30061     },
30062
30063     /**
30064      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
30065      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30066      */
30067     completeEdit : function(remainVisible){
30068         if(!this.editing){
30069             return;
30070         }
30071         var v = this.getValue();
30072         if(this.revertInvalid !== false && !this.field.isValid()){
30073             v = this.startValue;
30074             this.cancelEdit(true);
30075         }
30076         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30077             this.editing = false;
30078             this.hide();
30079             return;
30080         }
30081         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30082             this.editing = false;
30083             if(this.updateEl && this.boundEl){
30084                 this.boundEl.update(v);
30085             }
30086             if(remainVisible !== true){
30087                 this.hide();
30088             }
30089             this.fireEvent("complete", this, v, this.startValue);
30090         }
30091     },
30092
30093     // private
30094     onShow : function(){
30095         this.el.show();
30096         if(this.hideEl !== false){
30097             this.boundEl.hide();
30098         }
30099         this.field.show();
30100         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30101             this.fixIEFocus = true;
30102             this.deferredFocus.defer(50, this);
30103         }else{
30104             this.field.focus();
30105         }
30106         this.fireEvent("startedit", this.boundEl, this.startValue);
30107     },
30108
30109     deferredFocus : function(){
30110         if(this.editing){
30111             this.field.focus();
30112         }
30113     },
30114
30115     /**
30116      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30117      * reverted to the original starting value.
30118      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30119      * cancel (defaults to false)
30120      */
30121     cancelEdit : function(remainVisible){
30122         if(this.editing){
30123             this.setValue(this.startValue);
30124             if(remainVisible !== true){
30125                 this.hide();
30126             }
30127         }
30128     },
30129
30130     // private
30131     onBlur : function(){
30132         if(this.allowBlur !== true && this.editing){
30133             this.completeEdit();
30134         }
30135     },
30136
30137     // private
30138     onHide : function(){
30139         if(this.editing){
30140             this.completeEdit();
30141             return;
30142         }
30143         this.field.blur();
30144         if(this.field.collapse){
30145             this.field.collapse();
30146         }
30147         this.el.hide();
30148         if(this.hideEl !== false){
30149             this.boundEl.show();
30150         }
30151         if(Roo.QuickTips){
30152             Roo.QuickTips.enable();
30153         }
30154     },
30155
30156     /**
30157      * Sets the data value of the editor
30158      * @param {Mixed} value Any valid value supported by the underlying field
30159      */
30160     setValue : function(v){
30161         this.field.setValue(v);
30162     },
30163
30164     /**
30165      * Gets the data value of the editor
30166      * @return {Mixed} The data value
30167      */
30168     getValue : function(){
30169         return this.field.getValue();
30170     }
30171 });/*
30172  * Based on:
30173  * Ext JS Library 1.1.1
30174  * Copyright(c) 2006-2007, Ext JS, LLC.
30175  *
30176  * Originally Released Under LGPL - original licence link has changed is not relivant.
30177  *
30178  * Fork - LGPL
30179  * <script type="text/javascript">
30180  */
30181  
30182 /**
30183  * @class Roo.BasicDialog
30184  * @extends Roo.util.Observable
30185  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30186  * <pre><code>
30187 var dlg = new Roo.BasicDialog("my-dlg", {
30188     height: 200,
30189     width: 300,
30190     minHeight: 100,
30191     minWidth: 150,
30192     modal: true,
30193     proxyDrag: true,
30194     shadow: true
30195 });
30196 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30197 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30198 dlg.addButton('Cancel', dlg.hide, dlg);
30199 dlg.show();
30200 </code></pre>
30201   <b>A Dialog should always be a direct child of the body element.</b>
30202  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30203  * @cfg {String} title Default text to display in the title bar (defaults to null)
30204  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30205  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30206  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30207  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30208  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30209  * (defaults to null with no animation)
30210  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30211  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30212  * property for valid values (defaults to 'all')
30213  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30214  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30215  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30216  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30217  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30218  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30219  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30220  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30221  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30222  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30223  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30224  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30225  * draggable = true (defaults to false)
30226  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30227  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30228  * shadow (defaults to false)
30229  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30230  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30231  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30232  * @cfg {Array} buttons Array of buttons
30233  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30234  * @constructor
30235  * Create a new BasicDialog.
30236  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30237  * @param {Object} config Configuration options
30238  */
30239 Roo.BasicDialog = function(el, config){
30240     this.el = Roo.get(el);
30241     var dh = Roo.DomHelper;
30242     if(!this.el && config && config.autoCreate){
30243         if(typeof config.autoCreate == "object"){
30244             if(!config.autoCreate.id){
30245                 config.autoCreate.id = el;
30246             }
30247             this.el = dh.append(document.body,
30248                         config.autoCreate, true);
30249         }else{
30250             this.el = dh.append(document.body,
30251                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30252         }
30253     }
30254     el = this.el;
30255     el.setDisplayed(true);
30256     el.hide = this.hideAction;
30257     this.id = el.id;
30258     el.addClass("x-dlg");
30259
30260     Roo.apply(this, config);
30261
30262     this.proxy = el.createProxy("x-dlg-proxy");
30263     this.proxy.hide = this.hideAction;
30264     this.proxy.setOpacity(.5);
30265     this.proxy.hide();
30266
30267     if(config.width){
30268         el.setWidth(config.width);
30269     }
30270     if(config.height){
30271         el.setHeight(config.height);
30272     }
30273     this.size = el.getSize();
30274     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30275         this.xy = [config.x,config.y];
30276     }else{
30277         this.xy = el.getCenterXY(true);
30278     }
30279     /** The header element @type Roo.Element */
30280     this.header = el.child("> .x-dlg-hd");
30281     /** The body element @type Roo.Element */
30282     this.body = el.child("> .x-dlg-bd");
30283     /** The footer element @type Roo.Element */
30284     this.footer = el.child("> .x-dlg-ft");
30285
30286     if(!this.header){
30287         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30288     }
30289     if(!this.body){
30290         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30291     }
30292
30293     this.header.unselectable();
30294     if(this.title){
30295         this.header.update(this.title);
30296     }
30297     // this element allows the dialog to be focused for keyboard event
30298     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30299     this.focusEl.swallowEvent("click", true);
30300
30301     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30302
30303     // wrap the body and footer for special rendering
30304     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30305     if(this.footer){
30306         this.bwrap.dom.appendChild(this.footer.dom);
30307     }
30308
30309     this.bg = this.el.createChild({
30310         tag: "div", cls:"x-dlg-bg",
30311         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30312     });
30313     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30314
30315
30316     if(this.autoScroll !== false && !this.autoTabs){
30317         this.body.setStyle("overflow", "auto");
30318     }
30319
30320     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30321
30322     if(this.closable !== false){
30323         this.el.addClass("x-dlg-closable");
30324         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30325         this.close.on("click", this.closeClick, this);
30326         this.close.addClassOnOver("x-dlg-close-over");
30327     }
30328     if(this.collapsible !== false){
30329         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30330         this.collapseBtn.on("click", this.collapseClick, this);
30331         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30332         this.header.on("dblclick", this.collapseClick, this);
30333     }
30334     if(this.resizable !== false){
30335         this.el.addClass("x-dlg-resizable");
30336         this.resizer = new Roo.Resizable(el, {
30337             minWidth: this.minWidth || 80,
30338             minHeight:this.minHeight || 80,
30339             handles: this.resizeHandles || "all",
30340             pinned: true
30341         });
30342         this.resizer.on("beforeresize", this.beforeResize, this);
30343         this.resizer.on("resize", this.onResize, this);
30344     }
30345     if(this.draggable !== false){
30346         el.addClass("x-dlg-draggable");
30347         if (!this.proxyDrag) {
30348             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30349         }
30350         else {
30351             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30352         }
30353         dd.setHandleElId(this.header.id);
30354         dd.endDrag = this.endMove.createDelegate(this);
30355         dd.startDrag = this.startMove.createDelegate(this);
30356         dd.onDrag = this.onDrag.createDelegate(this);
30357         dd.scroll = false;
30358         this.dd = dd;
30359     }
30360     if(this.modal){
30361         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30362         this.mask.enableDisplayMode("block");
30363         this.mask.hide();
30364         this.el.addClass("x-dlg-modal");
30365     }
30366     if(this.shadow){
30367         this.shadow = new Roo.Shadow({
30368             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30369             offset : this.shadowOffset
30370         });
30371     }else{
30372         this.shadowOffset = 0;
30373     }
30374     if(Roo.useShims && this.shim !== false){
30375         this.shim = this.el.createShim();
30376         this.shim.hide = this.hideAction;
30377         this.shim.hide();
30378     }else{
30379         this.shim = false;
30380     }
30381     if(this.autoTabs){
30382         this.initTabs();
30383     }
30384     if (this.buttons) { 
30385         var bts= this.buttons;
30386         this.buttons = [];
30387         Roo.each(bts, function(b) {
30388             this.addButton(b);
30389         }, this);
30390     }
30391     
30392     
30393     this.addEvents({
30394         /**
30395          * @event keydown
30396          * Fires when a key is pressed
30397          * @param {Roo.BasicDialog} this
30398          * @param {Roo.EventObject} e
30399          */
30400         "keydown" : true,
30401         /**
30402          * @event move
30403          * Fires when this dialog is moved by the user.
30404          * @param {Roo.BasicDialog} this
30405          * @param {Number} x The new page X
30406          * @param {Number} y The new page Y
30407          */
30408         "move" : true,
30409         /**
30410          * @event resize
30411          * Fires when this dialog is resized by the user.
30412          * @param {Roo.BasicDialog} this
30413          * @param {Number} width The new width
30414          * @param {Number} height The new height
30415          */
30416         "resize" : true,
30417         /**
30418          * @event beforehide
30419          * Fires before this dialog is hidden.
30420          * @param {Roo.BasicDialog} this
30421          */
30422         "beforehide" : true,
30423         /**
30424          * @event hide
30425          * Fires when this dialog is hidden.
30426          * @param {Roo.BasicDialog} this
30427          */
30428         "hide" : true,
30429         /**
30430          * @event beforeshow
30431          * Fires before this dialog is shown.
30432          * @param {Roo.BasicDialog} this
30433          */
30434         "beforeshow" : true,
30435         /**
30436          * @event show
30437          * Fires when this dialog is shown.
30438          * @param {Roo.BasicDialog} this
30439          */
30440         "show" : true
30441     });
30442     el.on("keydown", this.onKeyDown, this);
30443     el.on("mousedown", this.toFront, this);
30444     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30445     this.el.hide();
30446     Roo.DialogManager.register(this);
30447     Roo.BasicDialog.superclass.constructor.call(this);
30448 };
30449
30450 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30451     shadowOffset: Roo.isIE ? 6 : 5,
30452     minHeight: 80,
30453     minWidth: 200,
30454     minButtonWidth: 75,
30455     defaultButton: null,
30456     buttonAlign: "right",
30457     tabTag: 'div',
30458     firstShow: true,
30459
30460     /**
30461      * Sets the dialog title text
30462      * @param {String} text The title text to display
30463      * @return {Roo.BasicDialog} this
30464      */
30465     setTitle : function(text){
30466         this.header.update(text);
30467         return this;
30468     },
30469
30470     // private
30471     closeClick : function(){
30472         this.hide();
30473     },
30474
30475     // private
30476     collapseClick : function(){
30477         this[this.collapsed ? "expand" : "collapse"]();
30478     },
30479
30480     /**
30481      * Collapses the dialog to its minimized state (only the title bar is visible).
30482      * Equivalent to the user clicking the collapse dialog button.
30483      */
30484     collapse : function(){
30485         if(!this.collapsed){
30486             this.collapsed = true;
30487             this.el.addClass("x-dlg-collapsed");
30488             this.restoreHeight = this.el.getHeight();
30489             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30490         }
30491     },
30492
30493     /**
30494      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30495      * clicking the expand dialog button.
30496      */
30497     expand : function(){
30498         if(this.collapsed){
30499             this.collapsed = false;
30500             this.el.removeClass("x-dlg-collapsed");
30501             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30502         }
30503     },
30504
30505     /**
30506      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30507      * @return {Roo.TabPanel} The tabs component
30508      */
30509     initTabs : function(){
30510         var tabs = this.getTabs();
30511         while(tabs.getTab(0)){
30512             tabs.removeTab(0);
30513         }
30514         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30515             var dom = el.dom;
30516             tabs.addTab(Roo.id(dom), dom.title);
30517             dom.title = "";
30518         });
30519         tabs.activate(0);
30520         return tabs;
30521     },
30522
30523     // private
30524     beforeResize : function(){
30525         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30526     },
30527
30528     // private
30529     onResize : function(){
30530         this.refreshSize();
30531         this.syncBodyHeight();
30532         this.adjustAssets();
30533         this.focus();
30534         this.fireEvent("resize", this, this.size.width, this.size.height);
30535     },
30536
30537     // private
30538     onKeyDown : function(e){
30539         if(this.isVisible()){
30540             this.fireEvent("keydown", this, e);
30541         }
30542     },
30543
30544     /**
30545      * Resizes the dialog.
30546      * @param {Number} width
30547      * @param {Number} height
30548      * @return {Roo.BasicDialog} this
30549      */
30550     resizeTo : function(width, height){
30551         this.el.setSize(width, height);
30552         this.size = {width: width, height: height};
30553         this.syncBodyHeight();
30554         if(this.fixedcenter){
30555             this.center();
30556         }
30557         if(this.isVisible()){
30558             this.constrainXY();
30559             this.adjustAssets();
30560         }
30561         this.fireEvent("resize", this, width, height);
30562         return this;
30563     },
30564
30565
30566     /**
30567      * Resizes the dialog to fit the specified content size.
30568      * @param {Number} width
30569      * @param {Number} height
30570      * @return {Roo.BasicDialog} this
30571      */
30572     setContentSize : function(w, h){
30573         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30574         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30575         //if(!this.el.isBorderBox()){
30576             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30577             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30578         //}
30579         if(this.tabs){
30580             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30581             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30582         }
30583         this.resizeTo(w, h);
30584         return this;
30585     },
30586
30587     /**
30588      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30589      * executed in response to a particular key being pressed while the dialog is active.
30590      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30591      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30592      * @param {Function} fn The function to call
30593      * @param {Object} scope (optional) The scope of the function
30594      * @return {Roo.BasicDialog} this
30595      */
30596     addKeyListener : function(key, fn, scope){
30597         var keyCode, shift, ctrl, alt;
30598         if(typeof key == "object" && !(key instanceof Array)){
30599             keyCode = key["key"];
30600             shift = key["shift"];
30601             ctrl = key["ctrl"];
30602             alt = key["alt"];
30603         }else{
30604             keyCode = key;
30605         }
30606         var handler = function(dlg, e){
30607             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30608                 var k = e.getKey();
30609                 if(keyCode instanceof Array){
30610                     for(var i = 0, len = keyCode.length; i < len; i++){
30611                         if(keyCode[i] == k){
30612                           fn.call(scope || window, dlg, k, e);
30613                           return;
30614                         }
30615                     }
30616                 }else{
30617                     if(k == keyCode){
30618                         fn.call(scope || window, dlg, k, e);
30619                     }
30620                 }
30621             }
30622         };
30623         this.on("keydown", handler);
30624         return this;
30625     },
30626
30627     /**
30628      * Returns the TabPanel component (creates it if it doesn't exist).
30629      * Note: If you wish to simply check for the existence of tabs without creating them,
30630      * check for a null 'tabs' property.
30631      * @return {Roo.TabPanel} The tabs component
30632      */
30633     getTabs : function(){
30634         if(!this.tabs){
30635             this.el.addClass("x-dlg-auto-tabs");
30636             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30637             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30638         }
30639         return this.tabs;
30640     },
30641
30642     /**
30643      * Adds a button to the footer section of the dialog.
30644      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30645      * object or a valid Roo.DomHelper element config
30646      * @param {Function} handler The function called when the button is clicked
30647      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30648      * @return {Roo.Button} The new button
30649      */
30650     addButton : function(config, handler, scope){
30651         var dh = Roo.DomHelper;
30652         if(!this.footer){
30653             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30654         }
30655         if(!this.btnContainer){
30656             var tb = this.footer.createChild({
30657
30658                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30659                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30660             }, null, true);
30661             this.btnContainer = tb.firstChild.firstChild.firstChild;
30662         }
30663         var bconfig = {
30664             handler: handler,
30665             scope: scope,
30666             minWidth: this.minButtonWidth,
30667             hideParent:true
30668         };
30669         if(typeof config == "string"){
30670             bconfig.text = config;
30671         }else{
30672             if(config.tag){
30673                 bconfig.dhconfig = config;
30674             }else{
30675                 Roo.apply(bconfig, config);
30676             }
30677         }
30678         var fc = false;
30679         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30680             bconfig.position = Math.max(0, bconfig.position);
30681             fc = this.btnContainer.childNodes[bconfig.position];
30682         }
30683          
30684         var btn = new Roo.Button(
30685             fc ? 
30686                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30687                 : this.btnContainer.appendChild(document.createElement("td")),
30688             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30689             bconfig
30690         );
30691         this.syncBodyHeight();
30692         if(!this.buttons){
30693             /**
30694              * Array of all the buttons that have been added to this dialog via addButton
30695              * @type Array
30696              */
30697             this.buttons = [];
30698         }
30699         this.buttons.push(btn);
30700         return btn;
30701     },
30702
30703     /**
30704      * Sets the default button to be focused when the dialog is displayed.
30705      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30706      * @return {Roo.BasicDialog} this
30707      */
30708     setDefaultButton : function(btn){
30709         this.defaultButton = btn;
30710         return this;
30711     },
30712
30713     // private
30714     getHeaderFooterHeight : function(safe){
30715         var height = 0;
30716         if(this.header){
30717            height += this.header.getHeight();
30718         }
30719         if(this.footer){
30720            var fm = this.footer.getMargins();
30721             height += (this.footer.getHeight()+fm.top+fm.bottom);
30722         }
30723         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30724         height += this.centerBg.getPadding("tb");
30725         return height;
30726     },
30727
30728     // private
30729     syncBodyHeight : function()
30730     {
30731         var bd = this.body, // the text
30732             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30733             bw = this.bwrap;
30734         var height = this.size.height - this.getHeaderFooterHeight(false);
30735         bd.setHeight(height-bd.getMargins("tb"));
30736         var hh = this.header.getHeight();
30737         var h = this.size.height-hh;
30738         cb.setHeight(h);
30739         
30740         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30741         bw.setHeight(h-cb.getPadding("tb"));
30742         
30743         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30744         bd.setWidth(bw.getWidth(true));
30745         if(this.tabs){
30746             this.tabs.syncHeight();
30747             if(Roo.isIE){
30748                 this.tabs.el.repaint();
30749             }
30750         }
30751     },
30752
30753     /**
30754      * Restores the previous state of the dialog if Roo.state is configured.
30755      * @return {Roo.BasicDialog} this
30756      */
30757     restoreState : function(){
30758         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30759         if(box && box.width){
30760             this.xy = [box.x, box.y];
30761             this.resizeTo(box.width, box.height);
30762         }
30763         return this;
30764     },
30765
30766     // private
30767     beforeShow : function(){
30768         this.expand();
30769         if(this.fixedcenter){
30770             this.xy = this.el.getCenterXY(true);
30771         }
30772         if(this.modal){
30773             Roo.get(document.body).addClass("x-body-masked");
30774             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30775             this.mask.show();
30776         }
30777         this.constrainXY();
30778     },
30779
30780     // private
30781     animShow : function(){
30782         var b = Roo.get(this.animateTarget).getBox();
30783         this.proxy.setSize(b.width, b.height);
30784         this.proxy.setLocation(b.x, b.y);
30785         this.proxy.show();
30786         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30787                     true, .35, this.showEl.createDelegate(this));
30788     },
30789
30790     /**
30791      * Shows the dialog.
30792      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30793      * @return {Roo.BasicDialog} this
30794      */
30795     show : function(animateTarget){
30796         if (this.fireEvent("beforeshow", this) === false){
30797             return;
30798         }
30799         if(this.syncHeightBeforeShow){
30800             this.syncBodyHeight();
30801         }else if(this.firstShow){
30802             this.firstShow = false;
30803             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30804         }
30805         this.animateTarget = animateTarget || this.animateTarget;
30806         if(!this.el.isVisible()){
30807             this.beforeShow();
30808             if(this.animateTarget && Roo.get(this.animateTarget)){
30809                 this.animShow();
30810             }else{
30811                 this.showEl();
30812             }
30813         }
30814         return this;
30815     },
30816
30817     // private
30818     showEl : function(){
30819         this.proxy.hide();
30820         this.el.setXY(this.xy);
30821         this.el.show();
30822         this.adjustAssets(true);
30823         this.toFront();
30824         this.focus();
30825         // IE peekaboo bug - fix found by Dave Fenwick
30826         if(Roo.isIE){
30827             this.el.repaint();
30828         }
30829         this.fireEvent("show", this);
30830     },
30831
30832     /**
30833      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30834      * dialog itself will receive focus.
30835      */
30836     focus : function(){
30837         if(this.defaultButton){
30838             this.defaultButton.focus();
30839         }else{
30840             this.focusEl.focus();
30841         }
30842     },
30843
30844     // private
30845     constrainXY : function(){
30846         if(this.constraintoviewport !== false){
30847             if(!this.viewSize){
30848                 if(this.container){
30849                     var s = this.container.getSize();
30850                     this.viewSize = [s.width, s.height];
30851                 }else{
30852                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30853                 }
30854             }
30855             var s = Roo.get(this.container||document).getScroll();
30856
30857             var x = this.xy[0], y = this.xy[1];
30858             var w = this.size.width, h = this.size.height;
30859             var vw = this.viewSize[0], vh = this.viewSize[1];
30860             // only move it if it needs it
30861             var moved = false;
30862             // first validate right/bottom
30863             if(x + w > vw+s.left){
30864                 x = vw - w;
30865                 moved = true;
30866             }
30867             if(y + h > vh+s.top){
30868                 y = vh - h;
30869                 moved = true;
30870             }
30871             // then make sure top/left isn't negative
30872             if(x < s.left){
30873                 x = s.left;
30874                 moved = true;
30875             }
30876             if(y < s.top){
30877                 y = s.top;
30878                 moved = true;
30879             }
30880             if(moved){
30881                 // cache xy
30882                 this.xy = [x, y];
30883                 if(this.isVisible()){
30884                     this.el.setLocation(x, y);
30885                     this.adjustAssets();
30886                 }
30887             }
30888         }
30889     },
30890
30891     // private
30892     onDrag : function(){
30893         if(!this.proxyDrag){
30894             this.xy = this.el.getXY();
30895             this.adjustAssets();
30896         }
30897     },
30898
30899     // private
30900     adjustAssets : function(doShow){
30901         var x = this.xy[0], y = this.xy[1];
30902         var w = this.size.width, h = this.size.height;
30903         if(doShow === true){
30904             if(this.shadow){
30905                 this.shadow.show(this.el);
30906             }
30907             if(this.shim){
30908                 this.shim.show();
30909             }
30910         }
30911         if(this.shadow && this.shadow.isVisible()){
30912             this.shadow.show(this.el);
30913         }
30914         if(this.shim && this.shim.isVisible()){
30915             this.shim.setBounds(x, y, w, h);
30916         }
30917     },
30918
30919     // private
30920     adjustViewport : function(w, h){
30921         if(!w || !h){
30922             w = Roo.lib.Dom.getViewWidth();
30923             h = Roo.lib.Dom.getViewHeight();
30924         }
30925         // cache the size
30926         this.viewSize = [w, h];
30927         if(this.modal && this.mask.isVisible()){
30928             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30929             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30930         }
30931         if(this.isVisible()){
30932             this.constrainXY();
30933         }
30934     },
30935
30936     /**
30937      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30938      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30939      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30940      */
30941     destroy : function(removeEl){
30942         if(this.isVisible()){
30943             this.animateTarget = null;
30944             this.hide();
30945         }
30946         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30947         if(this.tabs){
30948             this.tabs.destroy(removeEl);
30949         }
30950         Roo.destroy(
30951              this.shim,
30952              this.proxy,
30953              this.resizer,
30954              this.close,
30955              this.mask
30956         );
30957         if(this.dd){
30958             this.dd.unreg();
30959         }
30960         if(this.buttons){
30961            for(var i = 0, len = this.buttons.length; i < len; i++){
30962                this.buttons[i].destroy();
30963            }
30964         }
30965         this.el.removeAllListeners();
30966         if(removeEl === true){
30967             this.el.update("");
30968             this.el.remove();
30969         }
30970         Roo.DialogManager.unregister(this);
30971     },
30972
30973     // private
30974     startMove : function(){
30975         if(this.proxyDrag){
30976             this.proxy.show();
30977         }
30978         if(this.constraintoviewport !== false){
30979             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30980         }
30981     },
30982
30983     // private
30984     endMove : function(){
30985         if(!this.proxyDrag){
30986             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30987         }else{
30988             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30989             this.proxy.hide();
30990         }
30991         this.refreshSize();
30992         this.adjustAssets();
30993         this.focus();
30994         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30995     },
30996
30997     /**
30998      * Brings this dialog to the front of any other visible dialogs
30999      * @return {Roo.BasicDialog} this
31000      */
31001     toFront : function(){
31002         Roo.DialogManager.bringToFront(this);
31003         return this;
31004     },
31005
31006     /**
31007      * Sends this dialog to the back (under) of any other visible dialogs
31008      * @return {Roo.BasicDialog} this
31009      */
31010     toBack : function(){
31011         Roo.DialogManager.sendToBack(this);
31012         return this;
31013     },
31014
31015     /**
31016      * Centers this dialog in the viewport
31017      * @return {Roo.BasicDialog} this
31018      */
31019     center : function(){
31020         var xy = this.el.getCenterXY(true);
31021         this.moveTo(xy[0], xy[1]);
31022         return this;
31023     },
31024
31025     /**
31026      * Moves the dialog's top-left corner to the specified point
31027      * @param {Number} x
31028      * @param {Number} y
31029      * @return {Roo.BasicDialog} this
31030      */
31031     moveTo : function(x, y){
31032         this.xy = [x,y];
31033         if(this.isVisible()){
31034             this.el.setXY(this.xy);
31035             this.adjustAssets();
31036         }
31037         return this;
31038     },
31039
31040     /**
31041      * Aligns the dialog to the specified element
31042      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31043      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
31044      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31045      * @return {Roo.BasicDialog} this
31046      */
31047     alignTo : function(element, position, offsets){
31048         this.xy = this.el.getAlignToXY(element, position, offsets);
31049         if(this.isVisible()){
31050             this.el.setXY(this.xy);
31051             this.adjustAssets();
31052         }
31053         return this;
31054     },
31055
31056     /**
31057      * Anchors an element to another element and realigns it when the window is resized.
31058      * @param {String/HTMLElement/Roo.Element} element The element to align to.
31059      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
31060      * @param {Array} offsets (optional) Offset the positioning by [x, y]
31061      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
31062      * is a number, it is used as the buffer delay (defaults to 50ms).
31063      * @return {Roo.BasicDialog} this
31064      */
31065     anchorTo : function(el, alignment, offsets, monitorScroll){
31066         var action = function(){
31067             this.alignTo(el, alignment, offsets);
31068         };
31069         Roo.EventManager.onWindowResize(action, this);
31070         var tm = typeof monitorScroll;
31071         if(tm != 'undefined'){
31072             Roo.EventManager.on(window, 'scroll', action, this,
31073                 {buffer: tm == 'number' ? monitorScroll : 50});
31074         }
31075         action.call(this);
31076         return this;
31077     },
31078
31079     /**
31080      * Returns true if the dialog is visible
31081      * @return {Boolean}
31082      */
31083     isVisible : function(){
31084         return this.el.isVisible();
31085     },
31086
31087     // private
31088     animHide : function(callback){
31089         var b = Roo.get(this.animateTarget).getBox();
31090         this.proxy.show();
31091         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31092         this.el.hide();
31093         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31094                     this.hideEl.createDelegate(this, [callback]));
31095     },
31096
31097     /**
31098      * Hides the dialog.
31099      * @param {Function} callback (optional) Function to call when the dialog is hidden
31100      * @return {Roo.BasicDialog} this
31101      */
31102     hide : function(callback){
31103         if (this.fireEvent("beforehide", this) === false){
31104             return;
31105         }
31106         if(this.shadow){
31107             this.shadow.hide();
31108         }
31109         if(this.shim) {
31110           this.shim.hide();
31111         }
31112         // sometimes animateTarget seems to get set.. causing problems...
31113         // this just double checks..
31114         if(this.animateTarget && Roo.get(this.animateTarget)) {
31115            this.animHide(callback);
31116         }else{
31117             this.el.hide();
31118             this.hideEl(callback);
31119         }
31120         return this;
31121     },
31122
31123     // private
31124     hideEl : function(callback){
31125         this.proxy.hide();
31126         if(this.modal){
31127             this.mask.hide();
31128             Roo.get(document.body).removeClass("x-body-masked");
31129         }
31130         this.fireEvent("hide", this);
31131         if(typeof callback == "function"){
31132             callback();
31133         }
31134     },
31135
31136     // private
31137     hideAction : function(){
31138         this.setLeft("-10000px");
31139         this.setTop("-10000px");
31140         this.setStyle("visibility", "hidden");
31141     },
31142
31143     // private
31144     refreshSize : function(){
31145         this.size = this.el.getSize();
31146         this.xy = this.el.getXY();
31147         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31148     },
31149
31150     // private
31151     // z-index is managed by the DialogManager and may be overwritten at any time
31152     setZIndex : function(index){
31153         if(this.modal){
31154             this.mask.setStyle("z-index", index);
31155         }
31156         if(this.shim){
31157             this.shim.setStyle("z-index", ++index);
31158         }
31159         if(this.shadow){
31160             this.shadow.setZIndex(++index);
31161         }
31162         this.el.setStyle("z-index", ++index);
31163         if(this.proxy){
31164             this.proxy.setStyle("z-index", ++index);
31165         }
31166         if(this.resizer){
31167             this.resizer.proxy.setStyle("z-index", ++index);
31168         }
31169
31170         this.lastZIndex = index;
31171     },
31172
31173     /**
31174      * Returns the element for this dialog
31175      * @return {Roo.Element} The underlying dialog Element
31176      */
31177     getEl : function(){
31178         return this.el;
31179     }
31180 });
31181
31182 /**
31183  * @class Roo.DialogManager
31184  * Provides global access to BasicDialogs that have been created and
31185  * support for z-indexing (layering) multiple open dialogs.
31186  */
31187 Roo.DialogManager = function(){
31188     var list = {};
31189     var accessList = [];
31190     var front = null;
31191
31192     // private
31193     var sortDialogs = function(d1, d2){
31194         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31195     };
31196
31197     // private
31198     var orderDialogs = function(){
31199         accessList.sort(sortDialogs);
31200         var seed = Roo.DialogManager.zseed;
31201         for(var i = 0, len = accessList.length; i < len; i++){
31202             var dlg = accessList[i];
31203             if(dlg){
31204                 dlg.setZIndex(seed + (i*10));
31205             }
31206         }
31207     };
31208
31209     return {
31210         /**
31211          * The starting z-index for BasicDialogs (defaults to 9000)
31212          * @type Number The z-index value
31213          */
31214         zseed : 9000,
31215
31216         // private
31217         register : function(dlg){
31218             list[dlg.id] = dlg;
31219             accessList.push(dlg);
31220         },
31221
31222         // private
31223         unregister : function(dlg){
31224             delete list[dlg.id];
31225             var i=0;
31226             var len=0;
31227             if(!accessList.indexOf){
31228                 for(  i = 0, len = accessList.length; i < len; i++){
31229                     if(accessList[i] == dlg){
31230                         accessList.splice(i, 1);
31231                         return;
31232                     }
31233                 }
31234             }else{
31235                  i = accessList.indexOf(dlg);
31236                 if(i != -1){
31237                     accessList.splice(i, 1);
31238                 }
31239             }
31240         },
31241
31242         /**
31243          * Gets a registered dialog by id
31244          * @param {String/Object} id The id of the dialog or a dialog
31245          * @return {Roo.BasicDialog} this
31246          */
31247         get : function(id){
31248             return typeof id == "object" ? id : list[id];
31249         },
31250
31251         /**
31252          * Brings the specified dialog to the front
31253          * @param {String/Object} dlg The id of the dialog or a dialog
31254          * @return {Roo.BasicDialog} this
31255          */
31256         bringToFront : function(dlg){
31257             dlg = this.get(dlg);
31258             if(dlg != front){
31259                 front = dlg;
31260                 dlg._lastAccess = new Date().getTime();
31261                 orderDialogs();
31262             }
31263             return dlg;
31264         },
31265
31266         /**
31267          * Sends the specified dialog to the back
31268          * @param {String/Object} dlg The id of the dialog or a dialog
31269          * @return {Roo.BasicDialog} this
31270          */
31271         sendToBack : function(dlg){
31272             dlg = this.get(dlg);
31273             dlg._lastAccess = -(new Date().getTime());
31274             orderDialogs();
31275             return dlg;
31276         },
31277
31278         /**
31279          * Hides all dialogs
31280          */
31281         hideAll : function(){
31282             for(var id in list){
31283                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31284                     list[id].hide();
31285                 }
31286             }
31287         }
31288     };
31289 }();
31290
31291 /**
31292  * @class Roo.LayoutDialog
31293  * @extends Roo.BasicDialog
31294  * Dialog which provides adjustments for working with a layout in a Dialog.
31295  * Add your necessary layout config options to the dialog's config.<br>
31296  * Example usage (including a nested layout):
31297  * <pre><code>
31298 if(!dialog){
31299     dialog = new Roo.LayoutDialog("download-dlg", {
31300         modal: true,
31301         width:600,
31302         height:450,
31303         shadow:true,
31304         minWidth:500,
31305         minHeight:350,
31306         autoTabs:true,
31307         proxyDrag:true,
31308         // layout config merges with the dialog config
31309         center:{
31310             tabPosition: "top",
31311             alwaysShowTabs: true
31312         }
31313     });
31314     dialog.addKeyListener(27, dialog.hide, dialog);
31315     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31316     dialog.addButton("Build It!", this.getDownload, this);
31317
31318     // we can even add nested layouts
31319     var innerLayout = new Roo.BorderLayout("dl-inner", {
31320         east: {
31321             initialSize: 200,
31322             autoScroll:true,
31323             split:true
31324         },
31325         center: {
31326             autoScroll:true
31327         }
31328     });
31329     innerLayout.beginUpdate();
31330     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31331     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31332     innerLayout.endUpdate(true);
31333
31334     var layout = dialog.getLayout();
31335     layout.beginUpdate();
31336     layout.add("center", new Roo.ContentPanel("standard-panel",
31337                         {title: "Download the Source", fitToFrame:true}));
31338     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31339                {title: "Build your own roo.js"}));
31340     layout.getRegion("center").showPanel(sp);
31341     layout.endUpdate();
31342 }
31343 </code></pre>
31344     * @constructor
31345     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31346     * @param {Object} config configuration options
31347   */
31348 Roo.LayoutDialog = function(el, cfg){
31349     
31350     var config=  cfg;
31351     if (typeof(cfg) == 'undefined') {
31352         config = Roo.apply({}, el);
31353         // not sure why we use documentElement here.. - it should always be body.
31354         // IE7 borks horribly if we use documentElement.
31355         // webkit also does not like documentElement - it creates a body element...
31356         el = Roo.get( document.body || document.documentElement ).createChild();
31357         //config.autoCreate = true;
31358     }
31359     
31360     
31361     config.autoTabs = false;
31362     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31363     this.body.setStyle({overflow:"hidden", position:"relative"});
31364     this.layout = new Roo.BorderLayout(this.body.dom, config);
31365     this.layout.monitorWindowResize = false;
31366     this.el.addClass("x-dlg-auto-layout");
31367     // fix case when center region overwrites center function
31368     this.center = Roo.BasicDialog.prototype.center;
31369     this.on("show", this.layout.layout, this.layout, true);
31370     if (config.items) {
31371         var xitems = config.items;
31372         delete config.items;
31373         Roo.each(xitems, this.addxtype, this);
31374     }
31375     
31376     
31377 };
31378 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31379     /**
31380      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31381      * @deprecated
31382      */
31383     endUpdate : function(){
31384         this.layout.endUpdate();
31385     },
31386
31387     /**
31388      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31389      *  @deprecated
31390      */
31391     beginUpdate : function(){
31392         this.layout.beginUpdate();
31393     },
31394
31395     /**
31396      * Get the BorderLayout for this dialog
31397      * @return {Roo.BorderLayout}
31398      */
31399     getLayout : function(){
31400         return this.layout;
31401     },
31402
31403     showEl : function(){
31404         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31405         if(Roo.isIE7){
31406             this.layout.layout();
31407         }
31408     },
31409
31410     // private
31411     // Use the syncHeightBeforeShow config option to control this automatically
31412     syncBodyHeight : function(){
31413         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31414         if(this.layout){this.layout.layout();}
31415     },
31416     
31417       /**
31418      * Add an xtype element (actually adds to the layout.)
31419      * @return {Object} xdata xtype object data.
31420      */
31421     
31422     addxtype : function(c) {
31423         return this.layout.addxtype(c);
31424     }
31425 });/*
31426  * Based on:
31427  * Ext JS Library 1.1.1
31428  * Copyright(c) 2006-2007, Ext JS, LLC.
31429  *
31430  * Originally Released Under LGPL - original licence link has changed is not relivant.
31431  *
31432  * Fork - LGPL
31433  * <script type="text/javascript">
31434  */
31435  
31436 /**
31437  * @class Roo.MessageBox
31438  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31439  * Example usage:
31440  *<pre><code>
31441 // Basic alert:
31442 Roo.Msg.alert('Status', 'Changes saved successfully.');
31443
31444 // Prompt for user data:
31445 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31446     if (btn == 'ok'){
31447         // process text value...
31448     }
31449 });
31450
31451 // Show a dialog using config options:
31452 Roo.Msg.show({
31453    title:'Save Changes?',
31454    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31455    buttons: Roo.Msg.YESNOCANCEL,
31456    fn: processResult,
31457    animEl: 'elId'
31458 });
31459 </code></pre>
31460  * @singleton
31461  */
31462 Roo.MessageBox = function(){
31463     var dlg, opt, mask, waitTimer;
31464     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31465     var buttons, activeTextEl, bwidth;
31466
31467     // private
31468     var handleButton = function(button){
31469         dlg.hide();
31470         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31471     };
31472
31473     // private
31474     var handleHide = function(){
31475         if(opt && opt.cls){
31476             dlg.el.removeClass(opt.cls);
31477         }
31478         if(waitTimer){
31479             Roo.TaskMgr.stop(waitTimer);
31480             waitTimer = null;
31481         }
31482     };
31483
31484     // private
31485     var updateButtons = function(b){
31486         var width = 0;
31487         if(!b){
31488             buttons["ok"].hide();
31489             buttons["cancel"].hide();
31490             buttons["yes"].hide();
31491             buttons["no"].hide();
31492             dlg.footer.dom.style.display = 'none';
31493             return width;
31494         }
31495         dlg.footer.dom.style.display = '';
31496         for(var k in buttons){
31497             if(typeof buttons[k] != "function"){
31498                 if(b[k]){
31499                     buttons[k].show();
31500                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31501                     width += buttons[k].el.getWidth()+15;
31502                 }else{
31503                     buttons[k].hide();
31504                 }
31505             }
31506         }
31507         return width;
31508     };
31509
31510     // private
31511     var handleEsc = function(d, k, e){
31512         if(opt && opt.closable !== false){
31513             dlg.hide();
31514         }
31515         if(e){
31516             e.stopEvent();
31517         }
31518     };
31519
31520     return {
31521         /**
31522          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31523          * @return {Roo.BasicDialog} The BasicDialog element
31524          */
31525         getDialog : function(){
31526            if(!dlg){
31527                 dlg = new Roo.BasicDialog("x-msg-box", {
31528                     autoCreate : true,
31529                     shadow: true,
31530                     draggable: true,
31531                     resizable:false,
31532                     constraintoviewport:false,
31533                     fixedcenter:true,
31534                     collapsible : false,
31535                     shim:true,
31536                     modal: true,
31537                     width:400, height:100,
31538                     buttonAlign:"center",
31539                     closeClick : function(){
31540                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31541                             handleButton("no");
31542                         }else{
31543                             handleButton("cancel");
31544                         }
31545                     }
31546                 });
31547                 dlg.on("hide", handleHide);
31548                 mask = dlg.mask;
31549                 dlg.addKeyListener(27, handleEsc);
31550                 buttons = {};
31551                 var bt = this.buttonText;
31552                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31553                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31554                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31555                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31556                 bodyEl = dlg.body.createChild({
31557
31558                     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>'
31559                 });
31560                 msgEl = bodyEl.dom.firstChild;
31561                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31562                 textboxEl.enableDisplayMode();
31563                 textboxEl.addKeyListener([10,13], function(){
31564                     if(dlg.isVisible() && opt && opt.buttons){
31565                         if(opt.buttons.ok){
31566                             handleButton("ok");
31567                         }else if(opt.buttons.yes){
31568                             handleButton("yes");
31569                         }
31570                     }
31571                 });
31572                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31573                 textareaEl.enableDisplayMode();
31574                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31575                 progressEl.enableDisplayMode();
31576                 var pf = progressEl.dom.firstChild;
31577                 if (pf) {
31578                     pp = Roo.get(pf.firstChild);
31579                     pp.setHeight(pf.offsetHeight);
31580                 }
31581                 
31582             }
31583             return dlg;
31584         },
31585
31586         /**
31587          * Updates the message box body text
31588          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31589          * the XHTML-compliant non-breaking space character '&amp;#160;')
31590          * @return {Roo.MessageBox} This message box
31591          */
31592         updateText : function(text){
31593             if(!dlg.isVisible() && !opt.width){
31594                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31595             }
31596             msgEl.innerHTML = text || '&#160;';
31597       
31598             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31599             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31600             var w = Math.max(
31601                     Math.min(opt.width || cw , this.maxWidth), 
31602                     Math.max(opt.minWidth || this.minWidth, bwidth)
31603             );
31604             if(opt.prompt){
31605                 activeTextEl.setWidth(w);
31606             }
31607             if(dlg.isVisible()){
31608                 dlg.fixedcenter = false;
31609             }
31610             // to big, make it scroll. = But as usual stupid IE does not support
31611             // !important..
31612             
31613             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31614                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31615                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31616             } else {
31617                 bodyEl.dom.style.height = '';
31618                 bodyEl.dom.style.overflowY = '';
31619             }
31620             if (cw > w) {
31621                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31622             } else {
31623                 bodyEl.dom.style.overflowX = '';
31624             }
31625             
31626             dlg.setContentSize(w, bodyEl.getHeight());
31627             if(dlg.isVisible()){
31628                 dlg.fixedcenter = true;
31629             }
31630             return this;
31631         },
31632
31633         /**
31634          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31635          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31636          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31637          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31638          * @return {Roo.MessageBox} This message box
31639          */
31640         updateProgress : function(value, text){
31641             if(text){
31642                 this.updateText(text);
31643             }
31644             if (pp) { // weird bug on my firefox - for some reason this is not defined
31645                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31646             }
31647             return this;
31648         },        
31649
31650         /**
31651          * Returns true if the message box is currently displayed
31652          * @return {Boolean} True if the message box is visible, else false
31653          */
31654         isVisible : function(){
31655             return dlg && dlg.isVisible();  
31656         },
31657
31658         /**
31659          * Hides the message box if it is displayed
31660          */
31661         hide : function(){
31662             if(this.isVisible()){
31663                 dlg.hide();
31664             }  
31665         },
31666
31667         /**
31668          * Displays a new message box, or reinitializes an existing message box, based on the config options
31669          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31670          * The following config object properties are supported:
31671          * <pre>
31672 Property    Type             Description
31673 ----------  ---------------  ------------------------------------------------------------------------------------
31674 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31675                                    closes (defaults to undefined)
31676 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31677                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31678 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31679                                    progress and wait dialogs will ignore this property and always hide the
31680                                    close button as they can only be closed programmatically.
31681 cls               String           A custom CSS class to apply to the message box element
31682 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31683                                    displayed (defaults to 75)
31684 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31685                                    function will be btn (the name of the button that was clicked, if applicable,
31686                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31687                                    Progress and wait dialogs will ignore this option since they do not respond to
31688                                    user actions and can only be closed programmatically, so any required function
31689                                    should be called by the same code after it closes the dialog.
31690 icon              String           A CSS class that provides a background image to be used as an icon for
31691                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31692 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31693 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31694 modal             Boolean          False to allow user interaction with the page while the message box is
31695                                    displayed (defaults to true)
31696 msg               String           A string that will replace the existing message box body text (defaults
31697                                    to the XHTML-compliant non-breaking space character '&#160;')
31698 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31699 progress          Boolean          True to display a progress bar (defaults to false)
31700 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31701 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31702 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31703 title             String           The title text
31704 value             String           The string value to set into the active textbox element if displayed
31705 wait              Boolean          True to display a progress bar (defaults to false)
31706 width             Number           The width of the dialog in pixels
31707 </pre>
31708          *
31709          * Example usage:
31710          * <pre><code>
31711 Roo.Msg.show({
31712    title: 'Address',
31713    msg: 'Please enter your address:',
31714    width: 300,
31715    buttons: Roo.MessageBox.OKCANCEL,
31716    multiline: true,
31717    fn: saveAddress,
31718    animEl: 'addAddressBtn'
31719 });
31720 </code></pre>
31721          * @param {Object} config Configuration options
31722          * @return {Roo.MessageBox} This message box
31723          */
31724         show : function(options)
31725         {
31726             
31727             // this causes nightmares if you show one dialog after another
31728             // especially on callbacks..
31729              
31730             if(this.isVisible()){
31731                 
31732                 this.hide();
31733                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31734                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31735                 Roo.log("New Dialog Message:" +  options.msg )
31736                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31737                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31738                 
31739             }
31740             var d = this.getDialog();
31741             opt = options;
31742             d.setTitle(opt.title || "&#160;");
31743             d.close.setDisplayed(opt.closable !== false);
31744             activeTextEl = textboxEl;
31745             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31746             if(opt.prompt){
31747                 if(opt.multiline){
31748                     textboxEl.hide();
31749                     textareaEl.show();
31750                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31751                         opt.multiline : this.defaultTextHeight);
31752                     activeTextEl = textareaEl;
31753                 }else{
31754                     textboxEl.show();
31755                     textareaEl.hide();
31756                 }
31757             }else{
31758                 textboxEl.hide();
31759                 textareaEl.hide();
31760             }
31761             progressEl.setDisplayed(opt.progress === true);
31762             this.updateProgress(0);
31763             activeTextEl.dom.value = opt.value || "";
31764             if(opt.prompt){
31765                 dlg.setDefaultButton(activeTextEl);
31766             }else{
31767                 var bs = opt.buttons;
31768                 var db = null;
31769                 if(bs && bs.ok){
31770                     db = buttons["ok"];
31771                 }else if(bs && bs.yes){
31772                     db = buttons["yes"];
31773                 }
31774                 dlg.setDefaultButton(db);
31775             }
31776             bwidth = updateButtons(opt.buttons);
31777             this.updateText(opt.msg);
31778             if(opt.cls){
31779                 d.el.addClass(opt.cls);
31780             }
31781             d.proxyDrag = opt.proxyDrag === true;
31782             d.modal = opt.modal !== false;
31783             d.mask = opt.modal !== false ? mask : false;
31784             if(!d.isVisible()){
31785                 // force it to the end of the z-index stack so it gets a cursor in FF
31786                 document.body.appendChild(dlg.el.dom);
31787                 d.animateTarget = null;
31788                 d.show(options.animEl);
31789             }
31790             return this;
31791         },
31792
31793         /**
31794          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31795          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31796          * and closing the message box when the process is complete.
31797          * @param {String} title The title bar text
31798          * @param {String} msg The message box body text
31799          * @return {Roo.MessageBox} This message box
31800          */
31801         progress : function(title, msg){
31802             this.show({
31803                 title : title,
31804                 msg : msg,
31805                 buttons: false,
31806                 progress:true,
31807                 closable:false,
31808                 minWidth: this.minProgressWidth,
31809                 modal : true
31810             });
31811             return this;
31812         },
31813
31814         /**
31815          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31816          * If a callback function is passed it will be called after the user clicks the button, and the
31817          * id of the button that was clicked will be passed as the only parameter to the callback
31818          * (could also be the top-right close button).
31819          * @param {String} title The title bar text
31820          * @param {String} msg The message box body text
31821          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31822          * @param {Object} scope (optional) The scope of the callback function
31823          * @return {Roo.MessageBox} This message box
31824          */
31825         alert : function(title, msg, fn, scope){
31826             this.show({
31827                 title : title,
31828                 msg : msg,
31829                 buttons: this.OK,
31830                 fn: fn,
31831                 scope : scope,
31832                 modal : true
31833             });
31834             return this;
31835         },
31836
31837         /**
31838          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31839          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31840          * You are responsible for closing the message box when the process is complete.
31841          * @param {String} msg The message box body text
31842          * @param {String} title (optional) The title bar text
31843          * @return {Roo.MessageBox} This message box
31844          */
31845         wait : function(msg, title){
31846             this.show({
31847                 title : title,
31848                 msg : msg,
31849                 buttons: false,
31850                 closable:false,
31851                 progress:true,
31852                 modal:true,
31853                 width:300,
31854                 wait:true
31855             });
31856             waitTimer = Roo.TaskMgr.start({
31857                 run: function(i){
31858                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31859                 },
31860                 interval: 1000
31861             });
31862             return this;
31863         },
31864
31865         /**
31866          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31867          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31868          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31869          * @param {String} title The title bar text
31870          * @param {String} msg The message box body text
31871          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31872          * @param {Object} scope (optional) The scope of the callback function
31873          * @return {Roo.MessageBox} This message box
31874          */
31875         confirm : function(title, msg, fn, scope){
31876             this.show({
31877                 title : title,
31878                 msg : msg,
31879                 buttons: this.YESNO,
31880                 fn: fn,
31881                 scope : scope,
31882                 modal : true
31883             });
31884             return this;
31885         },
31886
31887         /**
31888          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31889          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31890          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31891          * (could also be the top-right close button) and the text that was entered will be passed as the two
31892          * parameters to the callback.
31893          * @param {String} title The title bar text
31894          * @param {String} msg The message box body text
31895          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31896          * @param {Object} scope (optional) The scope of the callback function
31897          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31898          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31899          * @return {Roo.MessageBox} This message box
31900          */
31901         prompt : function(title, msg, fn, scope, multiline){
31902             this.show({
31903                 title : title,
31904                 msg : msg,
31905                 buttons: this.OKCANCEL,
31906                 fn: fn,
31907                 minWidth:250,
31908                 scope : scope,
31909                 prompt:true,
31910                 multiline: multiline,
31911                 modal : true
31912             });
31913             return this;
31914         },
31915
31916         /**
31917          * Button config that displays a single OK button
31918          * @type Object
31919          */
31920         OK : {ok:true},
31921         /**
31922          * Button config that displays Yes and No buttons
31923          * @type Object
31924          */
31925         YESNO : {yes:true, no:true},
31926         /**
31927          * Button config that displays OK and Cancel buttons
31928          * @type Object
31929          */
31930         OKCANCEL : {ok:true, cancel:true},
31931         /**
31932          * Button config that displays Yes, No and Cancel buttons
31933          * @type Object
31934          */
31935         YESNOCANCEL : {yes:true, no:true, cancel:true},
31936
31937         /**
31938          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31939          * @type Number
31940          */
31941         defaultTextHeight : 75,
31942         /**
31943          * The maximum width in pixels of the message box (defaults to 600)
31944          * @type Number
31945          */
31946         maxWidth : 600,
31947         /**
31948          * The minimum width in pixels of the message box (defaults to 100)
31949          * @type Number
31950          */
31951         minWidth : 100,
31952         /**
31953          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31954          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31955          * @type Number
31956          */
31957         minProgressWidth : 250,
31958         /**
31959          * An object containing the default button text strings that can be overriden for localized language support.
31960          * Supported properties are: ok, cancel, yes and no.
31961          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31962          * @type Object
31963          */
31964         buttonText : {
31965             ok : "OK",
31966             cancel : "Cancel",
31967             yes : "Yes",
31968             no : "No"
31969         }
31970     };
31971 }();
31972
31973 /**
31974  * Shorthand for {@link Roo.MessageBox}
31975  */
31976 Roo.Msg = Roo.MessageBox;/*
31977  * Based on:
31978  * Ext JS Library 1.1.1
31979  * Copyright(c) 2006-2007, Ext JS, LLC.
31980  *
31981  * Originally Released Under LGPL - original licence link has changed is not relivant.
31982  *
31983  * Fork - LGPL
31984  * <script type="text/javascript">
31985  */
31986 /**
31987  * @class Roo.QuickTips
31988  * Provides attractive and customizable tooltips for any element.
31989  * @singleton
31990  */
31991 Roo.QuickTips = function(){
31992     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31993     var ce, bd, xy, dd;
31994     var visible = false, disabled = true, inited = false;
31995     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31996     
31997     var onOver = function(e){
31998         if(disabled){
31999             return;
32000         }
32001         var t = e.getTarget();
32002         if(!t || t.nodeType !== 1 || t == document || t == document.body){
32003             return;
32004         }
32005         if(ce && t == ce.el){
32006             clearTimeout(hideProc);
32007             return;
32008         }
32009         if(t && tagEls[t.id]){
32010             tagEls[t.id].el = t;
32011             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
32012             return;
32013         }
32014         var ttp, et = Roo.fly(t);
32015         var ns = cfg.namespace;
32016         if(tm.interceptTitles && t.title){
32017             ttp = t.title;
32018             t.qtip = ttp;
32019             t.removeAttribute("title");
32020             e.preventDefault();
32021         }else{
32022             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
32023         }
32024         if(ttp){
32025             showProc = show.defer(tm.showDelay, tm, [{
32026                 el: t, 
32027                 text: ttp, 
32028                 width: et.getAttributeNS(ns, cfg.width),
32029                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
32030                 title: et.getAttributeNS(ns, cfg.title),
32031                     cls: et.getAttributeNS(ns, cfg.cls)
32032             }]);
32033         }
32034     };
32035     
32036     var onOut = function(e){
32037         clearTimeout(showProc);
32038         var t = e.getTarget();
32039         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
32040             hideProc = setTimeout(hide, tm.hideDelay);
32041         }
32042     };
32043     
32044     var onMove = function(e){
32045         if(disabled){
32046             return;
32047         }
32048         xy = e.getXY();
32049         xy[1] += 18;
32050         if(tm.trackMouse && ce){
32051             el.setXY(xy);
32052         }
32053     };
32054     
32055     var onDown = function(e){
32056         clearTimeout(showProc);
32057         clearTimeout(hideProc);
32058         if(!e.within(el)){
32059             if(tm.hideOnClick){
32060                 hide();
32061                 tm.disable();
32062                 tm.enable.defer(100, tm);
32063             }
32064         }
32065     };
32066     
32067     var getPad = function(){
32068         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32069     };
32070
32071     var show = function(o){
32072         if(disabled){
32073             return;
32074         }
32075         clearTimeout(dismissProc);
32076         ce = o;
32077         if(removeCls){ // in case manually hidden
32078             el.removeClass(removeCls);
32079             removeCls = null;
32080         }
32081         if(ce.cls){
32082             el.addClass(ce.cls);
32083             removeCls = ce.cls;
32084         }
32085         if(ce.title){
32086             tipTitle.update(ce.title);
32087             tipTitle.show();
32088         }else{
32089             tipTitle.update('');
32090             tipTitle.hide();
32091         }
32092         el.dom.style.width  = tm.maxWidth+'px';
32093         //tipBody.dom.style.width = '';
32094         tipBodyText.update(o.text);
32095         var p = getPad(), w = ce.width;
32096         if(!w){
32097             var td = tipBodyText.dom;
32098             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32099             if(aw > tm.maxWidth){
32100                 w = tm.maxWidth;
32101             }else if(aw < tm.minWidth){
32102                 w = tm.minWidth;
32103             }else{
32104                 w = aw;
32105             }
32106         }
32107         //tipBody.setWidth(w);
32108         el.setWidth(parseInt(w, 10) + p);
32109         if(ce.autoHide === false){
32110             close.setDisplayed(true);
32111             if(dd){
32112                 dd.unlock();
32113             }
32114         }else{
32115             close.setDisplayed(false);
32116             if(dd){
32117                 dd.lock();
32118             }
32119         }
32120         if(xy){
32121             el.avoidY = xy[1]-18;
32122             el.setXY(xy);
32123         }
32124         if(tm.animate){
32125             el.setOpacity(.1);
32126             el.setStyle("visibility", "visible");
32127             el.fadeIn({callback: afterShow});
32128         }else{
32129             afterShow();
32130         }
32131     };
32132     
32133     var afterShow = function(){
32134         if(ce){
32135             el.show();
32136             esc.enable();
32137             if(tm.autoDismiss && ce.autoHide !== false){
32138                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32139             }
32140         }
32141     };
32142     
32143     var hide = function(noanim){
32144         clearTimeout(dismissProc);
32145         clearTimeout(hideProc);
32146         ce = null;
32147         if(el.isVisible()){
32148             esc.disable();
32149             if(noanim !== true && tm.animate){
32150                 el.fadeOut({callback: afterHide});
32151             }else{
32152                 afterHide();
32153             } 
32154         }
32155     };
32156     
32157     var afterHide = function(){
32158         el.hide();
32159         if(removeCls){
32160             el.removeClass(removeCls);
32161             removeCls = null;
32162         }
32163     };
32164     
32165     return {
32166         /**
32167         * @cfg {Number} minWidth
32168         * The minimum width of the quick tip (defaults to 40)
32169         */
32170        minWidth : 40,
32171         /**
32172         * @cfg {Number} maxWidth
32173         * The maximum width of the quick tip (defaults to 300)
32174         */
32175        maxWidth : 300,
32176         /**
32177         * @cfg {Boolean} interceptTitles
32178         * True to automatically use the element's DOM title value if available (defaults to false)
32179         */
32180        interceptTitles : false,
32181         /**
32182         * @cfg {Boolean} trackMouse
32183         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32184         */
32185        trackMouse : false,
32186         /**
32187         * @cfg {Boolean} hideOnClick
32188         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32189         */
32190        hideOnClick : true,
32191         /**
32192         * @cfg {Number} showDelay
32193         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32194         */
32195        showDelay : 500,
32196         /**
32197         * @cfg {Number} hideDelay
32198         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32199         */
32200        hideDelay : 200,
32201         /**
32202         * @cfg {Boolean} autoHide
32203         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32204         * Used in conjunction with hideDelay.
32205         */
32206        autoHide : true,
32207         /**
32208         * @cfg {Boolean}
32209         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32210         * (defaults to true).  Used in conjunction with autoDismissDelay.
32211         */
32212        autoDismiss : true,
32213         /**
32214         * @cfg {Number}
32215         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32216         */
32217        autoDismissDelay : 5000,
32218        /**
32219         * @cfg {Boolean} animate
32220         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32221         */
32222        animate : false,
32223
32224        /**
32225         * @cfg {String} title
32226         * Title text to display (defaults to '').  This can be any valid HTML markup.
32227         */
32228         title: '',
32229        /**
32230         * @cfg {String} text
32231         * Body text to display (defaults to '').  This can be any valid HTML markup.
32232         */
32233         text : '',
32234        /**
32235         * @cfg {String} cls
32236         * A CSS class to apply to the base quick tip element (defaults to '').
32237         */
32238         cls : '',
32239        /**
32240         * @cfg {Number} width
32241         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32242         * minWidth or maxWidth.
32243         */
32244         width : null,
32245
32246     /**
32247      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32248      * or display QuickTips in a page.
32249      */
32250        init : function(){
32251           tm = Roo.QuickTips;
32252           cfg = tm.tagConfig;
32253           if(!inited){
32254               if(!Roo.isReady){ // allow calling of init() before onReady
32255                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32256                   return;
32257               }
32258               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32259               el.fxDefaults = {stopFx: true};
32260               // maximum custom styling
32261               //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>');
32262               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>');              
32263               tipTitle = el.child('h3');
32264               tipTitle.enableDisplayMode("block");
32265               tipBody = el.child('div.x-tip-bd');
32266               tipBodyText = el.child('div.x-tip-bd-inner');
32267               //bdLeft = el.child('div.x-tip-bd-left');
32268               //bdRight = el.child('div.x-tip-bd-right');
32269               close = el.child('div.x-tip-close');
32270               close.enableDisplayMode("block");
32271               close.on("click", hide);
32272               var d = Roo.get(document);
32273               d.on("mousedown", onDown);
32274               d.on("mouseover", onOver);
32275               d.on("mouseout", onOut);
32276               d.on("mousemove", onMove);
32277               esc = d.addKeyListener(27, hide);
32278               esc.disable();
32279               if(Roo.dd.DD){
32280                   dd = el.initDD("default", null, {
32281                       onDrag : function(){
32282                           el.sync();  
32283                       }
32284                   });
32285                   dd.setHandleElId(tipTitle.id);
32286                   dd.lock();
32287               }
32288               inited = true;
32289           }
32290           this.enable(); 
32291        },
32292
32293     /**
32294      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32295      * are supported:
32296      * <pre>
32297 Property    Type                   Description
32298 ----------  ---------------------  ------------------------------------------------------------------------
32299 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32300      * </ul>
32301      * @param {Object} config The config object
32302      */
32303        register : function(config){
32304            var cs = config instanceof Array ? config : arguments;
32305            for(var i = 0, len = cs.length; i < len; i++) {
32306                var c = cs[i];
32307                var target = c.target;
32308                if(target){
32309                    if(target instanceof Array){
32310                        for(var j = 0, jlen = target.length; j < jlen; j++){
32311                            tagEls[target[j]] = c;
32312                        }
32313                    }else{
32314                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32315                    }
32316                }
32317            }
32318        },
32319
32320     /**
32321      * Removes this quick tip from its element and destroys it.
32322      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32323      */
32324        unregister : function(el){
32325            delete tagEls[Roo.id(el)];
32326        },
32327
32328     /**
32329      * Enable this quick tip.
32330      */
32331        enable : function(){
32332            if(inited && disabled){
32333                locks.pop();
32334                if(locks.length < 1){
32335                    disabled = false;
32336                }
32337            }
32338        },
32339
32340     /**
32341      * Disable this quick tip.
32342      */
32343        disable : function(){
32344           disabled = true;
32345           clearTimeout(showProc);
32346           clearTimeout(hideProc);
32347           clearTimeout(dismissProc);
32348           if(ce){
32349               hide(true);
32350           }
32351           locks.push(1);
32352        },
32353
32354     /**
32355      * Returns true if the quick tip is enabled, else false.
32356      */
32357        isEnabled : function(){
32358             return !disabled;
32359        },
32360
32361         // private
32362        tagConfig : {
32363            namespace : "ext",
32364            attribute : "qtip",
32365            width : "width",
32366            target : "target",
32367            title : "qtitle",
32368            hide : "hide",
32369            cls : "qclass"
32370        }
32371    };
32372 }();
32373
32374 // backwards compat
32375 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32376  * Based on:
32377  * Ext JS Library 1.1.1
32378  * Copyright(c) 2006-2007, Ext JS, LLC.
32379  *
32380  * Originally Released Under LGPL - original licence link has changed is not relivant.
32381  *
32382  * Fork - LGPL
32383  * <script type="text/javascript">
32384  */
32385  
32386
32387 /**
32388  * @class Roo.tree.TreePanel
32389  * @extends Roo.data.Tree
32390
32391  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32392  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32393  * @cfg {Boolean} enableDD true to enable drag and drop
32394  * @cfg {Boolean} enableDrag true to enable just drag
32395  * @cfg {Boolean} enableDrop true to enable just drop
32396  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32397  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32398  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32399  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32400  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32401  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32402  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32403  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32404  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32405  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32406  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32407  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32408  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32409  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32410  * @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>
32411  * @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>
32412  * 
32413  * @constructor
32414  * @param {String/HTMLElement/Element} el The container element
32415  * @param {Object} config
32416  */
32417 Roo.tree.TreePanel = function(el, config){
32418     var root = false;
32419     var loader = false;
32420     if (config.root) {
32421         root = config.root;
32422         delete config.root;
32423     }
32424     if (config.loader) {
32425         loader = config.loader;
32426         delete config.loader;
32427     }
32428     
32429     Roo.apply(this, config);
32430     Roo.tree.TreePanel.superclass.constructor.call(this);
32431     this.el = Roo.get(el);
32432     this.el.addClass('x-tree');
32433     //console.log(root);
32434     if (root) {
32435         this.setRootNode( Roo.factory(root, Roo.tree));
32436     }
32437     if (loader) {
32438         this.loader = Roo.factory(loader, Roo.tree);
32439     }
32440    /**
32441     * Read-only. The id of the container element becomes this TreePanel's id.
32442     */
32443     this.id = this.el.id;
32444     this.addEvents({
32445         /**
32446         * @event beforeload
32447         * Fires before a node is loaded, return false to cancel
32448         * @param {Node} node The node being loaded
32449         */
32450         "beforeload" : true,
32451         /**
32452         * @event load
32453         * Fires when a node is loaded
32454         * @param {Node} node The node that was loaded
32455         */
32456         "load" : true,
32457         /**
32458         * @event textchange
32459         * Fires when the text for a node is changed
32460         * @param {Node} node The node
32461         * @param {String} text The new text
32462         * @param {String} oldText The old text
32463         */
32464         "textchange" : true,
32465         /**
32466         * @event beforeexpand
32467         * Fires before a node is expanded, return false to cancel.
32468         * @param {Node} node The node
32469         * @param {Boolean} deep
32470         * @param {Boolean} anim
32471         */
32472         "beforeexpand" : true,
32473         /**
32474         * @event beforecollapse
32475         * Fires before a node is collapsed, return false to cancel.
32476         * @param {Node} node The node
32477         * @param {Boolean} deep
32478         * @param {Boolean} anim
32479         */
32480         "beforecollapse" : true,
32481         /**
32482         * @event expand
32483         * Fires when a node is expanded
32484         * @param {Node} node The node
32485         */
32486         "expand" : true,
32487         /**
32488         * @event disabledchange
32489         * Fires when the disabled status of a node changes
32490         * @param {Node} node The node
32491         * @param {Boolean} disabled
32492         */
32493         "disabledchange" : true,
32494         /**
32495         * @event collapse
32496         * Fires when a node is collapsed
32497         * @param {Node} node The node
32498         */
32499         "collapse" : true,
32500         /**
32501         * @event beforeclick
32502         * Fires before click processing on a node. Return false to cancel the default action.
32503         * @param {Node} node The node
32504         * @param {Roo.EventObject} e The event object
32505         */
32506         "beforeclick":true,
32507         /**
32508         * @event checkchange
32509         * Fires when a node with a checkbox's checked property changes
32510         * @param {Node} this This node
32511         * @param {Boolean} checked
32512         */
32513         "checkchange":true,
32514         /**
32515         * @event click
32516         * Fires when a node is clicked
32517         * @param {Node} node The node
32518         * @param {Roo.EventObject} e The event object
32519         */
32520         "click":true,
32521         /**
32522         * @event dblclick
32523         * Fires when a node is double clicked
32524         * @param {Node} node The node
32525         * @param {Roo.EventObject} e The event object
32526         */
32527         "dblclick":true,
32528         /**
32529         * @event contextmenu
32530         * Fires when a node is right clicked
32531         * @param {Node} node The node
32532         * @param {Roo.EventObject} e The event object
32533         */
32534         "contextmenu":true,
32535         /**
32536         * @event beforechildrenrendered
32537         * Fires right before the child nodes for a node are rendered
32538         * @param {Node} node The node
32539         */
32540         "beforechildrenrendered":true,
32541         /**
32542         * @event startdrag
32543         * Fires when a node starts being dragged
32544         * @param {Roo.tree.TreePanel} this
32545         * @param {Roo.tree.TreeNode} node
32546         * @param {event} e The raw browser event
32547         */ 
32548        "startdrag" : true,
32549        /**
32550         * @event enddrag
32551         * Fires when a drag operation is complete
32552         * @param {Roo.tree.TreePanel} this
32553         * @param {Roo.tree.TreeNode} node
32554         * @param {event} e The raw browser event
32555         */
32556        "enddrag" : true,
32557        /**
32558         * @event dragdrop
32559         * Fires when a dragged node is dropped on a valid DD target
32560         * @param {Roo.tree.TreePanel} this
32561         * @param {Roo.tree.TreeNode} node
32562         * @param {DD} dd The dd it was dropped on
32563         * @param {event} e The raw browser event
32564         */
32565        "dragdrop" : true,
32566        /**
32567         * @event beforenodedrop
32568         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32569         * passed to handlers has the following properties:<br />
32570         * <ul style="padding:5px;padding-left:16px;">
32571         * <li>tree - The TreePanel</li>
32572         * <li>target - The node being targeted for the drop</li>
32573         * <li>data - The drag data from the drag source</li>
32574         * <li>point - The point of the drop - append, above or below</li>
32575         * <li>source - The drag source</li>
32576         * <li>rawEvent - Raw mouse event</li>
32577         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32578         * to be inserted by setting them on this object.</li>
32579         * <li>cancel - Set this to true to cancel the drop.</li>
32580         * </ul>
32581         * @param {Object} dropEvent
32582         */
32583        "beforenodedrop" : true,
32584        /**
32585         * @event nodedrop
32586         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32587         * passed to handlers has the following properties:<br />
32588         * <ul style="padding:5px;padding-left:16px;">
32589         * <li>tree - The TreePanel</li>
32590         * <li>target - The node being targeted for the drop</li>
32591         * <li>data - The drag data from the drag source</li>
32592         * <li>point - The point of the drop - append, above or below</li>
32593         * <li>source - The drag source</li>
32594         * <li>rawEvent - Raw mouse event</li>
32595         * <li>dropNode - Dropped node(s).</li>
32596         * </ul>
32597         * @param {Object} dropEvent
32598         */
32599        "nodedrop" : true,
32600         /**
32601         * @event nodedragover
32602         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32603         * passed to handlers has the following properties:<br />
32604         * <ul style="padding:5px;padding-left:16px;">
32605         * <li>tree - The TreePanel</li>
32606         * <li>target - The node being targeted for the drop</li>
32607         * <li>data - The drag data from the drag source</li>
32608         * <li>point - The point of the drop - append, above or below</li>
32609         * <li>source - The drag source</li>
32610         * <li>rawEvent - Raw mouse event</li>
32611         * <li>dropNode - Drop node(s) provided by the source.</li>
32612         * <li>cancel - Set this to true to signal drop not allowed.</li>
32613         * </ul>
32614         * @param {Object} dragOverEvent
32615         */
32616        "nodedragover" : true
32617         
32618     });
32619     if(this.singleExpand){
32620        this.on("beforeexpand", this.restrictExpand, this);
32621     }
32622     if (this.editor) {
32623         this.editor.tree = this;
32624         this.editor = Roo.factory(this.editor, Roo.tree);
32625     }
32626     
32627     if (this.selModel) {
32628         this.selModel = Roo.factory(this.selModel, Roo.tree);
32629     }
32630    
32631 };
32632 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32633     rootVisible : true,
32634     animate: Roo.enableFx,
32635     lines : true,
32636     enableDD : false,
32637     hlDrop : Roo.enableFx,
32638   
32639     renderer: false,
32640     
32641     rendererTip: false,
32642     // private
32643     restrictExpand : function(node){
32644         var p = node.parentNode;
32645         if(p){
32646             if(p.expandedChild && p.expandedChild.parentNode == p){
32647                 p.expandedChild.collapse();
32648             }
32649             p.expandedChild = node;
32650         }
32651     },
32652
32653     // private override
32654     setRootNode : function(node){
32655         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32656         if(!this.rootVisible){
32657             node.ui = new Roo.tree.RootTreeNodeUI(node);
32658         }
32659         return node;
32660     },
32661
32662     /**
32663      * Returns the container element for this TreePanel
32664      */
32665     getEl : function(){
32666         return this.el;
32667     },
32668
32669     /**
32670      * Returns the default TreeLoader for this TreePanel
32671      */
32672     getLoader : function(){
32673         return this.loader;
32674     },
32675
32676     /**
32677      * Expand all nodes
32678      */
32679     expandAll : function(){
32680         this.root.expand(true);
32681     },
32682
32683     /**
32684      * Collapse all nodes
32685      */
32686     collapseAll : function(){
32687         this.root.collapse(true);
32688     },
32689
32690     /**
32691      * Returns the selection model used by this TreePanel
32692      */
32693     getSelectionModel : function(){
32694         if(!this.selModel){
32695             this.selModel = new Roo.tree.DefaultSelectionModel();
32696         }
32697         return this.selModel;
32698     },
32699
32700     /**
32701      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32702      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32703      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32704      * @return {Array}
32705      */
32706     getChecked : function(a, startNode){
32707         startNode = startNode || this.root;
32708         var r = [];
32709         var f = function(){
32710             if(this.attributes.checked){
32711                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32712             }
32713         }
32714         startNode.cascade(f);
32715         return r;
32716     },
32717
32718     /**
32719      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32720      * @param {String} path
32721      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32722      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32723      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32724      */
32725     expandPath : function(path, attr, callback){
32726         attr = attr || "id";
32727         var keys = path.split(this.pathSeparator);
32728         var curNode = this.root;
32729         if(curNode.attributes[attr] != keys[1]){ // invalid root
32730             if(callback){
32731                 callback(false, null);
32732             }
32733             return;
32734         }
32735         var index = 1;
32736         var f = function(){
32737             if(++index == keys.length){
32738                 if(callback){
32739                     callback(true, curNode);
32740                 }
32741                 return;
32742             }
32743             var c = curNode.findChild(attr, keys[index]);
32744             if(!c){
32745                 if(callback){
32746                     callback(false, curNode);
32747                 }
32748                 return;
32749             }
32750             curNode = c;
32751             c.expand(false, false, f);
32752         };
32753         curNode.expand(false, false, f);
32754     },
32755
32756     /**
32757      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32758      * @param {String} path
32759      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32760      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32761      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32762      */
32763     selectPath : function(path, attr, callback){
32764         attr = attr || "id";
32765         var keys = path.split(this.pathSeparator);
32766         var v = keys.pop();
32767         if(keys.length > 0){
32768             var f = function(success, node){
32769                 if(success && node){
32770                     var n = node.findChild(attr, v);
32771                     if(n){
32772                         n.select();
32773                         if(callback){
32774                             callback(true, n);
32775                         }
32776                     }else if(callback){
32777                         callback(false, n);
32778                     }
32779                 }else{
32780                     if(callback){
32781                         callback(false, n);
32782                     }
32783                 }
32784             };
32785             this.expandPath(keys.join(this.pathSeparator), attr, f);
32786         }else{
32787             this.root.select();
32788             if(callback){
32789                 callback(true, this.root);
32790             }
32791         }
32792     },
32793
32794     getTreeEl : function(){
32795         return this.el;
32796     },
32797
32798     /**
32799      * Trigger rendering of this TreePanel
32800      */
32801     render : function(){
32802         if (this.innerCt) {
32803             return this; // stop it rendering more than once!!
32804         }
32805         
32806         this.innerCt = this.el.createChild({tag:"ul",
32807                cls:"x-tree-root-ct " +
32808                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32809
32810         if(this.containerScroll){
32811             Roo.dd.ScrollManager.register(this.el);
32812         }
32813         if((this.enableDD || this.enableDrop) && !this.dropZone){
32814            /**
32815             * The dropZone used by this tree if drop is enabled
32816             * @type Roo.tree.TreeDropZone
32817             */
32818              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32819                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32820            });
32821         }
32822         if((this.enableDD || this.enableDrag) && !this.dragZone){
32823            /**
32824             * The dragZone used by this tree if drag is enabled
32825             * @type Roo.tree.TreeDragZone
32826             */
32827             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32828                ddGroup: this.ddGroup || "TreeDD",
32829                scroll: this.ddScroll
32830            });
32831         }
32832         this.getSelectionModel().init(this);
32833         if (!this.root) {
32834             Roo.log("ROOT not set in tree");
32835             return this;
32836         }
32837         this.root.render();
32838         if(!this.rootVisible){
32839             this.root.renderChildren();
32840         }
32841         return this;
32842     }
32843 });/*
32844  * Based on:
32845  * Ext JS Library 1.1.1
32846  * Copyright(c) 2006-2007, Ext JS, LLC.
32847  *
32848  * Originally Released Under LGPL - original licence link has changed is not relivant.
32849  *
32850  * Fork - LGPL
32851  * <script type="text/javascript">
32852  */
32853  
32854
32855 /**
32856  * @class Roo.tree.DefaultSelectionModel
32857  * @extends Roo.util.Observable
32858  * The default single selection for a TreePanel.
32859  * @param {Object} cfg Configuration
32860  */
32861 Roo.tree.DefaultSelectionModel = function(cfg){
32862    this.selNode = null;
32863    
32864    
32865    
32866    this.addEvents({
32867        /**
32868         * @event selectionchange
32869         * Fires when the selected node changes
32870         * @param {DefaultSelectionModel} this
32871         * @param {TreeNode} node the new selection
32872         */
32873        "selectionchange" : true,
32874
32875        /**
32876         * @event beforeselect
32877         * Fires before the selected node changes, return false to cancel the change
32878         * @param {DefaultSelectionModel} this
32879         * @param {TreeNode} node the new selection
32880         * @param {TreeNode} node the old selection
32881         */
32882        "beforeselect" : true
32883    });
32884    
32885     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32886 };
32887
32888 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32889     init : function(tree){
32890         this.tree = tree;
32891         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32892         tree.on("click", this.onNodeClick, this);
32893     },
32894     
32895     onNodeClick : function(node, e){
32896         if (e.ctrlKey && this.selNode == node)  {
32897             this.unselect(node);
32898             return;
32899         }
32900         this.select(node);
32901     },
32902     
32903     /**
32904      * Select a node.
32905      * @param {TreeNode} node The node to select
32906      * @return {TreeNode} The selected node
32907      */
32908     select : function(node){
32909         var last = this.selNode;
32910         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32911             if(last){
32912                 last.ui.onSelectedChange(false);
32913             }
32914             this.selNode = node;
32915             node.ui.onSelectedChange(true);
32916             this.fireEvent("selectionchange", this, node, last);
32917         }
32918         return node;
32919     },
32920     
32921     /**
32922      * Deselect a node.
32923      * @param {TreeNode} node The node to unselect
32924      */
32925     unselect : function(node){
32926         if(this.selNode == node){
32927             this.clearSelections();
32928         }    
32929     },
32930     
32931     /**
32932      * Clear all selections
32933      */
32934     clearSelections : function(){
32935         var n = this.selNode;
32936         if(n){
32937             n.ui.onSelectedChange(false);
32938             this.selNode = null;
32939             this.fireEvent("selectionchange", this, null);
32940         }
32941         return n;
32942     },
32943     
32944     /**
32945      * Get the selected node
32946      * @return {TreeNode} The selected node
32947      */
32948     getSelectedNode : function(){
32949         return this.selNode;    
32950     },
32951     
32952     /**
32953      * Returns true if the node is selected
32954      * @param {TreeNode} node The node to check
32955      * @return {Boolean}
32956      */
32957     isSelected : function(node){
32958         return this.selNode == node;  
32959     },
32960
32961     /**
32962      * Selects the node above the selected node in the tree, intelligently walking the nodes
32963      * @return TreeNode The new selection
32964      */
32965     selectPrevious : function(){
32966         var s = this.selNode || this.lastSelNode;
32967         if(!s){
32968             return null;
32969         }
32970         var ps = s.previousSibling;
32971         if(ps){
32972             if(!ps.isExpanded() || ps.childNodes.length < 1){
32973                 return this.select(ps);
32974             } else{
32975                 var lc = ps.lastChild;
32976                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32977                     lc = lc.lastChild;
32978                 }
32979                 return this.select(lc);
32980             }
32981         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32982             return this.select(s.parentNode);
32983         }
32984         return null;
32985     },
32986
32987     /**
32988      * Selects the node above the selected node in the tree, intelligently walking the nodes
32989      * @return TreeNode The new selection
32990      */
32991     selectNext : function(){
32992         var s = this.selNode || this.lastSelNode;
32993         if(!s){
32994             return null;
32995         }
32996         if(s.firstChild && s.isExpanded()){
32997              return this.select(s.firstChild);
32998          }else if(s.nextSibling){
32999              return this.select(s.nextSibling);
33000          }else if(s.parentNode){
33001             var newS = null;
33002             s.parentNode.bubble(function(){
33003                 if(this.nextSibling){
33004                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
33005                     return false;
33006                 }
33007             });
33008             return newS;
33009          }
33010         return null;
33011     },
33012
33013     onKeyDown : function(e){
33014         var s = this.selNode || this.lastSelNode;
33015         // undesirable, but required
33016         var sm = this;
33017         if(!s){
33018             return;
33019         }
33020         var k = e.getKey();
33021         switch(k){
33022              case e.DOWN:
33023                  e.stopEvent();
33024                  this.selectNext();
33025              break;
33026              case e.UP:
33027                  e.stopEvent();
33028                  this.selectPrevious();
33029              break;
33030              case e.RIGHT:
33031                  e.preventDefault();
33032                  if(s.hasChildNodes()){
33033                      if(!s.isExpanded()){
33034                          s.expand();
33035                      }else if(s.firstChild){
33036                          this.select(s.firstChild, e);
33037                      }
33038                  }
33039              break;
33040              case e.LEFT:
33041                  e.preventDefault();
33042                  if(s.hasChildNodes() && s.isExpanded()){
33043                      s.collapse();
33044                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
33045                      this.select(s.parentNode, e);
33046                  }
33047              break;
33048         };
33049     }
33050 });
33051
33052 /**
33053  * @class Roo.tree.MultiSelectionModel
33054  * @extends Roo.util.Observable
33055  * Multi selection for a TreePanel.
33056  * @param {Object} cfg Configuration
33057  */
33058 Roo.tree.MultiSelectionModel = function(){
33059    this.selNodes = [];
33060    this.selMap = {};
33061    this.addEvents({
33062        /**
33063         * @event selectionchange
33064         * Fires when the selected nodes change
33065         * @param {MultiSelectionModel} this
33066         * @param {Array} nodes Array of the selected nodes
33067         */
33068        "selectionchange" : true
33069    });
33070    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33071    
33072 };
33073
33074 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33075     init : function(tree){
33076         this.tree = tree;
33077         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33078         tree.on("click", this.onNodeClick, this);
33079     },
33080     
33081     onNodeClick : function(node, e){
33082         this.select(node, e, e.ctrlKey);
33083     },
33084     
33085     /**
33086      * Select a node.
33087      * @param {TreeNode} node The node to select
33088      * @param {EventObject} e (optional) An event associated with the selection
33089      * @param {Boolean} keepExisting True to retain existing selections
33090      * @return {TreeNode} The selected node
33091      */
33092     select : function(node, e, keepExisting){
33093         if(keepExisting !== true){
33094             this.clearSelections(true);
33095         }
33096         if(this.isSelected(node)){
33097             this.lastSelNode = node;
33098             return node;
33099         }
33100         this.selNodes.push(node);
33101         this.selMap[node.id] = node;
33102         this.lastSelNode = node;
33103         node.ui.onSelectedChange(true);
33104         this.fireEvent("selectionchange", this, this.selNodes);
33105         return node;
33106     },
33107     
33108     /**
33109      * Deselect a node.
33110      * @param {TreeNode} node The node to unselect
33111      */
33112     unselect : function(node){
33113         if(this.selMap[node.id]){
33114             node.ui.onSelectedChange(false);
33115             var sn = this.selNodes;
33116             var index = -1;
33117             if(sn.indexOf){
33118                 index = sn.indexOf(node);
33119             }else{
33120                 for(var i = 0, len = sn.length; i < len; i++){
33121                     if(sn[i] == node){
33122                         index = i;
33123                         break;
33124                     }
33125                 }
33126             }
33127             if(index != -1){
33128                 this.selNodes.splice(index, 1);
33129             }
33130             delete this.selMap[node.id];
33131             this.fireEvent("selectionchange", this, this.selNodes);
33132         }
33133     },
33134     
33135     /**
33136      * Clear all selections
33137      */
33138     clearSelections : function(suppressEvent){
33139         var sn = this.selNodes;
33140         if(sn.length > 0){
33141             for(var i = 0, len = sn.length; i < len; i++){
33142                 sn[i].ui.onSelectedChange(false);
33143             }
33144             this.selNodes = [];
33145             this.selMap = {};
33146             if(suppressEvent !== true){
33147                 this.fireEvent("selectionchange", this, this.selNodes);
33148             }
33149         }
33150     },
33151     
33152     /**
33153      * Returns true if the node is selected
33154      * @param {TreeNode} node The node to check
33155      * @return {Boolean}
33156      */
33157     isSelected : function(node){
33158         return this.selMap[node.id] ? true : false;  
33159     },
33160     
33161     /**
33162      * Returns an array of the selected nodes
33163      * @return {Array}
33164      */
33165     getSelectedNodes : function(){
33166         return this.selNodes;    
33167     },
33168
33169     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33170
33171     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33172
33173     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33174 });/*
33175  * Based on:
33176  * Ext JS Library 1.1.1
33177  * Copyright(c) 2006-2007, Ext JS, LLC.
33178  *
33179  * Originally Released Under LGPL - original licence link has changed is not relivant.
33180  *
33181  * Fork - LGPL
33182  * <script type="text/javascript">
33183  */
33184  
33185 /**
33186  * @class Roo.tree.TreeNode
33187  * @extends Roo.data.Node
33188  * @cfg {String} text The text for this node
33189  * @cfg {Boolean} expanded true to start the node expanded
33190  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33191  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33192  * @cfg {Boolean} disabled true to start the node disabled
33193  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33194  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33195  * @cfg {String} cls A css class to be added to the node
33196  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33197  * @cfg {String} href URL of the link used for the node (defaults to #)
33198  * @cfg {String} hrefTarget target frame for the link
33199  * @cfg {String} qtip An Ext QuickTip for the node
33200  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33201  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33202  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33203  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33204  * (defaults to undefined with no checkbox rendered)
33205  * @constructor
33206  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33207  */
33208 Roo.tree.TreeNode = function(attributes){
33209     attributes = attributes || {};
33210     if(typeof attributes == "string"){
33211         attributes = {text: attributes};
33212     }
33213     this.childrenRendered = false;
33214     this.rendered = false;
33215     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33216     this.expanded = attributes.expanded === true;
33217     this.isTarget = attributes.isTarget !== false;
33218     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33219     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33220
33221     /**
33222      * Read-only. The text for this node. To change it use setText().
33223      * @type String
33224      */
33225     this.text = attributes.text;
33226     /**
33227      * True if this node is disabled.
33228      * @type Boolean
33229      */
33230     this.disabled = attributes.disabled === true;
33231
33232     this.addEvents({
33233         /**
33234         * @event textchange
33235         * Fires when the text for this node is changed
33236         * @param {Node} this This node
33237         * @param {String} text The new text
33238         * @param {String} oldText The old text
33239         */
33240         "textchange" : true,
33241         /**
33242         * @event beforeexpand
33243         * Fires before this node is expanded, return false to cancel.
33244         * @param {Node} this This node
33245         * @param {Boolean} deep
33246         * @param {Boolean} anim
33247         */
33248         "beforeexpand" : true,
33249         /**
33250         * @event beforecollapse
33251         * Fires before this node is collapsed, return false to cancel.
33252         * @param {Node} this This node
33253         * @param {Boolean} deep
33254         * @param {Boolean} anim
33255         */
33256         "beforecollapse" : true,
33257         /**
33258         * @event expand
33259         * Fires when this node is expanded
33260         * @param {Node} this This node
33261         */
33262         "expand" : true,
33263         /**
33264         * @event disabledchange
33265         * Fires when the disabled status of this node changes
33266         * @param {Node} this This node
33267         * @param {Boolean} disabled
33268         */
33269         "disabledchange" : true,
33270         /**
33271         * @event collapse
33272         * Fires when this node is collapsed
33273         * @param {Node} this This node
33274         */
33275         "collapse" : true,
33276         /**
33277         * @event beforeclick
33278         * Fires before click processing. Return false to cancel the default action.
33279         * @param {Node} this This node
33280         * @param {Roo.EventObject} e The event object
33281         */
33282         "beforeclick":true,
33283         /**
33284         * @event checkchange
33285         * Fires when a node with a checkbox's checked property changes
33286         * @param {Node} this This node
33287         * @param {Boolean} checked
33288         */
33289         "checkchange":true,
33290         /**
33291         * @event click
33292         * Fires when this node is clicked
33293         * @param {Node} this This node
33294         * @param {Roo.EventObject} e The event object
33295         */
33296         "click":true,
33297         /**
33298         * @event dblclick
33299         * Fires when this node is double clicked
33300         * @param {Node} this This node
33301         * @param {Roo.EventObject} e The event object
33302         */
33303         "dblclick":true,
33304         /**
33305         * @event contextmenu
33306         * Fires when this node is right clicked
33307         * @param {Node} this This node
33308         * @param {Roo.EventObject} e The event object
33309         */
33310         "contextmenu":true,
33311         /**
33312         * @event beforechildrenrendered
33313         * Fires right before the child nodes for this node are rendered
33314         * @param {Node} this This node
33315         */
33316         "beforechildrenrendered":true
33317     });
33318
33319     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33320
33321     /**
33322      * Read-only. The UI for this node
33323      * @type TreeNodeUI
33324      */
33325     this.ui = new uiClass(this);
33326     
33327     // finally support items[]
33328     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33329         return;
33330     }
33331     
33332     
33333     Roo.each(this.attributes.items, function(c) {
33334         this.appendChild(Roo.factory(c,Roo.Tree));
33335     }, this);
33336     delete this.attributes.items;
33337     
33338     
33339     
33340 };
33341 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33342     preventHScroll: true,
33343     /**
33344      * Returns true if this node is expanded
33345      * @return {Boolean}
33346      */
33347     isExpanded : function(){
33348         return this.expanded;
33349     },
33350
33351     /**
33352      * Returns the UI object for this node
33353      * @return {TreeNodeUI}
33354      */
33355     getUI : function(){
33356         return this.ui;
33357     },
33358
33359     // private override
33360     setFirstChild : function(node){
33361         var of = this.firstChild;
33362         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33363         if(this.childrenRendered && of && node != of){
33364             of.renderIndent(true, true);
33365         }
33366         if(this.rendered){
33367             this.renderIndent(true, true);
33368         }
33369     },
33370
33371     // private override
33372     setLastChild : function(node){
33373         var ol = this.lastChild;
33374         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33375         if(this.childrenRendered && ol && node != ol){
33376             ol.renderIndent(true, true);
33377         }
33378         if(this.rendered){
33379             this.renderIndent(true, true);
33380         }
33381     },
33382
33383     // these methods are overridden to provide lazy rendering support
33384     // private override
33385     appendChild : function()
33386     {
33387         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33388         if(node && this.childrenRendered){
33389             node.render();
33390         }
33391         this.ui.updateExpandIcon();
33392         return node;
33393     },
33394
33395     // private override
33396     removeChild : function(node){
33397         this.ownerTree.getSelectionModel().unselect(node);
33398         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33399         // if it's been rendered remove dom node
33400         if(this.childrenRendered){
33401             node.ui.remove();
33402         }
33403         if(this.childNodes.length < 1){
33404             this.collapse(false, false);
33405         }else{
33406             this.ui.updateExpandIcon();
33407         }
33408         if(!this.firstChild) {
33409             this.childrenRendered = false;
33410         }
33411         return node;
33412     },
33413
33414     // private override
33415     insertBefore : function(node, refNode){
33416         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33417         if(newNode && refNode && this.childrenRendered){
33418             node.render();
33419         }
33420         this.ui.updateExpandIcon();
33421         return newNode;
33422     },
33423
33424     /**
33425      * Sets the text for this node
33426      * @param {String} text
33427      */
33428     setText : function(text){
33429         var oldText = this.text;
33430         this.text = text;
33431         this.attributes.text = text;
33432         if(this.rendered){ // event without subscribing
33433             this.ui.onTextChange(this, text, oldText);
33434         }
33435         this.fireEvent("textchange", this, text, oldText);
33436     },
33437
33438     /**
33439      * Triggers selection of this node
33440      */
33441     select : function(){
33442         this.getOwnerTree().getSelectionModel().select(this);
33443     },
33444
33445     /**
33446      * Triggers deselection of this node
33447      */
33448     unselect : function(){
33449         this.getOwnerTree().getSelectionModel().unselect(this);
33450     },
33451
33452     /**
33453      * Returns true if this node is selected
33454      * @return {Boolean}
33455      */
33456     isSelected : function(){
33457         return this.getOwnerTree().getSelectionModel().isSelected(this);
33458     },
33459
33460     /**
33461      * Expand this node.
33462      * @param {Boolean} deep (optional) True to expand all children as well
33463      * @param {Boolean} anim (optional) false to cancel the default animation
33464      * @param {Function} callback (optional) A callback to be called when
33465      * expanding this node completes (does not wait for deep expand to complete).
33466      * Called with 1 parameter, this node.
33467      */
33468     expand : function(deep, anim, callback){
33469         if(!this.expanded){
33470             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33471                 return;
33472             }
33473             if(!this.childrenRendered){
33474                 this.renderChildren();
33475             }
33476             this.expanded = true;
33477             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33478                 this.ui.animExpand(function(){
33479                     this.fireEvent("expand", this);
33480                     if(typeof callback == "function"){
33481                         callback(this);
33482                     }
33483                     if(deep === true){
33484                         this.expandChildNodes(true);
33485                     }
33486                 }.createDelegate(this));
33487                 return;
33488             }else{
33489                 this.ui.expand();
33490                 this.fireEvent("expand", this);
33491                 if(typeof callback == "function"){
33492                     callback(this);
33493                 }
33494             }
33495         }else{
33496            if(typeof callback == "function"){
33497                callback(this);
33498            }
33499         }
33500         if(deep === true){
33501             this.expandChildNodes(true);
33502         }
33503     },
33504
33505     isHiddenRoot : function(){
33506         return this.isRoot && !this.getOwnerTree().rootVisible;
33507     },
33508
33509     /**
33510      * Collapse this node.
33511      * @param {Boolean} deep (optional) True to collapse all children as well
33512      * @param {Boolean} anim (optional) false to cancel the default animation
33513      */
33514     collapse : function(deep, anim){
33515         if(this.expanded && !this.isHiddenRoot()){
33516             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33517                 return;
33518             }
33519             this.expanded = false;
33520             if((this.getOwnerTree().animate && anim !== false) || anim){
33521                 this.ui.animCollapse(function(){
33522                     this.fireEvent("collapse", this);
33523                     if(deep === true){
33524                         this.collapseChildNodes(true);
33525                     }
33526                 }.createDelegate(this));
33527                 return;
33528             }else{
33529                 this.ui.collapse();
33530                 this.fireEvent("collapse", this);
33531             }
33532         }
33533         if(deep === true){
33534             var cs = this.childNodes;
33535             for(var i = 0, len = cs.length; i < len; i++) {
33536                 cs[i].collapse(true, false);
33537             }
33538         }
33539     },
33540
33541     // private
33542     delayedExpand : function(delay){
33543         if(!this.expandProcId){
33544             this.expandProcId = this.expand.defer(delay, this);
33545         }
33546     },
33547
33548     // private
33549     cancelExpand : function(){
33550         if(this.expandProcId){
33551             clearTimeout(this.expandProcId);
33552         }
33553         this.expandProcId = false;
33554     },
33555
33556     /**
33557      * Toggles expanded/collapsed state of the node
33558      */
33559     toggle : function(){
33560         if(this.expanded){
33561             this.collapse();
33562         }else{
33563             this.expand();
33564         }
33565     },
33566
33567     /**
33568      * Ensures all parent nodes are expanded
33569      */
33570     ensureVisible : function(callback){
33571         var tree = this.getOwnerTree();
33572         tree.expandPath(this.parentNode.getPath(), false, function(){
33573             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33574             Roo.callback(callback);
33575         }.createDelegate(this));
33576     },
33577
33578     /**
33579      * Expand all child nodes
33580      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33581      */
33582     expandChildNodes : function(deep){
33583         var cs = this.childNodes;
33584         for(var i = 0, len = cs.length; i < len; i++) {
33585                 cs[i].expand(deep);
33586         }
33587     },
33588
33589     /**
33590      * Collapse all child nodes
33591      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33592      */
33593     collapseChildNodes : function(deep){
33594         var cs = this.childNodes;
33595         for(var i = 0, len = cs.length; i < len; i++) {
33596                 cs[i].collapse(deep);
33597         }
33598     },
33599
33600     /**
33601      * Disables this node
33602      */
33603     disable : function(){
33604         this.disabled = true;
33605         this.unselect();
33606         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33607             this.ui.onDisableChange(this, true);
33608         }
33609         this.fireEvent("disabledchange", this, true);
33610     },
33611
33612     /**
33613      * Enables this node
33614      */
33615     enable : function(){
33616         this.disabled = false;
33617         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33618             this.ui.onDisableChange(this, false);
33619         }
33620         this.fireEvent("disabledchange", this, false);
33621     },
33622
33623     // private
33624     renderChildren : function(suppressEvent){
33625         if(suppressEvent !== false){
33626             this.fireEvent("beforechildrenrendered", this);
33627         }
33628         var cs = this.childNodes;
33629         for(var i = 0, len = cs.length; i < len; i++){
33630             cs[i].render(true);
33631         }
33632         this.childrenRendered = true;
33633     },
33634
33635     // private
33636     sort : function(fn, scope){
33637         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33638         if(this.childrenRendered){
33639             var cs = this.childNodes;
33640             for(var i = 0, len = cs.length; i < len; i++){
33641                 cs[i].render(true);
33642             }
33643         }
33644     },
33645
33646     // private
33647     render : function(bulkRender){
33648         this.ui.render(bulkRender);
33649         if(!this.rendered){
33650             this.rendered = true;
33651             if(this.expanded){
33652                 this.expanded = false;
33653                 this.expand(false, false);
33654             }
33655         }
33656     },
33657
33658     // private
33659     renderIndent : function(deep, refresh){
33660         if(refresh){
33661             this.ui.childIndent = null;
33662         }
33663         this.ui.renderIndent();
33664         if(deep === true && this.childrenRendered){
33665             var cs = this.childNodes;
33666             for(var i = 0, len = cs.length; i < len; i++){
33667                 cs[i].renderIndent(true, refresh);
33668             }
33669         }
33670     }
33671 });/*
33672  * Based on:
33673  * Ext JS Library 1.1.1
33674  * Copyright(c) 2006-2007, Ext JS, LLC.
33675  *
33676  * Originally Released Under LGPL - original licence link has changed is not relivant.
33677  *
33678  * Fork - LGPL
33679  * <script type="text/javascript">
33680  */
33681  
33682 /**
33683  * @class Roo.tree.AsyncTreeNode
33684  * @extends Roo.tree.TreeNode
33685  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33686  * @constructor
33687  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33688  */
33689  Roo.tree.AsyncTreeNode = function(config){
33690     this.loaded = false;
33691     this.loading = false;
33692     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33693     /**
33694     * @event beforeload
33695     * Fires before this node is loaded, return false to cancel
33696     * @param {Node} this This node
33697     */
33698     this.addEvents({'beforeload':true, 'load': true});
33699     /**
33700     * @event load
33701     * Fires when this node is loaded
33702     * @param {Node} this This node
33703     */
33704     /**
33705      * The loader used by this node (defaults to using the tree's defined loader)
33706      * @type TreeLoader
33707      * @property loader
33708      */
33709 };
33710 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33711     expand : function(deep, anim, callback){
33712         if(this.loading){ // if an async load is already running, waiting til it's done
33713             var timer;
33714             var f = function(){
33715                 if(!this.loading){ // done loading
33716                     clearInterval(timer);
33717                     this.expand(deep, anim, callback);
33718                 }
33719             }.createDelegate(this);
33720             timer = setInterval(f, 200);
33721             return;
33722         }
33723         if(!this.loaded){
33724             if(this.fireEvent("beforeload", this) === false){
33725                 return;
33726             }
33727             this.loading = true;
33728             this.ui.beforeLoad(this);
33729             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33730             if(loader){
33731                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33732                 return;
33733             }
33734         }
33735         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33736     },
33737     
33738     /**
33739      * Returns true if this node is currently loading
33740      * @return {Boolean}
33741      */
33742     isLoading : function(){
33743         return this.loading;  
33744     },
33745     
33746     loadComplete : function(deep, anim, callback){
33747         this.loading = false;
33748         this.loaded = true;
33749         this.ui.afterLoad(this);
33750         this.fireEvent("load", this);
33751         this.expand(deep, anim, callback);
33752     },
33753     
33754     /**
33755      * Returns true if this node has been loaded
33756      * @return {Boolean}
33757      */
33758     isLoaded : function(){
33759         return this.loaded;
33760     },
33761     
33762     hasChildNodes : function(){
33763         if(!this.isLeaf() && !this.loaded){
33764             return true;
33765         }else{
33766             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33767         }
33768     },
33769
33770     /**
33771      * Trigger a reload for this node
33772      * @param {Function} callback
33773      */
33774     reload : function(callback){
33775         this.collapse(false, false);
33776         while(this.firstChild){
33777             this.removeChild(this.firstChild);
33778         }
33779         this.childrenRendered = false;
33780         this.loaded = false;
33781         if(this.isHiddenRoot()){
33782             this.expanded = false;
33783         }
33784         this.expand(false, false, callback);
33785     }
33786 });/*
33787  * Based on:
33788  * Ext JS Library 1.1.1
33789  * Copyright(c) 2006-2007, Ext JS, LLC.
33790  *
33791  * Originally Released Under LGPL - original licence link has changed is not relivant.
33792  *
33793  * Fork - LGPL
33794  * <script type="text/javascript">
33795  */
33796  
33797 /**
33798  * @class Roo.tree.TreeNodeUI
33799  * @constructor
33800  * @param {Object} node The node to render
33801  * The TreeNode UI implementation is separate from the
33802  * tree implementation. Unless you are customizing the tree UI,
33803  * you should never have to use this directly.
33804  */
33805 Roo.tree.TreeNodeUI = function(node){
33806     this.node = node;
33807     this.rendered = false;
33808     this.animating = false;
33809     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33810 };
33811
33812 Roo.tree.TreeNodeUI.prototype = {
33813     removeChild : function(node){
33814         if(this.rendered){
33815             this.ctNode.removeChild(node.ui.getEl());
33816         }
33817     },
33818
33819     beforeLoad : function(){
33820          this.addClass("x-tree-node-loading");
33821     },
33822
33823     afterLoad : function(){
33824          this.removeClass("x-tree-node-loading");
33825     },
33826
33827     onTextChange : function(node, text, oldText){
33828         if(this.rendered){
33829             this.textNode.innerHTML = text;
33830         }
33831     },
33832
33833     onDisableChange : function(node, state){
33834         this.disabled = state;
33835         if(state){
33836             this.addClass("x-tree-node-disabled");
33837         }else{
33838             this.removeClass("x-tree-node-disabled");
33839         }
33840     },
33841
33842     onSelectedChange : function(state){
33843         if(state){
33844             this.focus();
33845             this.addClass("x-tree-selected");
33846         }else{
33847             //this.blur();
33848             this.removeClass("x-tree-selected");
33849         }
33850     },
33851
33852     onMove : function(tree, node, oldParent, newParent, index, refNode){
33853         this.childIndent = null;
33854         if(this.rendered){
33855             var targetNode = newParent.ui.getContainer();
33856             if(!targetNode){//target not rendered
33857                 this.holder = document.createElement("div");
33858                 this.holder.appendChild(this.wrap);
33859                 return;
33860             }
33861             var insertBefore = refNode ? refNode.ui.getEl() : null;
33862             if(insertBefore){
33863                 targetNode.insertBefore(this.wrap, insertBefore);
33864             }else{
33865                 targetNode.appendChild(this.wrap);
33866             }
33867             this.node.renderIndent(true);
33868         }
33869     },
33870
33871     addClass : function(cls){
33872         if(this.elNode){
33873             Roo.fly(this.elNode).addClass(cls);
33874         }
33875     },
33876
33877     removeClass : function(cls){
33878         if(this.elNode){
33879             Roo.fly(this.elNode).removeClass(cls);
33880         }
33881     },
33882
33883     remove : function(){
33884         if(this.rendered){
33885             this.holder = document.createElement("div");
33886             this.holder.appendChild(this.wrap);
33887         }
33888     },
33889
33890     fireEvent : function(){
33891         return this.node.fireEvent.apply(this.node, arguments);
33892     },
33893
33894     initEvents : function(){
33895         this.node.on("move", this.onMove, this);
33896         var E = Roo.EventManager;
33897         var a = this.anchor;
33898
33899         var el = Roo.fly(a, '_treeui');
33900
33901         if(Roo.isOpera){ // opera render bug ignores the CSS
33902             el.setStyle("text-decoration", "none");
33903         }
33904
33905         el.on("click", this.onClick, this);
33906         el.on("dblclick", this.onDblClick, this);
33907
33908         if(this.checkbox){
33909             Roo.EventManager.on(this.checkbox,
33910                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33911         }
33912
33913         el.on("contextmenu", this.onContextMenu, this);
33914
33915         var icon = Roo.fly(this.iconNode);
33916         icon.on("click", this.onClick, this);
33917         icon.on("dblclick", this.onDblClick, this);
33918         icon.on("contextmenu", this.onContextMenu, this);
33919         E.on(this.ecNode, "click", this.ecClick, this, true);
33920
33921         if(this.node.disabled){
33922             this.addClass("x-tree-node-disabled");
33923         }
33924         if(this.node.hidden){
33925             this.addClass("x-tree-node-disabled");
33926         }
33927         var ot = this.node.getOwnerTree();
33928         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33929         if(dd && (!this.node.isRoot || ot.rootVisible)){
33930             Roo.dd.Registry.register(this.elNode, {
33931                 node: this.node,
33932                 handles: this.getDDHandles(),
33933                 isHandle: false
33934             });
33935         }
33936     },
33937
33938     getDDHandles : function(){
33939         return [this.iconNode, this.textNode];
33940     },
33941
33942     hide : function(){
33943         if(this.rendered){
33944             this.wrap.style.display = "none";
33945         }
33946     },
33947
33948     show : function(){
33949         if(this.rendered){
33950             this.wrap.style.display = "";
33951         }
33952     },
33953
33954     onContextMenu : function(e){
33955         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33956             e.preventDefault();
33957             this.focus();
33958             this.fireEvent("contextmenu", this.node, e);
33959         }
33960     },
33961
33962     onClick : function(e){
33963         if(this.dropping){
33964             e.stopEvent();
33965             return;
33966         }
33967         if(this.fireEvent("beforeclick", this.node, e) !== false){
33968             if(!this.disabled && this.node.attributes.href){
33969                 this.fireEvent("click", this.node, e);
33970                 return;
33971             }
33972             e.preventDefault();
33973             if(this.disabled){
33974                 return;
33975             }
33976
33977             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33978                 this.node.toggle();
33979             }
33980
33981             this.fireEvent("click", this.node, e);
33982         }else{
33983             e.stopEvent();
33984         }
33985     },
33986
33987     onDblClick : function(e){
33988         e.preventDefault();
33989         if(this.disabled){
33990             return;
33991         }
33992         if(this.checkbox){
33993             this.toggleCheck();
33994         }
33995         if(!this.animating && this.node.hasChildNodes()){
33996             this.node.toggle();
33997         }
33998         this.fireEvent("dblclick", this.node, e);
33999     },
34000
34001     onCheckChange : function(){
34002         var checked = this.checkbox.checked;
34003         this.node.attributes.checked = checked;
34004         this.fireEvent('checkchange', this.node, checked);
34005     },
34006
34007     ecClick : function(e){
34008         if(!this.animating && this.node.hasChildNodes()){
34009             this.node.toggle();
34010         }
34011     },
34012
34013     startDrop : function(){
34014         this.dropping = true;
34015     },
34016
34017     // delayed drop so the click event doesn't get fired on a drop
34018     endDrop : function(){
34019        setTimeout(function(){
34020            this.dropping = false;
34021        }.createDelegate(this), 50);
34022     },
34023
34024     expand : function(){
34025         this.updateExpandIcon();
34026         this.ctNode.style.display = "";
34027     },
34028
34029     focus : function(){
34030         if(!this.node.preventHScroll){
34031             try{this.anchor.focus();
34032             }catch(e){}
34033         }else if(!Roo.isIE){
34034             try{
34035                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
34036                 var l = noscroll.scrollLeft;
34037                 this.anchor.focus();
34038                 noscroll.scrollLeft = l;
34039             }catch(e){}
34040         }
34041     },
34042
34043     toggleCheck : function(value){
34044         var cb = this.checkbox;
34045         if(cb){
34046             cb.checked = (value === undefined ? !cb.checked : value);
34047         }
34048     },
34049
34050     blur : function(){
34051         try{
34052             this.anchor.blur();
34053         }catch(e){}
34054     },
34055
34056     animExpand : function(callback){
34057         var ct = Roo.get(this.ctNode);
34058         ct.stopFx();
34059         if(!this.node.hasChildNodes()){
34060             this.updateExpandIcon();
34061             this.ctNode.style.display = "";
34062             Roo.callback(callback);
34063             return;
34064         }
34065         this.animating = true;
34066         this.updateExpandIcon();
34067
34068         ct.slideIn('t', {
34069            callback : function(){
34070                this.animating = false;
34071                Roo.callback(callback);
34072             },
34073             scope: this,
34074             duration: this.node.ownerTree.duration || .25
34075         });
34076     },
34077
34078     highlight : function(){
34079         var tree = this.node.getOwnerTree();
34080         Roo.fly(this.wrap).highlight(
34081             tree.hlColor || "C3DAF9",
34082             {endColor: tree.hlBaseColor}
34083         );
34084     },
34085
34086     collapse : function(){
34087         this.updateExpandIcon();
34088         this.ctNode.style.display = "none";
34089     },
34090
34091     animCollapse : function(callback){
34092         var ct = Roo.get(this.ctNode);
34093         ct.enableDisplayMode('block');
34094         ct.stopFx();
34095
34096         this.animating = true;
34097         this.updateExpandIcon();
34098
34099         ct.slideOut('t', {
34100             callback : function(){
34101                this.animating = false;
34102                Roo.callback(callback);
34103             },
34104             scope: this,
34105             duration: this.node.ownerTree.duration || .25
34106         });
34107     },
34108
34109     getContainer : function(){
34110         return this.ctNode;
34111     },
34112
34113     getEl : function(){
34114         return this.wrap;
34115     },
34116
34117     appendDDGhost : function(ghostNode){
34118         ghostNode.appendChild(this.elNode.cloneNode(true));
34119     },
34120
34121     getDDRepairXY : function(){
34122         return Roo.lib.Dom.getXY(this.iconNode);
34123     },
34124
34125     onRender : function(){
34126         this.render();
34127     },
34128
34129     render : function(bulkRender){
34130         var n = this.node, a = n.attributes;
34131         var targetNode = n.parentNode ?
34132               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34133
34134         if(!this.rendered){
34135             this.rendered = true;
34136
34137             this.renderElements(n, a, targetNode, bulkRender);
34138
34139             if(a.qtip){
34140                if(this.textNode.setAttributeNS){
34141                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34142                    if(a.qtipTitle){
34143                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34144                    }
34145                }else{
34146                    this.textNode.setAttribute("ext:qtip", a.qtip);
34147                    if(a.qtipTitle){
34148                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34149                    }
34150                }
34151             }else if(a.qtipCfg){
34152                 a.qtipCfg.target = Roo.id(this.textNode);
34153                 Roo.QuickTips.register(a.qtipCfg);
34154             }
34155             this.initEvents();
34156             if(!this.node.expanded){
34157                 this.updateExpandIcon();
34158             }
34159         }else{
34160             if(bulkRender === true) {
34161                 targetNode.appendChild(this.wrap);
34162             }
34163         }
34164     },
34165
34166     renderElements : function(n, a, targetNode, bulkRender)
34167     {
34168         // add some indent caching, this helps performance when rendering a large tree
34169         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34170         var t = n.getOwnerTree();
34171         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34172         if (typeof(n.attributes.html) != 'undefined') {
34173             txt = n.attributes.html;
34174         }
34175         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34176         var cb = typeof a.checked == 'boolean';
34177         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34178         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34179             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34180             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34181             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34182             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34183             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34184              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34185                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34186             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34187             "</li>"];
34188
34189         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34190             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34191                                 n.nextSibling.ui.getEl(), buf.join(""));
34192         }else{
34193             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34194         }
34195
34196         this.elNode = this.wrap.childNodes[0];
34197         this.ctNode = this.wrap.childNodes[1];
34198         var cs = this.elNode.childNodes;
34199         this.indentNode = cs[0];
34200         this.ecNode = cs[1];
34201         this.iconNode = cs[2];
34202         var index = 3;
34203         if(cb){
34204             this.checkbox = cs[3];
34205             index++;
34206         }
34207         this.anchor = cs[index];
34208         this.textNode = cs[index].firstChild;
34209     },
34210
34211     getAnchor : function(){
34212         return this.anchor;
34213     },
34214
34215     getTextEl : function(){
34216         return this.textNode;
34217     },
34218
34219     getIconEl : function(){
34220         return this.iconNode;
34221     },
34222
34223     isChecked : function(){
34224         return this.checkbox ? this.checkbox.checked : false;
34225     },
34226
34227     updateExpandIcon : function(){
34228         if(this.rendered){
34229             var n = this.node, c1, c2;
34230             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34231             var hasChild = n.hasChildNodes();
34232             if(hasChild){
34233                 if(n.expanded){
34234                     cls += "-minus";
34235                     c1 = "x-tree-node-collapsed";
34236                     c2 = "x-tree-node-expanded";
34237                 }else{
34238                     cls += "-plus";
34239                     c1 = "x-tree-node-expanded";
34240                     c2 = "x-tree-node-collapsed";
34241                 }
34242                 if(this.wasLeaf){
34243                     this.removeClass("x-tree-node-leaf");
34244                     this.wasLeaf = false;
34245                 }
34246                 if(this.c1 != c1 || this.c2 != c2){
34247                     Roo.fly(this.elNode).replaceClass(c1, c2);
34248                     this.c1 = c1; this.c2 = c2;
34249                 }
34250             }else{
34251                 // this changes non-leafs into leafs if they have no children.
34252                 // it's not very rational behaviour..
34253                 
34254                 if(!this.wasLeaf && this.node.leaf){
34255                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34256                     delete this.c1;
34257                     delete this.c2;
34258                     this.wasLeaf = true;
34259                 }
34260             }
34261             var ecc = "x-tree-ec-icon "+cls;
34262             if(this.ecc != ecc){
34263                 this.ecNode.className = ecc;
34264                 this.ecc = ecc;
34265             }
34266         }
34267     },
34268
34269     getChildIndent : function(){
34270         if(!this.childIndent){
34271             var buf = [];
34272             var p = this.node;
34273             while(p){
34274                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34275                     if(!p.isLast()) {
34276                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34277                     } else {
34278                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34279                     }
34280                 }
34281                 p = p.parentNode;
34282             }
34283             this.childIndent = buf.join("");
34284         }
34285         return this.childIndent;
34286     },
34287
34288     renderIndent : function(){
34289         if(this.rendered){
34290             var indent = "";
34291             var p = this.node.parentNode;
34292             if(p){
34293                 indent = p.ui.getChildIndent();
34294             }
34295             if(this.indentMarkup != indent){ // don't rerender if not required
34296                 this.indentNode.innerHTML = indent;
34297                 this.indentMarkup = indent;
34298             }
34299             this.updateExpandIcon();
34300         }
34301     }
34302 };
34303
34304 Roo.tree.RootTreeNodeUI = function(){
34305     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34306 };
34307 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34308     render : function(){
34309         if(!this.rendered){
34310             var targetNode = this.node.ownerTree.innerCt.dom;
34311             this.node.expanded = true;
34312             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34313             this.wrap = this.ctNode = targetNode.firstChild;
34314         }
34315     },
34316     collapse : function(){
34317     },
34318     expand : function(){
34319     }
34320 });/*
34321  * Based on:
34322  * Ext JS Library 1.1.1
34323  * Copyright(c) 2006-2007, Ext JS, LLC.
34324  *
34325  * Originally Released Under LGPL - original licence link has changed is not relivant.
34326  *
34327  * Fork - LGPL
34328  * <script type="text/javascript">
34329  */
34330 /**
34331  * @class Roo.tree.TreeLoader
34332  * @extends Roo.util.Observable
34333  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34334  * nodes from a specified URL. The response must be a javascript Array definition
34335  * who's elements are node definition objects. eg:
34336  * <pre><code>
34337 {  success : true,
34338    data :      [
34339    
34340     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34341     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34342     ]
34343 }
34344
34345
34346 </code></pre>
34347  * <br><br>
34348  * The old style respose with just an array is still supported, but not recommended.
34349  * <br><br>
34350  *
34351  * A server request is sent, and child nodes are loaded only when a node is expanded.
34352  * The loading node's id is passed to the server under the parameter name "node" to
34353  * enable the server to produce the correct child nodes.
34354  * <br><br>
34355  * To pass extra parameters, an event handler may be attached to the "beforeload"
34356  * event, and the parameters specified in the TreeLoader's baseParams property:
34357  * <pre><code>
34358     myTreeLoader.on("beforeload", function(treeLoader, node) {
34359         this.baseParams.category = node.attributes.category;
34360     }, this);
34361 </code></pre><
34362  * This would pass an HTTP parameter called "category" to the server containing
34363  * the value of the Node's "category" attribute.
34364  * @constructor
34365  * Creates a new Treeloader.
34366  * @param {Object} config A config object containing config properties.
34367  */
34368 Roo.tree.TreeLoader = function(config){
34369     this.baseParams = {};
34370     this.requestMethod = "POST";
34371     Roo.apply(this, config);
34372
34373     this.addEvents({
34374     
34375         /**
34376          * @event beforeload
34377          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34378          * @param {Object} This TreeLoader object.
34379          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34380          * @param {Object} callback The callback function specified in the {@link #load} call.
34381          */
34382         beforeload : true,
34383         /**
34384          * @event load
34385          * Fires when the node has been successfuly loaded.
34386          * @param {Object} This TreeLoader object.
34387          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34388          * @param {Object} response The response object containing the data from the server.
34389          */
34390         load : true,
34391         /**
34392          * @event loadexception
34393          * Fires if the network request failed.
34394          * @param {Object} This TreeLoader object.
34395          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34396          * @param {Object} response The response object containing the data from the server.
34397          */
34398         loadexception : true,
34399         /**
34400          * @event create
34401          * Fires before a node is created, enabling you to return custom Node types 
34402          * @param {Object} This TreeLoader object.
34403          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34404          */
34405         create : true
34406     });
34407
34408     Roo.tree.TreeLoader.superclass.constructor.call(this);
34409 };
34410
34411 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34412     /**
34413     * @cfg {String} dataUrl The URL from which to request a Json string which
34414     * specifies an array of node definition object representing the child nodes
34415     * to be loaded.
34416     */
34417     /**
34418     * @cfg {String} requestMethod either GET or POST
34419     * defaults to POST (due to BC)
34420     * to be loaded.
34421     */
34422     /**
34423     * @cfg {Object} baseParams (optional) An object containing properties which
34424     * specify HTTP parameters to be passed to each request for child nodes.
34425     */
34426     /**
34427     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34428     * created by this loader. If the attributes sent by the server have an attribute in this object,
34429     * they take priority.
34430     */
34431     /**
34432     * @cfg {Object} uiProviders (optional) An object containing properties which
34433     * 
34434     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34435     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34436     * <i>uiProvider</i> attribute of a returned child node is a string rather
34437     * than a reference to a TreeNodeUI implementation, this that string value
34438     * is used as a property name in the uiProviders object. You can define the provider named
34439     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34440     */
34441     uiProviders : {},
34442
34443     /**
34444     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34445     * child nodes before loading.
34446     */
34447     clearOnLoad : true,
34448
34449     /**
34450     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34451     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34452     * Grid query { data : [ .....] }
34453     */
34454     
34455     root : false,
34456      /**
34457     * @cfg {String} queryParam (optional) 
34458     * Name of the query as it will be passed on the querystring (defaults to 'node')
34459     * eg. the request will be ?node=[id]
34460     */
34461     
34462     
34463     queryParam: false,
34464     
34465     /**
34466      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34467      * This is called automatically when a node is expanded, but may be used to reload
34468      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34469      * @param {Roo.tree.TreeNode} node
34470      * @param {Function} callback
34471      */
34472     load : function(node, callback){
34473         if(this.clearOnLoad){
34474             while(node.firstChild){
34475                 node.removeChild(node.firstChild);
34476             }
34477         }
34478         if(node.attributes.children){ // preloaded json children
34479             var cs = node.attributes.children;
34480             for(var i = 0, len = cs.length; i < len; i++){
34481                 node.appendChild(this.createNode(cs[i]));
34482             }
34483             if(typeof callback == "function"){
34484                 callback();
34485             }
34486         }else if(this.dataUrl){
34487             this.requestData(node, callback);
34488         }
34489     },
34490
34491     getParams: function(node){
34492         var buf = [], bp = this.baseParams;
34493         for(var key in bp){
34494             if(typeof bp[key] != "function"){
34495                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34496             }
34497         }
34498         var n = this.queryParam === false ? 'node' : this.queryParam;
34499         buf.push(n + "=", encodeURIComponent(node.id));
34500         return buf.join("");
34501     },
34502
34503     requestData : function(node, callback){
34504         if(this.fireEvent("beforeload", this, node, callback) !== false){
34505             this.transId = Roo.Ajax.request({
34506                 method:this.requestMethod,
34507                 url: this.dataUrl||this.url,
34508                 success: this.handleResponse,
34509                 failure: this.handleFailure,
34510                 scope: this,
34511                 argument: {callback: callback, node: node},
34512                 params: this.getParams(node)
34513             });
34514         }else{
34515             // if the load is cancelled, make sure we notify
34516             // the node that we are done
34517             if(typeof callback == "function"){
34518                 callback();
34519             }
34520         }
34521     },
34522
34523     isLoading : function(){
34524         return this.transId ? true : false;
34525     },
34526
34527     abort : function(){
34528         if(this.isLoading()){
34529             Roo.Ajax.abort(this.transId);
34530         }
34531     },
34532
34533     // private
34534     createNode : function(attr)
34535     {
34536         // apply baseAttrs, nice idea Corey!
34537         if(this.baseAttrs){
34538             Roo.applyIf(attr, this.baseAttrs);
34539         }
34540         if(this.applyLoader !== false){
34541             attr.loader = this;
34542         }
34543         // uiProvider = depreciated..
34544         
34545         if(typeof(attr.uiProvider) == 'string'){
34546            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34547                 /**  eval:var:attr */ eval(attr.uiProvider);
34548         }
34549         if(typeof(this.uiProviders['default']) != 'undefined') {
34550             attr.uiProvider = this.uiProviders['default'];
34551         }
34552         
34553         this.fireEvent('create', this, attr);
34554         
34555         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34556         return(attr.leaf ?
34557                         new Roo.tree.TreeNode(attr) :
34558                         new Roo.tree.AsyncTreeNode(attr));
34559     },
34560
34561     processResponse : function(response, node, callback)
34562     {
34563         var json = response.responseText;
34564         try {
34565             
34566             var o = Roo.decode(json);
34567             
34568             if (this.root === false && typeof(o.success) != undefined) {
34569                 this.root = 'data'; // the default behaviour for list like data..
34570                 }
34571                 
34572             if (this.root !== false &&  !o.success) {
34573                 // it's a failure condition.
34574                 var a = response.argument;
34575                 this.fireEvent("loadexception", this, a.node, response);
34576                 Roo.log("Load failed - should have a handler really");
34577                 return;
34578             }
34579             
34580             
34581             
34582             if (this.root !== false) {
34583                  o = o[this.root];
34584             }
34585             
34586             for(var i = 0, len = o.length; i < len; i++){
34587                 var n = this.createNode(o[i]);
34588                 if(n){
34589                     node.appendChild(n);
34590                 }
34591             }
34592             if(typeof callback == "function"){
34593                 callback(this, node);
34594             }
34595         }catch(e){
34596             this.handleFailure(response);
34597         }
34598     },
34599
34600     handleResponse : function(response){
34601         this.transId = false;
34602         var a = response.argument;
34603         this.processResponse(response, a.node, a.callback);
34604         this.fireEvent("load", this, a.node, response);
34605     },
34606
34607     handleFailure : function(response)
34608     {
34609         // should handle failure better..
34610         this.transId = false;
34611         var a = response.argument;
34612         this.fireEvent("loadexception", this, a.node, response);
34613         if(typeof a.callback == "function"){
34614             a.callback(this, a.node);
34615         }
34616     }
34617 });/*
34618  * Based on:
34619  * Ext JS Library 1.1.1
34620  * Copyright(c) 2006-2007, Ext JS, LLC.
34621  *
34622  * Originally Released Under LGPL - original licence link has changed is not relivant.
34623  *
34624  * Fork - LGPL
34625  * <script type="text/javascript">
34626  */
34627
34628 /**
34629 * @class Roo.tree.TreeFilter
34630 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34631 * @param {TreePanel} tree
34632 * @param {Object} config (optional)
34633  */
34634 Roo.tree.TreeFilter = function(tree, config){
34635     this.tree = tree;
34636     this.filtered = {};
34637     Roo.apply(this, config);
34638 };
34639
34640 Roo.tree.TreeFilter.prototype = {
34641     clearBlank:false,
34642     reverse:false,
34643     autoClear:false,
34644     remove:false,
34645
34646      /**
34647      * Filter the data by a specific attribute.
34648      * @param {String/RegExp} value Either string that the attribute value
34649      * should start with or a RegExp to test against the attribute
34650      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34651      * @param {TreeNode} startNode (optional) The node to start the filter at.
34652      */
34653     filter : function(value, attr, startNode){
34654         attr = attr || "text";
34655         var f;
34656         if(typeof value == "string"){
34657             var vlen = value.length;
34658             // auto clear empty filter
34659             if(vlen == 0 && this.clearBlank){
34660                 this.clear();
34661                 return;
34662             }
34663             value = value.toLowerCase();
34664             f = function(n){
34665                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34666             };
34667         }else if(value.exec){ // regex?
34668             f = function(n){
34669                 return value.test(n.attributes[attr]);
34670             };
34671         }else{
34672             throw 'Illegal filter type, must be string or regex';
34673         }
34674         this.filterBy(f, null, startNode);
34675         },
34676
34677     /**
34678      * Filter by a function. The passed function will be called with each
34679      * node in the tree (or from the startNode). If the function returns true, the node is kept
34680      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34681      * @param {Function} fn The filter function
34682      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34683      */
34684     filterBy : function(fn, scope, startNode){
34685         startNode = startNode || this.tree.root;
34686         if(this.autoClear){
34687             this.clear();
34688         }
34689         var af = this.filtered, rv = this.reverse;
34690         var f = function(n){
34691             if(n == startNode){
34692                 return true;
34693             }
34694             if(af[n.id]){
34695                 return false;
34696             }
34697             var m = fn.call(scope || n, n);
34698             if(!m || rv){
34699                 af[n.id] = n;
34700                 n.ui.hide();
34701                 return false;
34702             }
34703             return true;
34704         };
34705         startNode.cascade(f);
34706         if(this.remove){
34707            for(var id in af){
34708                if(typeof id != "function"){
34709                    var n = af[id];
34710                    if(n && n.parentNode){
34711                        n.parentNode.removeChild(n);
34712                    }
34713                }
34714            }
34715         }
34716     },
34717
34718     /**
34719      * Clears the current filter. Note: with the "remove" option
34720      * set a filter cannot be cleared.
34721      */
34722     clear : function(){
34723         var t = this.tree;
34724         var af = this.filtered;
34725         for(var id in af){
34726             if(typeof id != "function"){
34727                 var n = af[id];
34728                 if(n){
34729                     n.ui.show();
34730                 }
34731             }
34732         }
34733         this.filtered = {};
34734     }
34735 };
34736 /*
34737  * Based on:
34738  * Ext JS Library 1.1.1
34739  * Copyright(c) 2006-2007, Ext JS, LLC.
34740  *
34741  * Originally Released Under LGPL - original licence link has changed is not relivant.
34742  *
34743  * Fork - LGPL
34744  * <script type="text/javascript">
34745  */
34746  
34747
34748 /**
34749  * @class Roo.tree.TreeSorter
34750  * Provides sorting of nodes in a TreePanel
34751  * 
34752  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34753  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34754  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34755  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34756  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34757  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34758  * @constructor
34759  * @param {TreePanel} tree
34760  * @param {Object} config
34761  */
34762 Roo.tree.TreeSorter = function(tree, config){
34763     Roo.apply(this, config);
34764     tree.on("beforechildrenrendered", this.doSort, this);
34765     tree.on("append", this.updateSort, this);
34766     tree.on("insert", this.updateSort, this);
34767     
34768     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34769     var p = this.property || "text";
34770     var sortType = this.sortType;
34771     var fs = this.folderSort;
34772     var cs = this.caseSensitive === true;
34773     var leafAttr = this.leafAttr || 'leaf';
34774
34775     this.sortFn = function(n1, n2){
34776         if(fs){
34777             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34778                 return 1;
34779             }
34780             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34781                 return -1;
34782             }
34783         }
34784         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34785         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34786         if(v1 < v2){
34787                         return dsc ? +1 : -1;
34788                 }else if(v1 > v2){
34789                         return dsc ? -1 : +1;
34790         }else{
34791                 return 0;
34792         }
34793     };
34794 };
34795
34796 Roo.tree.TreeSorter.prototype = {
34797     doSort : function(node){
34798         node.sort(this.sortFn);
34799     },
34800     
34801     compareNodes : function(n1, n2){
34802         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34803     },
34804     
34805     updateSort : function(tree, node){
34806         if(node.childrenRendered){
34807             this.doSort.defer(1, this, [node]);
34808         }
34809     }
34810 };/*
34811  * Based on:
34812  * Ext JS Library 1.1.1
34813  * Copyright(c) 2006-2007, Ext JS, LLC.
34814  *
34815  * Originally Released Under LGPL - original licence link has changed is not relivant.
34816  *
34817  * Fork - LGPL
34818  * <script type="text/javascript">
34819  */
34820
34821 if(Roo.dd.DropZone){
34822     
34823 Roo.tree.TreeDropZone = function(tree, config){
34824     this.allowParentInsert = false;
34825     this.allowContainerDrop = false;
34826     this.appendOnly = false;
34827     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34828     this.tree = tree;
34829     this.lastInsertClass = "x-tree-no-status";
34830     this.dragOverData = {};
34831 };
34832
34833 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34834     ddGroup : "TreeDD",
34835     scroll:  true,
34836     
34837     expandDelay : 1000,
34838     
34839     expandNode : function(node){
34840         if(node.hasChildNodes() && !node.isExpanded()){
34841             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34842         }
34843     },
34844     
34845     queueExpand : function(node){
34846         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34847     },
34848     
34849     cancelExpand : function(){
34850         if(this.expandProcId){
34851             clearTimeout(this.expandProcId);
34852             this.expandProcId = false;
34853         }
34854     },
34855     
34856     isValidDropPoint : function(n, pt, dd, e, data){
34857         if(!n || !data){ return false; }
34858         var targetNode = n.node;
34859         var dropNode = data.node;
34860         // default drop rules
34861         if(!(targetNode && targetNode.isTarget && pt)){
34862             return false;
34863         }
34864         if(pt == "append" && targetNode.allowChildren === false){
34865             return false;
34866         }
34867         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34868             return false;
34869         }
34870         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34871             return false;
34872         }
34873         // reuse the object
34874         var overEvent = this.dragOverData;
34875         overEvent.tree = this.tree;
34876         overEvent.target = targetNode;
34877         overEvent.data = data;
34878         overEvent.point = pt;
34879         overEvent.source = dd;
34880         overEvent.rawEvent = e;
34881         overEvent.dropNode = dropNode;
34882         overEvent.cancel = false;  
34883         var result = this.tree.fireEvent("nodedragover", overEvent);
34884         return overEvent.cancel === false && result !== false;
34885     },
34886     
34887     getDropPoint : function(e, n, dd)
34888     {
34889         var tn = n.node;
34890         if(tn.isRoot){
34891             return tn.allowChildren !== false ? "append" : false; // always append for root
34892         }
34893         var dragEl = n.ddel;
34894         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34895         var y = Roo.lib.Event.getPageY(e);
34896         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34897         
34898         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34899         var noAppend = tn.allowChildren === false;
34900         if(this.appendOnly || tn.parentNode.allowChildren === false){
34901             return noAppend ? false : "append";
34902         }
34903         var noBelow = false;
34904         if(!this.allowParentInsert){
34905             noBelow = tn.hasChildNodes() && tn.isExpanded();
34906         }
34907         var q = (b - t) / (noAppend ? 2 : 3);
34908         if(y >= t && y < (t + q)){
34909             return "above";
34910         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34911             return "below";
34912         }else{
34913             return "append";
34914         }
34915     },
34916     
34917     onNodeEnter : function(n, dd, e, data)
34918     {
34919         this.cancelExpand();
34920     },
34921     
34922     onNodeOver : function(n, dd, e, data)
34923     {
34924        
34925         var pt = this.getDropPoint(e, n, dd);
34926         var node = n.node;
34927         
34928         // auto node expand check
34929         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34930             this.queueExpand(node);
34931         }else if(pt != "append"){
34932             this.cancelExpand();
34933         }
34934         
34935         // set the insert point style on the target node
34936         var returnCls = this.dropNotAllowed;
34937         if(this.isValidDropPoint(n, pt, dd, e, data)){
34938            if(pt){
34939                var el = n.ddel;
34940                var cls;
34941                if(pt == "above"){
34942                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34943                    cls = "x-tree-drag-insert-above";
34944                }else if(pt == "below"){
34945                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34946                    cls = "x-tree-drag-insert-below";
34947                }else{
34948                    returnCls = "x-tree-drop-ok-append";
34949                    cls = "x-tree-drag-append";
34950                }
34951                if(this.lastInsertClass != cls){
34952                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34953                    this.lastInsertClass = cls;
34954                }
34955            }
34956        }
34957        return returnCls;
34958     },
34959     
34960     onNodeOut : function(n, dd, e, data){
34961         
34962         this.cancelExpand();
34963         this.removeDropIndicators(n);
34964     },
34965     
34966     onNodeDrop : function(n, dd, e, data){
34967         var point = this.getDropPoint(e, n, dd);
34968         var targetNode = n.node;
34969         targetNode.ui.startDrop();
34970         if(!this.isValidDropPoint(n, point, dd, e, data)){
34971             targetNode.ui.endDrop();
34972             return false;
34973         }
34974         // first try to find the drop node
34975         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34976         var dropEvent = {
34977             tree : this.tree,
34978             target: targetNode,
34979             data: data,
34980             point: point,
34981             source: dd,
34982             rawEvent: e,
34983             dropNode: dropNode,
34984             cancel: !dropNode   
34985         };
34986         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34987         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34988             targetNode.ui.endDrop();
34989             return false;
34990         }
34991         // allow target changing
34992         targetNode = dropEvent.target;
34993         if(point == "append" && !targetNode.isExpanded()){
34994             targetNode.expand(false, null, function(){
34995                 this.completeDrop(dropEvent);
34996             }.createDelegate(this));
34997         }else{
34998             this.completeDrop(dropEvent);
34999         }
35000         return true;
35001     },
35002     
35003     completeDrop : function(de){
35004         var ns = de.dropNode, p = de.point, t = de.target;
35005         if(!(ns instanceof Array)){
35006             ns = [ns];
35007         }
35008         var n;
35009         for(var i = 0, len = ns.length; i < len; i++){
35010             n = ns[i];
35011             if(p == "above"){
35012                 t.parentNode.insertBefore(n, t);
35013             }else if(p == "below"){
35014                 t.parentNode.insertBefore(n, t.nextSibling);
35015             }else{
35016                 t.appendChild(n);
35017             }
35018         }
35019         n.ui.focus();
35020         if(this.tree.hlDrop){
35021             n.ui.highlight();
35022         }
35023         t.ui.endDrop();
35024         this.tree.fireEvent("nodedrop", de);
35025     },
35026     
35027     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
35028         if(this.tree.hlDrop){
35029             dropNode.ui.focus();
35030             dropNode.ui.highlight();
35031         }
35032         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
35033     },
35034     
35035     getTree : function(){
35036         return this.tree;
35037     },
35038     
35039     removeDropIndicators : function(n){
35040         if(n && n.ddel){
35041             var el = n.ddel;
35042             Roo.fly(el).removeClass([
35043                     "x-tree-drag-insert-above",
35044                     "x-tree-drag-insert-below",
35045                     "x-tree-drag-append"]);
35046             this.lastInsertClass = "_noclass";
35047         }
35048     },
35049     
35050     beforeDragDrop : function(target, e, id){
35051         this.cancelExpand();
35052         return true;
35053     },
35054     
35055     afterRepair : function(data){
35056         if(data && Roo.enableFx){
35057             data.node.ui.highlight();
35058         }
35059         this.hideProxy();
35060     } 
35061     
35062 });
35063
35064 }
35065 /*
35066  * Based on:
35067  * Ext JS Library 1.1.1
35068  * Copyright(c) 2006-2007, Ext JS, LLC.
35069  *
35070  * Originally Released Under LGPL - original licence link has changed is not relivant.
35071  *
35072  * Fork - LGPL
35073  * <script type="text/javascript">
35074  */
35075  
35076
35077 if(Roo.dd.DragZone){
35078 Roo.tree.TreeDragZone = function(tree, config){
35079     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35080     this.tree = tree;
35081 };
35082
35083 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35084     ddGroup : "TreeDD",
35085    
35086     onBeforeDrag : function(data, e){
35087         var n = data.node;
35088         return n && n.draggable && !n.disabled;
35089     },
35090      
35091     
35092     onInitDrag : function(e){
35093         var data = this.dragData;
35094         this.tree.getSelectionModel().select(data.node);
35095         this.proxy.update("");
35096         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35097         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35098     },
35099     
35100     getRepairXY : function(e, data){
35101         return data.node.ui.getDDRepairXY();
35102     },
35103     
35104     onEndDrag : function(data, e){
35105         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35106         
35107         
35108     },
35109     
35110     onValidDrop : function(dd, e, id){
35111         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35112         this.hideProxy();
35113     },
35114     
35115     beforeInvalidDrop : function(e, id){
35116         // this scrolls the original position back into view
35117         var sm = this.tree.getSelectionModel();
35118         sm.clearSelections();
35119         sm.select(this.dragData.node);
35120     }
35121 });
35122 }/*
35123  * Based on:
35124  * Ext JS Library 1.1.1
35125  * Copyright(c) 2006-2007, Ext JS, LLC.
35126  *
35127  * Originally Released Under LGPL - original licence link has changed is not relivant.
35128  *
35129  * Fork - LGPL
35130  * <script type="text/javascript">
35131  */
35132 /**
35133  * @class Roo.tree.TreeEditor
35134  * @extends Roo.Editor
35135  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35136  * as the editor field.
35137  * @constructor
35138  * @param {Object} config (used to be the tree panel.)
35139  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35140  * 
35141  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35142  * @cfg {Roo.form.TextField|Object} field The field configuration
35143  *
35144  * 
35145  */
35146 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35147     var tree = config;
35148     var field;
35149     if (oldconfig) { // old style..
35150         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35151     } else {
35152         // new style..
35153         tree = config.tree;
35154         config.field = config.field  || {};
35155         config.field.xtype = 'TextField';
35156         field = Roo.factory(config.field, Roo.form);
35157     }
35158     config = config || {};
35159     
35160     
35161     this.addEvents({
35162         /**
35163          * @event beforenodeedit
35164          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35165          * false from the handler of this event.
35166          * @param {Editor} this
35167          * @param {Roo.tree.Node} node 
35168          */
35169         "beforenodeedit" : true
35170     });
35171     
35172     //Roo.log(config);
35173     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35174
35175     this.tree = tree;
35176
35177     tree.on('beforeclick', this.beforeNodeClick, this);
35178     tree.getTreeEl().on('mousedown', this.hide, this);
35179     this.on('complete', this.updateNode, this);
35180     this.on('beforestartedit', this.fitToTree, this);
35181     this.on('startedit', this.bindScroll, this, {delay:10});
35182     this.on('specialkey', this.onSpecialKey, this);
35183 };
35184
35185 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35186     /**
35187      * @cfg {String} alignment
35188      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35189      */
35190     alignment: "l-l",
35191     // inherit
35192     autoSize: false,
35193     /**
35194      * @cfg {Boolean} hideEl
35195      * True to hide the bound element while the editor is displayed (defaults to false)
35196      */
35197     hideEl : false,
35198     /**
35199      * @cfg {String} cls
35200      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35201      */
35202     cls: "x-small-editor x-tree-editor",
35203     /**
35204      * @cfg {Boolean} shim
35205      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35206      */
35207     shim:false,
35208     // inherit
35209     shadow:"frame",
35210     /**
35211      * @cfg {Number} maxWidth
35212      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35213      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35214      * scroll and client offsets into account prior to each edit.
35215      */
35216     maxWidth: 250,
35217
35218     editDelay : 350,
35219
35220     // private
35221     fitToTree : function(ed, el){
35222         var td = this.tree.getTreeEl().dom, nd = el.dom;
35223         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35224             td.scrollLeft = nd.offsetLeft;
35225         }
35226         var w = Math.min(
35227                 this.maxWidth,
35228                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35229         this.setSize(w, '');
35230         
35231         return this.fireEvent('beforenodeedit', this, this.editNode);
35232         
35233     },
35234
35235     // private
35236     triggerEdit : function(node){
35237         this.completeEdit();
35238         this.editNode = node;
35239         this.startEdit(node.ui.textNode, node.text);
35240     },
35241
35242     // private
35243     bindScroll : function(){
35244         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35245     },
35246
35247     // private
35248     beforeNodeClick : function(node, e){
35249         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35250         this.lastClick = new Date();
35251         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35252             e.stopEvent();
35253             this.triggerEdit(node);
35254             return false;
35255         }
35256         return true;
35257     },
35258
35259     // private
35260     updateNode : function(ed, value){
35261         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35262         this.editNode.setText(value);
35263     },
35264
35265     // private
35266     onHide : function(){
35267         Roo.tree.TreeEditor.superclass.onHide.call(this);
35268         if(this.editNode){
35269             this.editNode.ui.focus();
35270         }
35271     },
35272
35273     // private
35274     onSpecialKey : function(field, e){
35275         var k = e.getKey();
35276         if(k == e.ESC){
35277             e.stopEvent();
35278             this.cancelEdit();
35279         }else if(k == e.ENTER && !e.hasModifier()){
35280             e.stopEvent();
35281             this.completeEdit();
35282         }
35283     }
35284 });//<Script type="text/javascript">
35285 /*
35286  * Based on:
35287  * Ext JS Library 1.1.1
35288  * Copyright(c) 2006-2007, Ext JS, LLC.
35289  *
35290  * Originally Released Under LGPL - original licence link has changed is not relivant.
35291  *
35292  * Fork - LGPL
35293  * <script type="text/javascript">
35294  */
35295  
35296 /**
35297  * Not documented??? - probably should be...
35298  */
35299
35300 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35301     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35302     
35303     renderElements : function(n, a, targetNode, bulkRender){
35304         //consel.log("renderElements?");
35305         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35306
35307         var t = n.getOwnerTree();
35308         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35309         
35310         var cols = t.columns;
35311         var bw = t.borderWidth;
35312         var c = cols[0];
35313         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35314          var cb = typeof a.checked == "boolean";
35315         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35316         var colcls = 'x-t-' + tid + '-c0';
35317         var buf = [
35318             '<li class="x-tree-node">',
35319             
35320                 
35321                 '<div class="x-tree-node-el ', a.cls,'">',
35322                     // extran...
35323                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35324                 
35325                 
35326                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35327                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35328                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35329                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35330                            (a.iconCls ? ' '+a.iconCls : ''),
35331                            '" unselectable="on" />',
35332                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35333                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35334                              
35335                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35336                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35337                             '<span unselectable="on" qtip="' + tx + '">',
35338                              tx,
35339                              '</span></a>' ,
35340                     '</div>',
35341                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35342                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35343                  ];
35344         for(var i = 1, len = cols.length; i < len; i++){
35345             c = cols[i];
35346             colcls = 'x-t-' + tid + '-c' +i;
35347             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35348             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35349                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35350                       "</div>");
35351          }
35352          
35353          buf.push(
35354             '</a>',
35355             '<div class="x-clear"></div></div>',
35356             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35357             "</li>");
35358         
35359         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35360             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35361                                 n.nextSibling.ui.getEl(), buf.join(""));
35362         }else{
35363             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35364         }
35365         var el = this.wrap.firstChild;
35366         this.elRow = el;
35367         this.elNode = el.firstChild;
35368         this.ranchor = el.childNodes[1];
35369         this.ctNode = this.wrap.childNodes[1];
35370         var cs = el.firstChild.childNodes;
35371         this.indentNode = cs[0];
35372         this.ecNode = cs[1];
35373         this.iconNode = cs[2];
35374         var index = 3;
35375         if(cb){
35376             this.checkbox = cs[3];
35377             index++;
35378         }
35379         this.anchor = cs[index];
35380         
35381         this.textNode = cs[index].firstChild;
35382         
35383         //el.on("click", this.onClick, this);
35384         //el.on("dblclick", this.onDblClick, this);
35385         
35386         
35387        // console.log(this);
35388     },
35389     initEvents : function(){
35390         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35391         
35392             
35393         var a = this.ranchor;
35394
35395         var el = Roo.get(a);
35396
35397         if(Roo.isOpera){ // opera render bug ignores the CSS
35398             el.setStyle("text-decoration", "none");
35399         }
35400
35401         el.on("click", this.onClick, this);
35402         el.on("dblclick", this.onDblClick, this);
35403         el.on("contextmenu", this.onContextMenu, this);
35404         
35405     },
35406     
35407     /*onSelectedChange : function(state){
35408         if(state){
35409             this.focus();
35410             this.addClass("x-tree-selected");
35411         }else{
35412             //this.blur();
35413             this.removeClass("x-tree-selected");
35414         }
35415     },*/
35416     addClass : function(cls){
35417         if(this.elRow){
35418             Roo.fly(this.elRow).addClass(cls);
35419         }
35420         
35421     },
35422     
35423     
35424     removeClass : function(cls){
35425         if(this.elRow){
35426             Roo.fly(this.elRow).removeClass(cls);
35427         }
35428     }
35429
35430     
35431     
35432 });//<Script type="text/javascript">
35433
35434 /*
35435  * Based on:
35436  * Ext JS Library 1.1.1
35437  * Copyright(c) 2006-2007, Ext JS, LLC.
35438  *
35439  * Originally Released Under LGPL - original licence link has changed is not relivant.
35440  *
35441  * Fork - LGPL
35442  * <script type="text/javascript">
35443  */
35444  
35445
35446 /**
35447  * @class Roo.tree.ColumnTree
35448  * @extends Roo.data.TreePanel
35449  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35450  * @cfg {int} borderWidth  compined right/left border allowance
35451  * @constructor
35452  * @param {String/HTMLElement/Element} el The container element
35453  * @param {Object} config
35454  */
35455 Roo.tree.ColumnTree =  function(el, config)
35456 {
35457    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35458    this.addEvents({
35459         /**
35460         * @event resize
35461         * Fire this event on a container when it resizes
35462         * @param {int} w Width
35463         * @param {int} h Height
35464         */
35465        "resize" : true
35466     });
35467     this.on('resize', this.onResize, this);
35468 };
35469
35470 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35471     //lines:false,
35472     
35473     
35474     borderWidth: Roo.isBorderBox ? 0 : 2, 
35475     headEls : false,
35476     
35477     render : function(){
35478         // add the header.....
35479        
35480         Roo.tree.ColumnTree.superclass.render.apply(this);
35481         
35482         this.el.addClass('x-column-tree');
35483         
35484         this.headers = this.el.createChild(
35485             {cls:'x-tree-headers'},this.innerCt.dom);
35486    
35487         var cols = this.columns, c;
35488         var totalWidth = 0;
35489         this.headEls = [];
35490         var  len = cols.length;
35491         for(var i = 0; i < len; i++){
35492              c = cols[i];
35493              totalWidth += c.width;
35494             this.headEls.push(this.headers.createChild({
35495                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35496                  cn: {
35497                      cls:'x-tree-hd-text',
35498                      html: c.header
35499                  },
35500                  style:'width:'+(c.width-this.borderWidth)+'px;'
35501              }));
35502         }
35503         this.headers.createChild({cls:'x-clear'});
35504         // prevent floats from wrapping when clipped
35505         this.headers.setWidth(totalWidth);
35506         //this.innerCt.setWidth(totalWidth);
35507         this.innerCt.setStyle({ overflow: 'auto' });
35508         this.onResize(this.width, this.height);
35509              
35510         
35511     },
35512     onResize : function(w,h)
35513     {
35514         this.height = h;
35515         this.width = w;
35516         // resize cols..
35517         this.innerCt.setWidth(this.width);
35518         this.innerCt.setHeight(this.height-20);
35519         
35520         // headers...
35521         var cols = this.columns, c;
35522         var totalWidth = 0;
35523         var expEl = false;
35524         var len = cols.length;
35525         for(var i = 0; i < len; i++){
35526             c = cols[i];
35527             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35528                 // it's the expander..
35529                 expEl  = this.headEls[i];
35530                 continue;
35531             }
35532             totalWidth += c.width;
35533             
35534         }
35535         if (expEl) {
35536             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35537         }
35538         this.headers.setWidth(w-20);
35539
35540         
35541         
35542         
35543     }
35544 });
35545 /*
35546  * Based on:
35547  * Ext JS Library 1.1.1
35548  * Copyright(c) 2006-2007, Ext JS, LLC.
35549  *
35550  * Originally Released Under LGPL - original licence link has changed is not relivant.
35551  *
35552  * Fork - LGPL
35553  * <script type="text/javascript">
35554  */
35555  
35556 /**
35557  * @class Roo.menu.Menu
35558  * @extends Roo.util.Observable
35559  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35560  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35561  * @constructor
35562  * Creates a new Menu
35563  * @param {Object} config Configuration options
35564  */
35565 Roo.menu.Menu = function(config){
35566     Roo.apply(this, config);
35567     this.id = this.id || Roo.id();
35568     this.addEvents({
35569         /**
35570          * @event beforeshow
35571          * Fires before this menu is displayed
35572          * @param {Roo.menu.Menu} this
35573          */
35574         beforeshow : true,
35575         /**
35576          * @event beforehide
35577          * Fires before this menu is hidden
35578          * @param {Roo.menu.Menu} this
35579          */
35580         beforehide : true,
35581         /**
35582          * @event show
35583          * Fires after this menu is displayed
35584          * @param {Roo.menu.Menu} this
35585          */
35586         show : true,
35587         /**
35588          * @event hide
35589          * Fires after this menu is hidden
35590          * @param {Roo.menu.Menu} this
35591          */
35592         hide : true,
35593         /**
35594          * @event click
35595          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35596          * @param {Roo.menu.Menu} this
35597          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35598          * @param {Roo.EventObject} e
35599          */
35600         click : true,
35601         /**
35602          * @event mouseover
35603          * Fires when the mouse is hovering over this menu
35604          * @param {Roo.menu.Menu} this
35605          * @param {Roo.EventObject} e
35606          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35607          */
35608         mouseover : true,
35609         /**
35610          * @event mouseout
35611          * Fires when the mouse exits this menu
35612          * @param {Roo.menu.Menu} this
35613          * @param {Roo.EventObject} e
35614          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35615          */
35616         mouseout : true,
35617         /**
35618          * @event itemclick
35619          * Fires when a menu item contained in this menu is clicked
35620          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35621          * @param {Roo.EventObject} e
35622          */
35623         itemclick: true
35624     });
35625     if (this.registerMenu) {
35626         Roo.menu.MenuMgr.register(this);
35627     }
35628     
35629     var mis = this.items;
35630     this.items = new Roo.util.MixedCollection();
35631     if(mis){
35632         this.add.apply(this, mis);
35633     }
35634 };
35635
35636 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35637     /**
35638      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35639      */
35640     minWidth : 120,
35641     /**
35642      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35643      * for bottom-right shadow (defaults to "sides")
35644      */
35645     shadow : "sides",
35646     /**
35647      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35648      * this menu (defaults to "tl-tr?")
35649      */
35650     subMenuAlign : "tl-tr?",
35651     /**
35652      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35653      * relative to its element of origin (defaults to "tl-bl?")
35654      */
35655     defaultAlign : "tl-bl?",
35656     /**
35657      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35658      */
35659     allowOtherMenus : false,
35660     /**
35661      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35662      */
35663     registerMenu : true,
35664
35665     hidden:true,
35666
35667     // private
35668     render : function(){
35669         if(this.el){
35670             return;
35671         }
35672         var el = this.el = new Roo.Layer({
35673             cls: "x-menu",
35674             shadow:this.shadow,
35675             constrain: false,
35676             parentEl: this.parentEl || document.body,
35677             zindex:15000
35678         });
35679
35680         this.keyNav = new Roo.menu.MenuNav(this);
35681
35682         if(this.plain){
35683             el.addClass("x-menu-plain");
35684         }
35685         if(this.cls){
35686             el.addClass(this.cls);
35687         }
35688         // generic focus element
35689         this.focusEl = el.createChild({
35690             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35691         });
35692         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35693         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35694         
35695         ul.on("mouseover", this.onMouseOver, this);
35696         ul.on("mouseout", this.onMouseOut, this);
35697         this.items.each(function(item){
35698             if (item.hidden) {
35699                 return;
35700             }
35701             
35702             var li = document.createElement("li");
35703             li.className = "x-menu-list-item";
35704             ul.dom.appendChild(li);
35705             item.render(li, this);
35706         }, this);
35707         this.ul = ul;
35708         this.autoWidth();
35709     },
35710
35711     // private
35712     autoWidth : function(){
35713         var el = this.el, ul = this.ul;
35714         if(!el){
35715             return;
35716         }
35717         var w = this.width;
35718         if(w){
35719             el.setWidth(w);
35720         }else if(Roo.isIE){
35721             el.setWidth(this.minWidth);
35722             var t = el.dom.offsetWidth; // force recalc
35723             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35724         }
35725     },
35726
35727     // private
35728     delayAutoWidth : function(){
35729         if(this.rendered){
35730             if(!this.awTask){
35731                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35732             }
35733             this.awTask.delay(20);
35734         }
35735     },
35736
35737     // private
35738     findTargetItem : function(e){
35739         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35740         if(t && t.menuItemId){
35741             return this.items.get(t.menuItemId);
35742         }
35743     },
35744
35745     // private
35746     onClick : function(e){
35747         Roo.log("menu.onClick");
35748         var t = this.findTargetItem(e);
35749         if(!t){
35750             return;
35751         }
35752         Roo.log(e);
35753         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35754             if(t == this.activeItem && t.shouldDeactivate(e)){
35755                 this.activeItem.deactivate();
35756                 delete this.activeItem;
35757                 return;
35758             }
35759             if(t.canActivate){
35760                 this.setActiveItem(t, true);
35761             }
35762             return;
35763             
35764             
35765         }
35766         
35767         t.onClick(e);
35768         this.fireEvent("click", this, t, e);
35769     },
35770
35771     // private
35772     setActiveItem : function(item, autoExpand){
35773         if(item != this.activeItem){
35774             if(this.activeItem){
35775                 this.activeItem.deactivate();
35776             }
35777             this.activeItem = item;
35778             item.activate(autoExpand);
35779         }else if(autoExpand){
35780             item.expandMenu();
35781         }
35782     },
35783
35784     // private
35785     tryActivate : function(start, step){
35786         var items = this.items;
35787         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35788             var item = items.get(i);
35789             if(!item.disabled && item.canActivate){
35790                 this.setActiveItem(item, false);
35791                 return item;
35792             }
35793         }
35794         return false;
35795     },
35796
35797     // private
35798     onMouseOver : function(e){
35799         var t;
35800         if(t = this.findTargetItem(e)){
35801             if(t.canActivate && !t.disabled){
35802                 this.setActiveItem(t, true);
35803             }
35804         }
35805         this.fireEvent("mouseover", this, e, t);
35806     },
35807
35808     // private
35809     onMouseOut : function(e){
35810         var t;
35811         if(t = this.findTargetItem(e)){
35812             if(t == this.activeItem && t.shouldDeactivate(e)){
35813                 this.activeItem.deactivate();
35814                 delete this.activeItem;
35815             }
35816         }
35817         this.fireEvent("mouseout", this, e, t);
35818     },
35819
35820     /**
35821      * Read-only.  Returns true if the menu is currently displayed, else false.
35822      * @type Boolean
35823      */
35824     isVisible : function(){
35825         return this.el && !this.hidden;
35826     },
35827
35828     /**
35829      * Displays this menu relative to another element
35830      * @param {String/HTMLElement/Roo.Element} element The element to align to
35831      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35832      * the element (defaults to this.defaultAlign)
35833      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35834      */
35835     show : function(el, pos, parentMenu){
35836         this.parentMenu = parentMenu;
35837         if(!this.el){
35838             this.render();
35839         }
35840         this.fireEvent("beforeshow", this);
35841         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35842     },
35843
35844     /**
35845      * Displays this menu at a specific xy position
35846      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35847      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35848      */
35849     showAt : function(xy, parentMenu, /* private: */_e){
35850         this.parentMenu = parentMenu;
35851         if(!this.el){
35852             this.render();
35853         }
35854         if(_e !== false){
35855             this.fireEvent("beforeshow", this);
35856             xy = this.el.adjustForConstraints(xy);
35857         }
35858         this.el.setXY(xy);
35859         this.el.show();
35860         this.hidden = false;
35861         this.focus();
35862         this.fireEvent("show", this);
35863     },
35864
35865     focus : function(){
35866         if(!this.hidden){
35867             this.doFocus.defer(50, this);
35868         }
35869     },
35870
35871     doFocus : function(){
35872         if(!this.hidden){
35873             this.focusEl.focus();
35874         }
35875     },
35876
35877     /**
35878      * Hides this menu and optionally all parent menus
35879      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35880      */
35881     hide : function(deep){
35882         if(this.el && this.isVisible()){
35883             this.fireEvent("beforehide", this);
35884             if(this.activeItem){
35885                 this.activeItem.deactivate();
35886                 this.activeItem = null;
35887             }
35888             this.el.hide();
35889             this.hidden = true;
35890             this.fireEvent("hide", this);
35891         }
35892         if(deep === true && this.parentMenu){
35893             this.parentMenu.hide(true);
35894         }
35895     },
35896
35897     /**
35898      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35899      * Any of the following are valid:
35900      * <ul>
35901      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35902      * <li>An HTMLElement object which will be converted to a menu item</li>
35903      * <li>A menu item config object that will be created as a new menu item</li>
35904      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35905      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35906      * </ul>
35907      * Usage:
35908      * <pre><code>
35909 // Create the menu
35910 var menu = new Roo.menu.Menu();
35911
35912 // Create a menu item to add by reference
35913 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35914
35915 // Add a bunch of items at once using different methods.
35916 // Only the last item added will be returned.
35917 var item = menu.add(
35918     menuItem,                // add existing item by ref
35919     'Dynamic Item',          // new TextItem
35920     '-',                     // new separator
35921     { text: 'Config Item' }  // new item by config
35922 );
35923 </code></pre>
35924      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35925      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35926      */
35927     add : function(){
35928         var a = arguments, l = a.length, item;
35929         for(var i = 0; i < l; i++){
35930             var el = a[i];
35931             if ((typeof(el) == "object") && el.xtype && el.xns) {
35932                 el = Roo.factory(el, Roo.menu);
35933             }
35934             
35935             if(el.render){ // some kind of Item
35936                 item = this.addItem(el);
35937             }else if(typeof el == "string"){ // string
35938                 if(el == "separator" || el == "-"){
35939                     item = this.addSeparator();
35940                 }else{
35941                     item = this.addText(el);
35942                 }
35943             }else if(el.tagName || el.el){ // element
35944                 item = this.addElement(el);
35945             }else if(typeof el == "object"){ // must be menu item config?
35946                 item = this.addMenuItem(el);
35947             }
35948         }
35949         return item;
35950     },
35951
35952     /**
35953      * Returns this menu's underlying {@link Roo.Element} object
35954      * @return {Roo.Element} The element
35955      */
35956     getEl : function(){
35957         if(!this.el){
35958             this.render();
35959         }
35960         return this.el;
35961     },
35962
35963     /**
35964      * Adds a separator bar to the menu
35965      * @return {Roo.menu.Item} The menu item that was added
35966      */
35967     addSeparator : function(){
35968         return this.addItem(new Roo.menu.Separator());
35969     },
35970
35971     /**
35972      * Adds an {@link Roo.Element} object to the menu
35973      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35974      * @return {Roo.menu.Item} The menu item that was added
35975      */
35976     addElement : function(el){
35977         return this.addItem(new Roo.menu.BaseItem(el));
35978     },
35979
35980     /**
35981      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35982      * @param {Roo.menu.Item} item The menu item to add
35983      * @return {Roo.menu.Item} The menu item that was added
35984      */
35985     addItem : function(item){
35986         this.items.add(item);
35987         if(this.ul){
35988             var li = document.createElement("li");
35989             li.className = "x-menu-list-item";
35990             this.ul.dom.appendChild(li);
35991             item.render(li, this);
35992             this.delayAutoWidth();
35993         }
35994         return item;
35995     },
35996
35997     /**
35998      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35999      * @param {Object} config A MenuItem config object
36000      * @return {Roo.menu.Item} The menu item that was added
36001      */
36002     addMenuItem : function(config){
36003         if(!(config instanceof Roo.menu.Item)){
36004             if(typeof config.checked == "boolean"){ // must be check menu item config?
36005                 config = new Roo.menu.CheckItem(config);
36006             }else{
36007                 config = new Roo.menu.Item(config);
36008             }
36009         }
36010         return this.addItem(config);
36011     },
36012
36013     /**
36014      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
36015      * @param {String} text The text to display in the menu item
36016      * @return {Roo.menu.Item} The menu item that was added
36017      */
36018     addText : function(text){
36019         return this.addItem(new Roo.menu.TextItem({ text : text }));
36020     },
36021
36022     /**
36023      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
36024      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
36025      * @param {Roo.menu.Item} item The menu item to add
36026      * @return {Roo.menu.Item} The menu item that was added
36027      */
36028     insert : function(index, item){
36029         this.items.insert(index, item);
36030         if(this.ul){
36031             var li = document.createElement("li");
36032             li.className = "x-menu-list-item";
36033             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
36034             item.render(li, this);
36035             this.delayAutoWidth();
36036         }
36037         return item;
36038     },
36039
36040     /**
36041      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
36042      * @param {Roo.menu.Item} item The menu item to remove
36043      */
36044     remove : function(item){
36045         this.items.removeKey(item.id);
36046         item.destroy();
36047     },
36048
36049     /**
36050      * Removes and destroys all items in the menu
36051      */
36052     removeAll : function(){
36053         var f;
36054         while(f = this.items.first()){
36055             this.remove(f);
36056         }
36057     }
36058 });
36059
36060 // MenuNav is a private utility class used internally by the Menu
36061 Roo.menu.MenuNav = function(menu){
36062     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
36063     this.scope = this.menu = menu;
36064 };
36065
36066 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36067     doRelay : function(e, h){
36068         var k = e.getKey();
36069         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36070             this.menu.tryActivate(0, 1);
36071             return false;
36072         }
36073         return h.call(this.scope || this, e, this.menu);
36074     },
36075
36076     up : function(e, m){
36077         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36078             m.tryActivate(m.items.length-1, -1);
36079         }
36080     },
36081
36082     down : function(e, m){
36083         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36084             m.tryActivate(0, 1);
36085         }
36086     },
36087
36088     right : function(e, m){
36089         if(m.activeItem){
36090             m.activeItem.expandMenu(true);
36091         }
36092     },
36093
36094     left : function(e, m){
36095         m.hide();
36096         if(m.parentMenu && m.parentMenu.activeItem){
36097             m.parentMenu.activeItem.activate();
36098         }
36099     },
36100
36101     enter : function(e, m){
36102         if(m.activeItem){
36103             e.stopPropagation();
36104             m.activeItem.onClick(e);
36105             m.fireEvent("click", this, m.activeItem);
36106             return true;
36107         }
36108     }
36109 });/*
36110  * Based on:
36111  * Ext JS Library 1.1.1
36112  * Copyright(c) 2006-2007, Ext JS, LLC.
36113  *
36114  * Originally Released Under LGPL - original licence link has changed is not relivant.
36115  *
36116  * Fork - LGPL
36117  * <script type="text/javascript">
36118  */
36119  
36120 /**
36121  * @class Roo.menu.MenuMgr
36122  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36123  * @singleton
36124  */
36125 Roo.menu.MenuMgr = function(){
36126    var menus, active, groups = {}, attached = false, lastShow = new Date();
36127
36128    // private - called when first menu is created
36129    function init(){
36130        menus = {};
36131        active = new Roo.util.MixedCollection();
36132        Roo.get(document).addKeyListener(27, function(){
36133            if(active.length > 0){
36134                hideAll();
36135            }
36136        });
36137    }
36138
36139    // private
36140    function hideAll(){
36141        if(active && active.length > 0){
36142            var c = active.clone();
36143            c.each(function(m){
36144                m.hide();
36145            });
36146        }
36147    }
36148
36149    // private
36150    function onHide(m){
36151        active.remove(m);
36152        if(active.length < 1){
36153            Roo.get(document).un("mousedown", onMouseDown);
36154            attached = false;
36155        }
36156    }
36157
36158    // private
36159    function onShow(m){
36160        var last = active.last();
36161        lastShow = new Date();
36162        active.add(m);
36163        if(!attached){
36164            Roo.get(document).on("mousedown", onMouseDown);
36165            attached = true;
36166        }
36167        if(m.parentMenu){
36168           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36169           m.parentMenu.activeChild = m;
36170        }else if(last && last.isVisible()){
36171           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36172        }
36173    }
36174
36175    // private
36176    function onBeforeHide(m){
36177        if(m.activeChild){
36178            m.activeChild.hide();
36179        }
36180        if(m.autoHideTimer){
36181            clearTimeout(m.autoHideTimer);
36182            delete m.autoHideTimer;
36183        }
36184    }
36185
36186    // private
36187    function onBeforeShow(m){
36188        var pm = m.parentMenu;
36189        if(!pm && !m.allowOtherMenus){
36190            hideAll();
36191        }else if(pm && pm.activeChild && active != m){
36192            pm.activeChild.hide();
36193        }
36194    }
36195
36196    // private
36197    function onMouseDown(e){
36198        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36199            hideAll();
36200        }
36201    }
36202
36203    // private
36204    function onBeforeCheck(mi, state){
36205        if(state){
36206            var g = groups[mi.group];
36207            for(var i = 0, l = g.length; i < l; i++){
36208                if(g[i] != mi){
36209                    g[i].setChecked(false);
36210                }
36211            }
36212        }
36213    }
36214
36215    return {
36216
36217        /**
36218         * Hides all menus that are currently visible
36219         */
36220        hideAll : function(){
36221             hideAll();  
36222        },
36223
36224        // private
36225        register : function(menu){
36226            if(!menus){
36227                init();
36228            }
36229            menus[menu.id] = menu;
36230            menu.on("beforehide", onBeforeHide);
36231            menu.on("hide", onHide);
36232            menu.on("beforeshow", onBeforeShow);
36233            menu.on("show", onShow);
36234            var g = menu.group;
36235            if(g && menu.events["checkchange"]){
36236                if(!groups[g]){
36237                    groups[g] = [];
36238                }
36239                groups[g].push(menu);
36240                menu.on("checkchange", onCheck);
36241            }
36242        },
36243
36244         /**
36245          * Returns a {@link Roo.menu.Menu} object
36246          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36247          * be used to generate and return a new Menu instance.
36248          */
36249        get : function(menu){
36250            if(typeof menu == "string"){ // menu id
36251                return menus[menu];
36252            }else if(menu.events){  // menu instance
36253                return menu;
36254            }else if(typeof menu.length == 'number'){ // array of menu items?
36255                return new Roo.menu.Menu({items:menu});
36256            }else{ // otherwise, must be a config
36257                return new Roo.menu.Menu(menu);
36258            }
36259        },
36260
36261        // private
36262        unregister : function(menu){
36263            delete menus[menu.id];
36264            menu.un("beforehide", onBeforeHide);
36265            menu.un("hide", onHide);
36266            menu.un("beforeshow", onBeforeShow);
36267            menu.un("show", onShow);
36268            var g = menu.group;
36269            if(g && menu.events["checkchange"]){
36270                groups[g].remove(menu);
36271                menu.un("checkchange", onCheck);
36272            }
36273        },
36274
36275        // private
36276        registerCheckable : function(menuItem){
36277            var g = menuItem.group;
36278            if(g){
36279                if(!groups[g]){
36280                    groups[g] = [];
36281                }
36282                groups[g].push(menuItem);
36283                menuItem.on("beforecheckchange", onBeforeCheck);
36284            }
36285        },
36286
36287        // private
36288        unregisterCheckable : function(menuItem){
36289            var g = menuItem.group;
36290            if(g){
36291                groups[g].remove(menuItem);
36292                menuItem.un("beforecheckchange", onBeforeCheck);
36293            }
36294        }
36295    };
36296 }();/*
36297  * Based on:
36298  * Ext JS Library 1.1.1
36299  * Copyright(c) 2006-2007, Ext JS, LLC.
36300  *
36301  * Originally Released Under LGPL - original licence link has changed is not relivant.
36302  *
36303  * Fork - LGPL
36304  * <script type="text/javascript">
36305  */
36306  
36307
36308 /**
36309  * @class Roo.menu.BaseItem
36310  * @extends Roo.Component
36311  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36312  * management and base configuration options shared by all menu components.
36313  * @constructor
36314  * Creates a new BaseItem
36315  * @param {Object} config Configuration options
36316  */
36317 Roo.menu.BaseItem = function(config){
36318     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36319
36320     this.addEvents({
36321         /**
36322          * @event click
36323          * Fires when this item is clicked
36324          * @param {Roo.menu.BaseItem} this
36325          * @param {Roo.EventObject} e
36326          */
36327         click: true,
36328         /**
36329          * @event activate
36330          * Fires when this item is activated
36331          * @param {Roo.menu.BaseItem} this
36332          */
36333         activate : true,
36334         /**
36335          * @event deactivate
36336          * Fires when this item is deactivated
36337          * @param {Roo.menu.BaseItem} this
36338          */
36339         deactivate : true
36340     });
36341
36342     if(this.handler){
36343         this.on("click", this.handler, this.scope, true);
36344     }
36345 };
36346
36347 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36348     /**
36349      * @cfg {Function} handler
36350      * A function that will handle the click event of this menu item (defaults to undefined)
36351      */
36352     /**
36353      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36354      */
36355     canActivate : false,
36356     
36357      /**
36358      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36359      */
36360     hidden: false,
36361     
36362     /**
36363      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36364      */
36365     activeClass : "x-menu-item-active",
36366     /**
36367      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36368      */
36369     hideOnClick : true,
36370     /**
36371      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36372      */
36373     hideDelay : 100,
36374
36375     // private
36376     ctype: "Roo.menu.BaseItem",
36377
36378     // private
36379     actionMode : "container",
36380
36381     // private
36382     render : function(container, parentMenu){
36383         this.parentMenu = parentMenu;
36384         Roo.menu.BaseItem.superclass.render.call(this, container);
36385         this.container.menuItemId = this.id;
36386     },
36387
36388     // private
36389     onRender : function(container, position){
36390         this.el = Roo.get(this.el);
36391         container.dom.appendChild(this.el.dom);
36392     },
36393
36394     // private
36395     onClick : function(e){
36396         if(!this.disabled && this.fireEvent("click", this, e) !== false
36397                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36398             this.handleClick(e);
36399         }else{
36400             e.stopEvent();
36401         }
36402     },
36403
36404     // private
36405     activate : function(){
36406         if(this.disabled){
36407             return false;
36408         }
36409         var li = this.container;
36410         li.addClass(this.activeClass);
36411         this.region = li.getRegion().adjust(2, 2, -2, -2);
36412         this.fireEvent("activate", this);
36413         return true;
36414     },
36415
36416     // private
36417     deactivate : function(){
36418         this.container.removeClass(this.activeClass);
36419         this.fireEvent("deactivate", this);
36420     },
36421
36422     // private
36423     shouldDeactivate : function(e){
36424         return !this.region || !this.region.contains(e.getPoint());
36425     },
36426
36427     // private
36428     handleClick : function(e){
36429         if(this.hideOnClick){
36430             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36431         }
36432     },
36433
36434     // private
36435     expandMenu : function(autoActivate){
36436         // do nothing
36437     },
36438
36439     // private
36440     hideMenu : function(){
36441         // do nothing
36442     }
36443 });/*
36444  * Based on:
36445  * Ext JS Library 1.1.1
36446  * Copyright(c) 2006-2007, Ext JS, LLC.
36447  *
36448  * Originally Released Under LGPL - original licence link has changed is not relivant.
36449  *
36450  * Fork - LGPL
36451  * <script type="text/javascript">
36452  */
36453  
36454 /**
36455  * @class Roo.menu.Adapter
36456  * @extends Roo.menu.BaseItem
36457  * 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.
36458  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36459  * @constructor
36460  * Creates a new Adapter
36461  * @param {Object} config Configuration options
36462  */
36463 Roo.menu.Adapter = function(component, config){
36464     Roo.menu.Adapter.superclass.constructor.call(this, config);
36465     this.component = component;
36466 };
36467 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36468     // private
36469     canActivate : true,
36470
36471     // private
36472     onRender : function(container, position){
36473         this.component.render(container);
36474         this.el = this.component.getEl();
36475     },
36476
36477     // private
36478     activate : function(){
36479         if(this.disabled){
36480             return false;
36481         }
36482         this.component.focus();
36483         this.fireEvent("activate", this);
36484         return true;
36485     },
36486
36487     // private
36488     deactivate : function(){
36489         this.fireEvent("deactivate", this);
36490     },
36491
36492     // private
36493     disable : function(){
36494         this.component.disable();
36495         Roo.menu.Adapter.superclass.disable.call(this);
36496     },
36497
36498     // private
36499     enable : function(){
36500         this.component.enable();
36501         Roo.menu.Adapter.superclass.enable.call(this);
36502     }
36503 });/*
36504  * Based on:
36505  * Ext JS Library 1.1.1
36506  * Copyright(c) 2006-2007, Ext JS, LLC.
36507  *
36508  * Originally Released Under LGPL - original licence link has changed is not relivant.
36509  *
36510  * Fork - LGPL
36511  * <script type="text/javascript">
36512  */
36513
36514 /**
36515  * @class Roo.menu.TextItem
36516  * @extends Roo.menu.BaseItem
36517  * Adds a static text string to a menu, usually used as either a heading or group separator.
36518  * Note: old style constructor with text is still supported.
36519  * 
36520  * @constructor
36521  * Creates a new TextItem
36522  * @param {Object} cfg Configuration
36523  */
36524 Roo.menu.TextItem = function(cfg){
36525     if (typeof(cfg) == 'string') {
36526         this.text = cfg;
36527     } else {
36528         Roo.apply(this,cfg);
36529     }
36530     
36531     Roo.menu.TextItem.superclass.constructor.call(this);
36532 };
36533
36534 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36535     /**
36536      * @cfg {Boolean} text Text to show on item.
36537      */
36538     text : '',
36539     
36540     /**
36541      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36542      */
36543     hideOnClick : false,
36544     /**
36545      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36546      */
36547     itemCls : "x-menu-text",
36548
36549     // private
36550     onRender : function(){
36551         var s = document.createElement("span");
36552         s.className = this.itemCls;
36553         s.innerHTML = this.text;
36554         this.el = s;
36555         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36556     }
36557 });/*
36558  * Based on:
36559  * Ext JS Library 1.1.1
36560  * Copyright(c) 2006-2007, Ext JS, LLC.
36561  *
36562  * Originally Released Under LGPL - original licence link has changed is not relivant.
36563  *
36564  * Fork - LGPL
36565  * <script type="text/javascript">
36566  */
36567
36568 /**
36569  * @class Roo.menu.Separator
36570  * @extends Roo.menu.BaseItem
36571  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36572  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36573  * @constructor
36574  * @param {Object} config Configuration options
36575  */
36576 Roo.menu.Separator = function(config){
36577     Roo.menu.Separator.superclass.constructor.call(this, config);
36578 };
36579
36580 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36581     /**
36582      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36583      */
36584     itemCls : "x-menu-sep",
36585     /**
36586      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36587      */
36588     hideOnClick : false,
36589
36590     // private
36591     onRender : function(li){
36592         var s = document.createElement("span");
36593         s.className = this.itemCls;
36594         s.innerHTML = "&#160;";
36595         this.el = s;
36596         li.addClass("x-menu-sep-li");
36597         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36598     }
36599 });/*
36600  * Based on:
36601  * Ext JS Library 1.1.1
36602  * Copyright(c) 2006-2007, Ext JS, LLC.
36603  *
36604  * Originally Released Under LGPL - original licence link has changed is not relivant.
36605  *
36606  * Fork - LGPL
36607  * <script type="text/javascript">
36608  */
36609 /**
36610  * @class Roo.menu.Item
36611  * @extends Roo.menu.BaseItem
36612  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36613  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36614  * activation and click handling.
36615  * @constructor
36616  * Creates a new Item
36617  * @param {Object} config Configuration options
36618  */
36619 Roo.menu.Item = function(config){
36620     Roo.menu.Item.superclass.constructor.call(this, config);
36621     if(this.menu){
36622         this.menu = Roo.menu.MenuMgr.get(this.menu);
36623     }
36624 };
36625 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36626     
36627     /**
36628      * @cfg {String} text
36629      * The text to show on the menu item.
36630      */
36631     text: '',
36632      /**
36633      * @cfg {String} HTML to render in menu
36634      * The text to show on the menu item (HTML version).
36635      */
36636     html: '',
36637     /**
36638      * @cfg {String} icon
36639      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36640      */
36641     icon: undefined,
36642     /**
36643      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36644      */
36645     itemCls : "x-menu-item",
36646     /**
36647      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36648      */
36649     canActivate : true,
36650     /**
36651      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36652      */
36653     showDelay: 200,
36654     // doc'd in BaseItem
36655     hideDelay: 200,
36656
36657     // private
36658     ctype: "Roo.menu.Item",
36659     
36660     // private
36661     onRender : function(container, position){
36662         var el = document.createElement("a");
36663         el.hideFocus = true;
36664         el.unselectable = "on";
36665         el.href = this.href || "#";
36666         if(this.hrefTarget){
36667             el.target = this.hrefTarget;
36668         }
36669         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36670         
36671         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36672         
36673         el.innerHTML = String.format(
36674                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36675                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36676         this.el = el;
36677         Roo.menu.Item.superclass.onRender.call(this, container, position);
36678     },
36679
36680     /**
36681      * Sets the text to display in this menu item
36682      * @param {String} text The text to display
36683      * @param {Boolean} isHTML true to indicate text is pure html.
36684      */
36685     setText : function(text, isHTML){
36686         if (isHTML) {
36687             this.html = text;
36688         } else {
36689             this.text = text;
36690             this.html = '';
36691         }
36692         if(this.rendered){
36693             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36694      
36695             this.el.update(String.format(
36696                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36697                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36698             this.parentMenu.autoWidth();
36699         }
36700     },
36701
36702     // private
36703     handleClick : function(e){
36704         if(!this.href){ // if no link defined, stop the event automatically
36705             e.stopEvent();
36706         }
36707         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36708     },
36709
36710     // private
36711     activate : function(autoExpand){
36712         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36713             this.focus();
36714             if(autoExpand){
36715                 this.expandMenu();
36716             }
36717         }
36718         return true;
36719     },
36720
36721     // private
36722     shouldDeactivate : function(e){
36723         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36724             if(this.menu && this.menu.isVisible()){
36725                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36726             }
36727             return true;
36728         }
36729         return false;
36730     },
36731
36732     // private
36733     deactivate : function(){
36734         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36735         this.hideMenu();
36736     },
36737
36738     // private
36739     expandMenu : function(autoActivate){
36740         if(!this.disabled && this.menu){
36741             clearTimeout(this.hideTimer);
36742             delete this.hideTimer;
36743             if(!this.menu.isVisible() && !this.showTimer){
36744                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36745             }else if (this.menu.isVisible() && autoActivate){
36746                 this.menu.tryActivate(0, 1);
36747             }
36748         }
36749     },
36750
36751     // private
36752     deferExpand : function(autoActivate){
36753         delete this.showTimer;
36754         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36755         if(autoActivate){
36756             this.menu.tryActivate(0, 1);
36757         }
36758     },
36759
36760     // private
36761     hideMenu : function(){
36762         clearTimeout(this.showTimer);
36763         delete this.showTimer;
36764         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36765             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36766         }
36767     },
36768
36769     // private
36770     deferHide : function(){
36771         delete this.hideTimer;
36772         this.menu.hide();
36773     }
36774 });/*
36775  * Based on:
36776  * Ext JS Library 1.1.1
36777  * Copyright(c) 2006-2007, Ext JS, LLC.
36778  *
36779  * Originally Released Under LGPL - original licence link has changed is not relivant.
36780  *
36781  * Fork - LGPL
36782  * <script type="text/javascript">
36783  */
36784  
36785 /**
36786  * @class Roo.menu.CheckItem
36787  * @extends Roo.menu.Item
36788  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36789  * @constructor
36790  * Creates a new CheckItem
36791  * @param {Object} config Configuration options
36792  */
36793 Roo.menu.CheckItem = function(config){
36794     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36795     this.addEvents({
36796         /**
36797          * @event beforecheckchange
36798          * Fires before the checked value is set, providing an opportunity to cancel if needed
36799          * @param {Roo.menu.CheckItem} this
36800          * @param {Boolean} checked The new checked value that will be set
36801          */
36802         "beforecheckchange" : true,
36803         /**
36804          * @event checkchange
36805          * Fires after the checked value has been set
36806          * @param {Roo.menu.CheckItem} this
36807          * @param {Boolean} checked The checked value that was set
36808          */
36809         "checkchange" : true
36810     });
36811     if(this.checkHandler){
36812         this.on('checkchange', this.checkHandler, this.scope);
36813     }
36814 };
36815 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36816     /**
36817      * @cfg {String} group
36818      * All check items with the same group name will automatically be grouped into a single-select
36819      * radio button group (defaults to '')
36820      */
36821     /**
36822      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36823      */
36824     itemCls : "x-menu-item x-menu-check-item",
36825     /**
36826      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36827      */
36828     groupClass : "x-menu-group-item",
36829
36830     /**
36831      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36832      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36833      * initialized with checked = true will be rendered as checked.
36834      */
36835     checked: false,
36836
36837     // private
36838     ctype: "Roo.menu.CheckItem",
36839
36840     // private
36841     onRender : function(c){
36842         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36843         if(this.group){
36844             this.el.addClass(this.groupClass);
36845         }
36846         Roo.menu.MenuMgr.registerCheckable(this);
36847         if(this.checked){
36848             this.checked = false;
36849             this.setChecked(true, true);
36850         }
36851     },
36852
36853     // private
36854     destroy : function(){
36855         if(this.rendered){
36856             Roo.menu.MenuMgr.unregisterCheckable(this);
36857         }
36858         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36859     },
36860
36861     /**
36862      * Set the checked state of this item
36863      * @param {Boolean} checked The new checked value
36864      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36865      */
36866     setChecked : function(state, suppressEvent){
36867         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36868             if(this.container){
36869                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36870             }
36871             this.checked = state;
36872             if(suppressEvent !== true){
36873                 this.fireEvent("checkchange", this, state);
36874             }
36875         }
36876     },
36877
36878     // private
36879     handleClick : function(e){
36880        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36881            this.setChecked(!this.checked);
36882        }
36883        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36884     }
36885 });/*
36886  * Based on:
36887  * Ext JS Library 1.1.1
36888  * Copyright(c) 2006-2007, Ext JS, LLC.
36889  *
36890  * Originally Released Under LGPL - original licence link has changed is not relivant.
36891  *
36892  * Fork - LGPL
36893  * <script type="text/javascript">
36894  */
36895  
36896 /**
36897  * @class Roo.menu.DateItem
36898  * @extends Roo.menu.Adapter
36899  * A menu item that wraps the {@link Roo.DatPicker} component.
36900  * @constructor
36901  * Creates a new DateItem
36902  * @param {Object} config Configuration options
36903  */
36904 Roo.menu.DateItem = function(config){
36905     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36906     /** The Roo.DatePicker object @type Roo.DatePicker */
36907     this.picker = this.component;
36908     this.addEvents({select: true});
36909     
36910     this.picker.on("render", function(picker){
36911         picker.getEl().swallowEvent("click");
36912         picker.container.addClass("x-menu-date-item");
36913     });
36914
36915     this.picker.on("select", this.onSelect, this);
36916 };
36917
36918 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36919     // private
36920     onSelect : function(picker, date){
36921         this.fireEvent("select", this, date, picker);
36922         Roo.menu.DateItem.superclass.handleClick.call(this);
36923     }
36924 });/*
36925  * Based on:
36926  * Ext JS Library 1.1.1
36927  * Copyright(c) 2006-2007, Ext JS, LLC.
36928  *
36929  * Originally Released Under LGPL - original licence link has changed is not relivant.
36930  *
36931  * Fork - LGPL
36932  * <script type="text/javascript">
36933  */
36934  
36935 /**
36936  * @class Roo.menu.ColorItem
36937  * @extends Roo.menu.Adapter
36938  * A menu item that wraps the {@link Roo.ColorPalette} component.
36939  * @constructor
36940  * Creates a new ColorItem
36941  * @param {Object} config Configuration options
36942  */
36943 Roo.menu.ColorItem = function(config){
36944     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36945     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36946     this.palette = this.component;
36947     this.relayEvents(this.palette, ["select"]);
36948     if(this.selectHandler){
36949         this.on('select', this.selectHandler, this.scope);
36950     }
36951 };
36952 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36953  * Based on:
36954  * Ext JS Library 1.1.1
36955  * Copyright(c) 2006-2007, Ext JS, LLC.
36956  *
36957  * Originally Released Under LGPL - original licence link has changed is not relivant.
36958  *
36959  * Fork - LGPL
36960  * <script type="text/javascript">
36961  */
36962  
36963
36964 /**
36965  * @class Roo.menu.DateMenu
36966  * @extends Roo.menu.Menu
36967  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36968  * @constructor
36969  * Creates a new DateMenu
36970  * @param {Object} config Configuration options
36971  */
36972 Roo.menu.DateMenu = function(config){
36973     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36974     this.plain = true;
36975     var di = new Roo.menu.DateItem(config);
36976     this.add(di);
36977     /**
36978      * The {@link Roo.DatePicker} instance for this DateMenu
36979      * @type DatePicker
36980      */
36981     this.picker = di.picker;
36982     /**
36983      * @event select
36984      * @param {DatePicker} picker
36985      * @param {Date} date
36986      */
36987     this.relayEvents(di, ["select"]);
36988     this.on('beforeshow', function(){
36989         if(this.picker){
36990             this.picker.hideMonthPicker(false);
36991         }
36992     }, this);
36993 };
36994 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36995     cls:'x-date-menu'
36996 });/*
36997  * Based on:
36998  * Ext JS Library 1.1.1
36999  * Copyright(c) 2006-2007, Ext JS, LLC.
37000  *
37001  * Originally Released Under LGPL - original licence link has changed is not relivant.
37002  *
37003  * Fork - LGPL
37004  * <script type="text/javascript">
37005  */
37006  
37007
37008 /**
37009  * @class Roo.menu.ColorMenu
37010  * @extends Roo.menu.Menu
37011  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
37012  * @constructor
37013  * Creates a new ColorMenu
37014  * @param {Object} config Configuration options
37015  */
37016 Roo.menu.ColorMenu = function(config){
37017     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
37018     this.plain = true;
37019     var ci = new Roo.menu.ColorItem(config);
37020     this.add(ci);
37021     /**
37022      * The {@link Roo.ColorPalette} instance for this ColorMenu
37023      * @type ColorPalette
37024      */
37025     this.palette = ci.palette;
37026     /**
37027      * @event select
37028      * @param {ColorPalette} palette
37029      * @param {String} color
37030      */
37031     this.relayEvents(ci, ["select"]);
37032 };
37033 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
37034  * Based on:
37035  * Ext JS Library 1.1.1
37036  * Copyright(c) 2006-2007, Ext JS, LLC.
37037  *
37038  * Originally Released Under LGPL - original licence link has changed is not relivant.
37039  *
37040  * Fork - LGPL
37041  * <script type="text/javascript">
37042  */
37043  
37044 /**
37045  * @class Roo.form.Field
37046  * @extends Roo.BoxComponent
37047  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
37048  * @constructor
37049  * Creates a new Field
37050  * @param {Object} config Configuration options
37051  */
37052 Roo.form.Field = function(config){
37053     Roo.form.Field.superclass.constructor.call(this, config);
37054 };
37055
37056 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
37057     /**
37058      * @cfg {String} fieldLabel Label to use when rendering a form.
37059      */
37060        /**
37061      * @cfg {String} qtip Mouse over tip
37062      */
37063      
37064     /**
37065      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37066      */
37067     invalidClass : "x-form-invalid",
37068     /**
37069      * @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")
37070      */
37071     invalidText : "The value in this field is invalid",
37072     /**
37073      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37074      */
37075     focusClass : "x-form-focus",
37076     /**
37077      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37078       automatic validation (defaults to "keyup").
37079      */
37080     validationEvent : "keyup",
37081     /**
37082      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37083      */
37084     validateOnBlur : true,
37085     /**
37086      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37087      */
37088     validationDelay : 250,
37089     /**
37090      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37091      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37092      */
37093     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
37094     /**
37095      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37096      */
37097     fieldClass : "x-form-field",
37098     /**
37099      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37100      *<pre>
37101 Value         Description
37102 -----------   ----------------------------------------------------------------------
37103 qtip          Display a quick tip when the user hovers over the field
37104 title         Display a default browser title attribute popup
37105 under         Add a block div beneath the field containing the error text
37106 side          Add an error icon to the right of the field with a popup on hover
37107 [element id]  Add the error text directly to the innerHTML of the specified element
37108 </pre>
37109      */
37110     msgTarget : 'qtip',
37111     /**
37112      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37113      */
37114     msgFx : 'normal',
37115
37116     /**
37117      * @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.
37118      */
37119     readOnly : false,
37120
37121     /**
37122      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37123      */
37124     disabled : false,
37125
37126     /**
37127      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37128      */
37129     inputType : undefined,
37130     
37131     /**
37132      * @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).
37133          */
37134         tabIndex : undefined,
37135         
37136     // private
37137     isFormField : true,
37138
37139     // private
37140     hasFocus : false,
37141     /**
37142      * @property {Roo.Element} fieldEl
37143      * Element Containing the rendered Field (with label etc.)
37144      */
37145     /**
37146      * @cfg {Mixed} value A value to initialize this field with.
37147      */
37148     value : undefined,
37149
37150     /**
37151      * @cfg {String} name The field's HTML name attribute.
37152      */
37153     /**
37154      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37155      */
37156
37157         // private ??
37158         initComponent : function(){
37159         Roo.form.Field.superclass.initComponent.call(this);
37160         this.addEvents({
37161             /**
37162              * @event focus
37163              * Fires when this field receives input focus.
37164              * @param {Roo.form.Field} this
37165              */
37166             focus : true,
37167             /**
37168              * @event blur
37169              * Fires when this field loses input focus.
37170              * @param {Roo.form.Field} this
37171              */
37172             blur : true,
37173             /**
37174              * @event specialkey
37175              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37176              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37177              * @param {Roo.form.Field} this
37178              * @param {Roo.EventObject} e The event object
37179              */
37180             specialkey : true,
37181             /**
37182              * @event change
37183              * Fires just before the field blurs if the field value has changed.
37184              * @param {Roo.form.Field} this
37185              * @param {Mixed} newValue The new value
37186              * @param {Mixed} oldValue The original value
37187              */
37188             change : true,
37189             /**
37190              * @event invalid
37191              * Fires after the field has been marked as invalid.
37192              * @param {Roo.form.Field} this
37193              * @param {String} msg The validation message
37194              */
37195             invalid : true,
37196             /**
37197              * @event valid
37198              * Fires after the field has been validated with no errors.
37199              * @param {Roo.form.Field} this
37200              */
37201             valid : true,
37202              /**
37203              * @event keyup
37204              * Fires after the key up
37205              * @param {Roo.form.Field} this
37206              * @param {Roo.EventObject}  e The event Object
37207              */
37208             keyup : true
37209         });
37210     },
37211
37212     /**
37213      * Returns the name attribute of the field if available
37214      * @return {String} name The field name
37215      */
37216     getName: function(){
37217          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37218     },
37219
37220     // private
37221     onRender : function(ct, position){
37222         Roo.form.Field.superclass.onRender.call(this, ct, position);
37223         if(!this.el){
37224             var cfg = this.getAutoCreate();
37225             if(!cfg.name){
37226                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37227             }
37228             if (!cfg.name.length) {
37229                 delete cfg.name;
37230             }
37231             if(this.inputType){
37232                 cfg.type = this.inputType;
37233             }
37234             this.el = ct.createChild(cfg, position);
37235         }
37236         var type = this.el.dom.type;
37237         if(type){
37238             if(type == 'password'){
37239                 type = 'text';
37240             }
37241             this.el.addClass('x-form-'+type);
37242         }
37243         if(this.readOnly){
37244             this.el.dom.readOnly = true;
37245         }
37246         if(this.tabIndex !== undefined){
37247             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37248         }
37249
37250         this.el.addClass([this.fieldClass, this.cls]);
37251         this.initValue();
37252     },
37253
37254     /**
37255      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37256      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37257      * @return {Roo.form.Field} this
37258      */
37259     applyTo : function(target){
37260         this.allowDomMove = false;
37261         this.el = Roo.get(target);
37262         this.render(this.el.dom.parentNode);
37263         return this;
37264     },
37265
37266     // private
37267     initValue : function(){
37268         if(this.value !== undefined){
37269             this.setValue(this.value);
37270         }else if(this.el.dom.value.length > 0){
37271             this.setValue(this.el.dom.value);
37272         }
37273     },
37274
37275     /**
37276      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37277      */
37278     isDirty : function() {
37279         if(this.disabled) {
37280             return false;
37281         }
37282         return String(this.getValue()) !== String(this.originalValue);
37283     },
37284
37285     // private
37286     afterRender : function(){
37287         Roo.form.Field.superclass.afterRender.call(this);
37288         this.initEvents();
37289     },
37290
37291     // private
37292     fireKey : function(e){
37293         //Roo.log('field ' + e.getKey());
37294         if(e.isNavKeyPress()){
37295             this.fireEvent("specialkey", this, e);
37296         }
37297     },
37298
37299     /**
37300      * Resets the current field value to the originally loaded value and clears any validation messages
37301      */
37302     reset : function(){
37303         this.setValue(this.resetValue);
37304         this.clearInvalid();
37305     },
37306
37307     // private
37308     initEvents : function(){
37309         // safari killled keypress - so keydown is now used..
37310         this.el.on("keydown" , this.fireKey,  this);
37311         this.el.on("focus", this.onFocus,  this);
37312         this.el.on("blur", this.onBlur,  this);
37313         this.el.relayEvent('keyup', this);
37314
37315         // reference to original value for reset
37316         this.originalValue = this.getValue();
37317         this.resetValue =  this.getValue();
37318     },
37319
37320     // private
37321     onFocus : function(){
37322         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37323             this.el.addClass(this.focusClass);
37324         }
37325         if(!this.hasFocus){
37326             this.hasFocus = true;
37327             this.startValue = this.getValue();
37328             this.fireEvent("focus", this);
37329         }
37330     },
37331
37332     beforeBlur : Roo.emptyFn,
37333
37334     // private
37335     onBlur : function(){
37336         this.beforeBlur();
37337         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37338             this.el.removeClass(this.focusClass);
37339         }
37340         this.hasFocus = false;
37341         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37342             this.validate();
37343         }
37344         var v = this.getValue();
37345         if(String(v) !== String(this.startValue)){
37346             this.fireEvent('change', this, v, this.startValue);
37347         }
37348         this.fireEvent("blur", this);
37349     },
37350
37351     /**
37352      * Returns whether or not the field value is currently valid
37353      * @param {Boolean} preventMark True to disable marking the field invalid
37354      * @return {Boolean} True if the value is valid, else false
37355      */
37356     isValid : function(preventMark){
37357         if(this.disabled){
37358             return true;
37359         }
37360         var restore = this.preventMark;
37361         this.preventMark = preventMark === true;
37362         var v = this.validateValue(this.processValue(this.getRawValue()));
37363         this.preventMark = restore;
37364         return v;
37365     },
37366
37367     /**
37368      * Validates the field value
37369      * @return {Boolean} True if the value is valid, else false
37370      */
37371     validate : function(){
37372         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37373             this.clearInvalid();
37374             return true;
37375         }
37376         return false;
37377     },
37378
37379     processValue : function(value){
37380         return value;
37381     },
37382
37383     // private
37384     // Subclasses should provide the validation implementation by overriding this
37385     validateValue : function(value){
37386         return true;
37387     },
37388
37389     /**
37390      * Mark this field as invalid
37391      * @param {String} msg The validation message
37392      */
37393     markInvalid : function(msg){
37394         if(!this.rendered || this.preventMark){ // not rendered
37395             return;
37396         }
37397         
37398         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37399         
37400         obj.el.addClass(this.invalidClass);
37401         msg = msg || this.invalidText;
37402         switch(this.msgTarget){
37403             case 'qtip':
37404                 obj.el.dom.qtip = msg;
37405                 obj.el.dom.qclass = 'x-form-invalid-tip';
37406                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37407                     Roo.QuickTips.enable();
37408                 }
37409                 break;
37410             case 'title':
37411                 this.el.dom.title = msg;
37412                 break;
37413             case 'under':
37414                 if(!this.errorEl){
37415                     var elp = this.el.findParent('.x-form-element', 5, true);
37416                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37417                     this.errorEl.setWidth(elp.getWidth(true)-20);
37418                 }
37419                 this.errorEl.update(msg);
37420                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37421                 break;
37422             case 'side':
37423                 if(!this.errorIcon){
37424                     var elp = this.el.findParent('.x-form-element', 5, true);
37425                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37426                 }
37427                 this.alignErrorIcon();
37428                 this.errorIcon.dom.qtip = msg;
37429                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37430                 this.errorIcon.show();
37431                 this.on('resize', this.alignErrorIcon, this);
37432                 break;
37433             default:
37434                 var t = Roo.getDom(this.msgTarget);
37435                 t.innerHTML = msg;
37436                 t.style.display = this.msgDisplay;
37437                 break;
37438         }
37439         this.fireEvent('invalid', this, msg);
37440     },
37441
37442     // private
37443     alignErrorIcon : function(){
37444         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37445     },
37446
37447     /**
37448      * Clear any invalid styles/messages for this field
37449      */
37450     clearInvalid : function(){
37451         if(!this.rendered || this.preventMark){ // not rendered
37452             return;
37453         }
37454         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37455         
37456         obj.el.removeClass(this.invalidClass);
37457         switch(this.msgTarget){
37458             case 'qtip':
37459                 obj.el.dom.qtip = '';
37460                 break;
37461             case 'title':
37462                 this.el.dom.title = '';
37463                 break;
37464             case 'under':
37465                 if(this.errorEl){
37466                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37467                 }
37468                 break;
37469             case 'side':
37470                 if(this.errorIcon){
37471                     this.errorIcon.dom.qtip = '';
37472                     this.errorIcon.hide();
37473                     this.un('resize', this.alignErrorIcon, this);
37474                 }
37475                 break;
37476             default:
37477                 var t = Roo.getDom(this.msgTarget);
37478                 t.innerHTML = '';
37479                 t.style.display = 'none';
37480                 break;
37481         }
37482         this.fireEvent('valid', this);
37483     },
37484
37485     /**
37486      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37487      * @return {Mixed} value The field value
37488      */
37489     getRawValue : function(){
37490         var v = this.el.getValue();
37491         
37492         return v;
37493     },
37494
37495     /**
37496      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37497      * @return {Mixed} value The field value
37498      */
37499     getValue : function(){
37500         var v = this.el.getValue();
37501          
37502         return v;
37503     },
37504
37505     /**
37506      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37507      * @param {Mixed} value The value to set
37508      */
37509     setRawValue : function(v){
37510         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37511     },
37512
37513     /**
37514      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37515      * @param {Mixed} value The value to set
37516      */
37517     setValue : function(v){
37518         this.value = v;
37519         if(this.rendered){
37520             this.el.dom.value = (v === null || v === undefined ? '' : v);
37521              this.validate();
37522         }
37523     },
37524
37525     adjustSize : function(w, h){
37526         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37527         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37528         return s;
37529     },
37530
37531     adjustWidth : function(tag, w){
37532         tag = tag.toLowerCase();
37533         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37534             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37535                 if(tag == 'input'){
37536                     return w + 2;
37537                 }
37538                 if(tag == 'textarea'){
37539                     return w-2;
37540                 }
37541             }else if(Roo.isOpera){
37542                 if(tag == 'input'){
37543                     return w + 2;
37544                 }
37545                 if(tag == 'textarea'){
37546                     return w-2;
37547                 }
37548             }
37549         }
37550         return w;
37551     }
37552 });
37553
37554
37555 // anything other than normal should be considered experimental
37556 Roo.form.Field.msgFx = {
37557     normal : {
37558         show: function(msgEl, f){
37559             msgEl.setDisplayed('block');
37560         },
37561
37562         hide : function(msgEl, f){
37563             msgEl.setDisplayed(false).update('');
37564         }
37565     },
37566
37567     slide : {
37568         show: function(msgEl, f){
37569             msgEl.slideIn('t', {stopFx:true});
37570         },
37571
37572         hide : function(msgEl, f){
37573             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37574         }
37575     },
37576
37577     slideRight : {
37578         show: function(msgEl, f){
37579             msgEl.fixDisplay();
37580             msgEl.alignTo(f.el, 'tl-tr');
37581             msgEl.slideIn('l', {stopFx:true});
37582         },
37583
37584         hide : function(msgEl, f){
37585             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37586         }
37587     }
37588 };/*
37589  * Based on:
37590  * Ext JS Library 1.1.1
37591  * Copyright(c) 2006-2007, Ext JS, LLC.
37592  *
37593  * Originally Released Under LGPL - original licence link has changed is not relivant.
37594  *
37595  * Fork - LGPL
37596  * <script type="text/javascript">
37597  */
37598  
37599
37600 /**
37601  * @class Roo.form.TextField
37602  * @extends Roo.form.Field
37603  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37604  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37605  * @constructor
37606  * Creates a new TextField
37607  * @param {Object} config Configuration options
37608  */
37609 Roo.form.TextField = function(config){
37610     Roo.form.TextField.superclass.constructor.call(this, config);
37611     this.addEvents({
37612         /**
37613          * @event autosize
37614          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37615          * according to the default logic, but this event provides a hook for the developer to apply additional
37616          * logic at runtime to resize the field if needed.
37617              * @param {Roo.form.Field} this This text field
37618              * @param {Number} width The new field width
37619              */
37620         autosize : true
37621     });
37622 };
37623
37624 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37625     /**
37626      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37627      */
37628     grow : false,
37629     /**
37630      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37631      */
37632     growMin : 30,
37633     /**
37634      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37635      */
37636     growMax : 800,
37637     /**
37638      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37639      */
37640     vtype : null,
37641     /**
37642      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37643      */
37644     maskRe : null,
37645     /**
37646      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37647      */
37648     disableKeyFilter : false,
37649     /**
37650      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37651      */
37652     allowBlank : true,
37653     /**
37654      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37655      */
37656     minLength : 0,
37657     /**
37658      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37659      */
37660     maxLength : Number.MAX_VALUE,
37661     /**
37662      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37663      */
37664     minLengthText : "The minimum length for this field is {0}",
37665     /**
37666      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37667      */
37668     maxLengthText : "The maximum length for this field is {0}",
37669     /**
37670      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37671      */
37672     selectOnFocus : false,
37673     /**
37674      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37675      */
37676     blankText : "This field is required",
37677     /**
37678      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37679      * If available, this function will be called only after the basic validators all return true, and will be passed the
37680      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37681      */
37682     validator : null,
37683     /**
37684      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37685      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37686      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37687      */
37688     regex : null,
37689     /**
37690      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37691      */
37692     regexText : "",
37693     /**
37694      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37695      */
37696     emptyText : null,
37697    
37698
37699     // private
37700     initEvents : function()
37701     {
37702         if (this.emptyText) {
37703             this.el.attr('placeholder', this.emptyText);
37704         }
37705         
37706         Roo.form.TextField.superclass.initEvents.call(this);
37707         if(this.validationEvent == 'keyup'){
37708             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37709             this.el.on('keyup', this.filterValidation, this);
37710         }
37711         else if(this.validationEvent !== false){
37712             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37713         }
37714         
37715         if(this.selectOnFocus){
37716             this.on("focus", this.preFocus, this);
37717             
37718         }
37719         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37720             this.el.on("keypress", this.filterKeys, this);
37721         }
37722         if(this.grow){
37723             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37724             this.el.on("click", this.autoSize,  this);
37725         }
37726         if(this.el.is('input[type=password]') && Roo.isSafari){
37727             this.el.on('keydown', this.SafariOnKeyDown, this);
37728         }
37729     },
37730
37731     processValue : function(value){
37732         if(this.stripCharsRe){
37733             var newValue = value.replace(this.stripCharsRe, '');
37734             if(newValue !== value){
37735                 this.setRawValue(newValue);
37736                 return newValue;
37737             }
37738         }
37739         return value;
37740     },
37741
37742     filterValidation : function(e){
37743         if(!e.isNavKeyPress()){
37744             this.validationTask.delay(this.validationDelay);
37745         }
37746     },
37747
37748     // private
37749     onKeyUp : function(e){
37750         if(!e.isNavKeyPress()){
37751             this.autoSize();
37752         }
37753     },
37754
37755     /**
37756      * Resets the current field value to the originally-loaded value and clears any validation messages.
37757      *  
37758      */
37759     reset : function(){
37760         Roo.form.TextField.superclass.reset.call(this);
37761        
37762     },
37763
37764     
37765     // private
37766     preFocus : function(){
37767         
37768         if(this.selectOnFocus){
37769             this.el.dom.select();
37770         }
37771     },
37772
37773     
37774     // private
37775     filterKeys : function(e){
37776         var k = e.getKey();
37777         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37778             return;
37779         }
37780         var c = e.getCharCode(), cc = String.fromCharCode(c);
37781         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37782             return;
37783         }
37784         if(!this.maskRe.test(cc)){
37785             e.stopEvent();
37786         }
37787     },
37788
37789     setValue : function(v){
37790         
37791         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37792         
37793         this.autoSize();
37794     },
37795
37796     /**
37797      * Validates a value according to the field's validation rules and marks the field as invalid
37798      * if the validation fails
37799      * @param {Mixed} value The value to validate
37800      * @return {Boolean} True if the value is valid, else false
37801      */
37802     validateValue : function(value){
37803         if(value.length < 1)  { // if it's blank
37804              if(this.allowBlank){
37805                 this.clearInvalid();
37806                 return true;
37807              }else{
37808                 this.markInvalid(this.blankText);
37809                 return false;
37810              }
37811         }
37812         if(value.length < this.minLength){
37813             this.markInvalid(String.format(this.minLengthText, this.minLength));
37814             return false;
37815         }
37816         if(value.length > this.maxLength){
37817             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37818             return false;
37819         }
37820         if(this.vtype){
37821             var vt = Roo.form.VTypes;
37822             if(!vt[this.vtype](value, this)){
37823                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37824                 return false;
37825             }
37826         }
37827         if(typeof this.validator == "function"){
37828             var msg = this.validator(value);
37829             if(msg !== true){
37830                 this.markInvalid(msg);
37831                 return false;
37832             }
37833         }
37834         if(this.regex && !this.regex.test(value)){
37835             this.markInvalid(this.regexText);
37836             return false;
37837         }
37838         return true;
37839     },
37840
37841     /**
37842      * Selects text in this field
37843      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37844      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37845      */
37846     selectText : function(start, end){
37847         var v = this.getRawValue();
37848         if(v.length > 0){
37849             start = start === undefined ? 0 : start;
37850             end = end === undefined ? v.length : end;
37851             var d = this.el.dom;
37852             if(d.setSelectionRange){
37853                 d.setSelectionRange(start, end);
37854             }else if(d.createTextRange){
37855                 var range = d.createTextRange();
37856                 range.moveStart("character", start);
37857                 range.moveEnd("character", v.length-end);
37858                 range.select();
37859             }
37860         }
37861     },
37862
37863     /**
37864      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37865      * This only takes effect if grow = true, and fires the autosize event.
37866      */
37867     autoSize : function(){
37868         if(!this.grow || !this.rendered){
37869             return;
37870         }
37871         if(!this.metrics){
37872             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37873         }
37874         var el = this.el;
37875         var v = el.dom.value;
37876         var d = document.createElement('div');
37877         d.appendChild(document.createTextNode(v));
37878         v = d.innerHTML;
37879         d = null;
37880         v += "&#160;";
37881         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37882         this.el.setWidth(w);
37883         this.fireEvent("autosize", this, w);
37884     },
37885     
37886     // private
37887     SafariOnKeyDown : function(event)
37888     {
37889         // this is a workaround for a password hang bug on chrome/ webkit.
37890         
37891         var isSelectAll = false;
37892         
37893         if(this.el.dom.selectionEnd > 0){
37894             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37895         }
37896         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37897             event.preventDefault();
37898             this.setValue('');
37899             return;
37900         }
37901         
37902         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
37903             
37904             event.preventDefault();
37905             // this is very hacky as keydown always get's upper case.
37906             
37907             var cc = String.fromCharCode(event.getCharCode());
37908             
37909             
37910             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37911             
37912         }
37913         
37914         
37915     }
37916 });/*
37917  * Based on:
37918  * Ext JS Library 1.1.1
37919  * Copyright(c) 2006-2007, Ext JS, LLC.
37920  *
37921  * Originally Released Under LGPL - original licence link has changed is not relivant.
37922  *
37923  * Fork - LGPL
37924  * <script type="text/javascript">
37925  */
37926  
37927 /**
37928  * @class Roo.form.Hidden
37929  * @extends Roo.form.TextField
37930  * Simple Hidden element used on forms 
37931  * 
37932  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37933  * 
37934  * @constructor
37935  * Creates a new Hidden form element.
37936  * @param {Object} config Configuration options
37937  */
37938
37939
37940
37941 // easy hidden field...
37942 Roo.form.Hidden = function(config){
37943     Roo.form.Hidden.superclass.constructor.call(this, config);
37944 };
37945   
37946 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37947     fieldLabel:      '',
37948     inputType:      'hidden',
37949     width:          50,
37950     allowBlank:     true,
37951     labelSeparator: '',
37952     hidden:         true,
37953     itemCls :       'x-form-item-display-none'
37954
37955
37956 });
37957
37958
37959 /*
37960  * Based on:
37961  * Ext JS Library 1.1.1
37962  * Copyright(c) 2006-2007, Ext JS, LLC.
37963  *
37964  * Originally Released Under LGPL - original licence link has changed is not relivant.
37965  *
37966  * Fork - LGPL
37967  * <script type="text/javascript">
37968  */
37969  
37970 /**
37971  * @class Roo.form.TriggerField
37972  * @extends Roo.form.TextField
37973  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37974  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37975  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37976  * for which you can provide a custom implementation.  For example:
37977  * <pre><code>
37978 var trigger = new Roo.form.TriggerField();
37979 trigger.onTriggerClick = myTriggerFn;
37980 trigger.applyTo('my-field');
37981 </code></pre>
37982  *
37983  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37984  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37985  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37986  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37987  * @constructor
37988  * Create a new TriggerField.
37989  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37990  * to the base TextField)
37991  */
37992 Roo.form.TriggerField = function(config){
37993     this.mimicing = false;
37994     Roo.form.TriggerField.superclass.constructor.call(this, config);
37995 };
37996
37997 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37998     /**
37999      * @cfg {String} triggerClass A CSS class to apply to the trigger
38000      */
38001     /**
38002      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38003      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
38004      */
38005     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
38006     /**
38007      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
38008      */
38009     hideTrigger:false,
38010
38011     /** @cfg {Boolean} grow @hide */
38012     /** @cfg {Number} growMin @hide */
38013     /** @cfg {Number} growMax @hide */
38014
38015     /**
38016      * @hide 
38017      * @method
38018      */
38019     autoSize: Roo.emptyFn,
38020     // private
38021     monitorTab : true,
38022     // private
38023     deferHeight : true,
38024
38025     
38026     actionMode : 'wrap',
38027     // private
38028     onResize : function(w, h){
38029         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
38030         if(typeof w == 'number'){
38031             var x = w - this.trigger.getWidth();
38032             this.el.setWidth(this.adjustWidth('input', x));
38033             this.trigger.setStyle('left', x+'px');
38034         }
38035     },
38036
38037     // private
38038     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38039
38040     // private
38041     getResizeEl : function(){
38042         return this.wrap;
38043     },
38044
38045     // private
38046     getPositionEl : function(){
38047         return this.wrap;
38048     },
38049
38050     // private
38051     alignErrorIcon : function(){
38052         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
38053     },
38054
38055     // private
38056     onRender : function(ct, position){
38057         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
38058         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
38059         this.trigger = this.wrap.createChild(this.triggerConfig ||
38060                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
38061         if(this.hideTrigger){
38062             this.trigger.setDisplayed(false);
38063         }
38064         this.initTrigger();
38065         if(!this.width){
38066             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
38067         }
38068     },
38069
38070     // private
38071     initTrigger : function(){
38072         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38073         this.trigger.addClassOnOver('x-form-trigger-over');
38074         this.trigger.addClassOnClick('x-form-trigger-click');
38075     },
38076
38077     // private
38078     onDestroy : function(){
38079         if(this.trigger){
38080             this.trigger.removeAllListeners();
38081             this.trigger.remove();
38082         }
38083         if(this.wrap){
38084             this.wrap.remove();
38085         }
38086         Roo.form.TriggerField.superclass.onDestroy.call(this);
38087     },
38088
38089     // private
38090     onFocus : function(){
38091         Roo.form.TriggerField.superclass.onFocus.call(this);
38092         if(!this.mimicing){
38093             this.wrap.addClass('x-trigger-wrap-focus');
38094             this.mimicing = true;
38095             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38096             if(this.monitorTab){
38097                 this.el.on("keydown", this.checkTab, this);
38098             }
38099         }
38100     },
38101
38102     // private
38103     checkTab : function(e){
38104         if(e.getKey() == e.TAB){
38105             this.triggerBlur();
38106         }
38107     },
38108
38109     // private
38110     onBlur : function(){
38111         // do nothing
38112     },
38113
38114     // private
38115     mimicBlur : function(e, t){
38116         if(!this.wrap.contains(t) && this.validateBlur()){
38117             this.triggerBlur();
38118         }
38119     },
38120
38121     // private
38122     triggerBlur : function(){
38123         this.mimicing = false;
38124         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38125         if(this.monitorTab){
38126             this.el.un("keydown", this.checkTab, this);
38127         }
38128         this.wrap.removeClass('x-trigger-wrap-focus');
38129         Roo.form.TriggerField.superclass.onBlur.call(this);
38130     },
38131
38132     // private
38133     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38134     validateBlur : function(e, t){
38135         return true;
38136     },
38137
38138     // private
38139     onDisable : function(){
38140         Roo.form.TriggerField.superclass.onDisable.call(this);
38141         if(this.wrap){
38142             this.wrap.addClass('x-item-disabled');
38143         }
38144     },
38145
38146     // private
38147     onEnable : function(){
38148         Roo.form.TriggerField.superclass.onEnable.call(this);
38149         if(this.wrap){
38150             this.wrap.removeClass('x-item-disabled');
38151         }
38152     },
38153
38154     // private
38155     onShow : function(){
38156         var ae = this.getActionEl();
38157         
38158         if(ae){
38159             ae.dom.style.display = '';
38160             ae.dom.style.visibility = 'visible';
38161         }
38162     },
38163
38164     // private
38165     
38166     onHide : function(){
38167         var ae = this.getActionEl();
38168         ae.dom.style.display = 'none';
38169     },
38170
38171     /**
38172      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38173      * by an implementing function.
38174      * @method
38175      * @param {EventObject} e
38176      */
38177     onTriggerClick : Roo.emptyFn
38178 });
38179
38180 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38181 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38182 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38183 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38184     initComponent : function(){
38185         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38186
38187         this.triggerConfig = {
38188             tag:'span', cls:'x-form-twin-triggers', cn:[
38189             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38190             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38191         ]};
38192     },
38193
38194     getTrigger : function(index){
38195         return this.triggers[index];
38196     },
38197
38198     initTrigger : function(){
38199         var ts = this.trigger.select('.x-form-trigger', true);
38200         this.wrap.setStyle('overflow', 'hidden');
38201         var triggerField = this;
38202         ts.each(function(t, all, index){
38203             t.hide = function(){
38204                 var w = triggerField.wrap.getWidth();
38205                 this.dom.style.display = 'none';
38206                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38207             };
38208             t.show = function(){
38209                 var w = triggerField.wrap.getWidth();
38210                 this.dom.style.display = '';
38211                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38212             };
38213             var triggerIndex = 'Trigger'+(index+1);
38214
38215             if(this['hide'+triggerIndex]){
38216                 t.dom.style.display = 'none';
38217             }
38218             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38219             t.addClassOnOver('x-form-trigger-over');
38220             t.addClassOnClick('x-form-trigger-click');
38221         }, this);
38222         this.triggers = ts.elements;
38223     },
38224
38225     onTrigger1Click : Roo.emptyFn,
38226     onTrigger2Click : Roo.emptyFn
38227 });/*
38228  * Based on:
38229  * Ext JS Library 1.1.1
38230  * Copyright(c) 2006-2007, Ext JS, LLC.
38231  *
38232  * Originally Released Under LGPL - original licence link has changed is not relivant.
38233  *
38234  * Fork - LGPL
38235  * <script type="text/javascript">
38236  */
38237  
38238 /**
38239  * @class Roo.form.TextArea
38240  * @extends Roo.form.TextField
38241  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38242  * support for auto-sizing.
38243  * @constructor
38244  * Creates a new TextArea
38245  * @param {Object} config Configuration options
38246  */
38247 Roo.form.TextArea = function(config){
38248     Roo.form.TextArea.superclass.constructor.call(this, config);
38249     // these are provided exchanges for backwards compat
38250     // minHeight/maxHeight were replaced by growMin/growMax to be
38251     // compatible with TextField growing config values
38252     if(this.minHeight !== undefined){
38253         this.growMin = this.minHeight;
38254     }
38255     if(this.maxHeight !== undefined){
38256         this.growMax = this.maxHeight;
38257     }
38258 };
38259
38260 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38261     /**
38262      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38263      */
38264     growMin : 60,
38265     /**
38266      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38267      */
38268     growMax: 1000,
38269     /**
38270      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38271      * in the field (equivalent to setting overflow: hidden, defaults to false)
38272      */
38273     preventScrollbars: false,
38274     /**
38275      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38276      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38277      */
38278
38279     // private
38280     onRender : function(ct, position){
38281         if(!this.el){
38282             this.defaultAutoCreate = {
38283                 tag: "textarea",
38284                 style:"width:300px;height:60px;",
38285                 autocomplete: "new-password"
38286             };
38287         }
38288         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38289         if(this.grow){
38290             this.textSizeEl = Roo.DomHelper.append(document.body, {
38291                 tag: "pre", cls: "x-form-grow-sizer"
38292             });
38293             if(this.preventScrollbars){
38294                 this.el.setStyle("overflow", "hidden");
38295             }
38296             this.el.setHeight(this.growMin);
38297         }
38298     },
38299
38300     onDestroy : function(){
38301         if(this.textSizeEl){
38302             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38303         }
38304         Roo.form.TextArea.superclass.onDestroy.call(this);
38305     },
38306
38307     // private
38308     onKeyUp : function(e){
38309         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38310             this.autoSize();
38311         }
38312     },
38313
38314     /**
38315      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38316      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38317      */
38318     autoSize : function(){
38319         if(!this.grow || !this.textSizeEl){
38320             return;
38321         }
38322         var el = this.el;
38323         var v = el.dom.value;
38324         var ts = this.textSizeEl;
38325
38326         ts.innerHTML = '';
38327         ts.appendChild(document.createTextNode(v));
38328         v = ts.innerHTML;
38329
38330         Roo.fly(ts).setWidth(this.el.getWidth());
38331         if(v.length < 1){
38332             v = "&#160;&#160;";
38333         }else{
38334             if(Roo.isIE){
38335                 v = v.replace(/\n/g, '<p>&#160;</p>');
38336             }
38337             v += "&#160;\n&#160;";
38338         }
38339         ts.innerHTML = v;
38340         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38341         if(h != this.lastHeight){
38342             this.lastHeight = h;
38343             this.el.setHeight(h);
38344             this.fireEvent("autosize", this, h);
38345         }
38346     }
38347 });/*
38348  * Based on:
38349  * Ext JS Library 1.1.1
38350  * Copyright(c) 2006-2007, Ext JS, LLC.
38351  *
38352  * Originally Released Under LGPL - original licence link has changed is not relivant.
38353  *
38354  * Fork - LGPL
38355  * <script type="text/javascript">
38356  */
38357  
38358
38359 /**
38360  * @class Roo.form.NumberField
38361  * @extends Roo.form.TextField
38362  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38363  * @constructor
38364  * Creates a new NumberField
38365  * @param {Object} config Configuration options
38366  */
38367 Roo.form.NumberField = function(config){
38368     Roo.form.NumberField.superclass.constructor.call(this, config);
38369 };
38370
38371 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38372     /**
38373      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38374      */
38375     fieldClass: "x-form-field x-form-num-field",
38376     /**
38377      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38378      */
38379     allowDecimals : true,
38380     /**
38381      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38382      */
38383     decimalSeparator : ".",
38384     /**
38385      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38386      */
38387     decimalPrecision : 2,
38388     /**
38389      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38390      */
38391     allowNegative : true,
38392     /**
38393      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38394      */
38395     minValue : Number.NEGATIVE_INFINITY,
38396     /**
38397      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38398      */
38399     maxValue : Number.MAX_VALUE,
38400     /**
38401      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38402      */
38403     minText : "The minimum value for this field is {0}",
38404     /**
38405      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38406      */
38407     maxText : "The maximum value for this field is {0}",
38408     /**
38409      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38410      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38411      */
38412     nanText : "{0} is not a valid number",
38413
38414     // private
38415     initEvents : function(){
38416         Roo.form.NumberField.superclass.initEvents.call(this);
38417         var allowed = "0123456789";
38418         if(this.allowDecimals){
38419             allowed += this.decimalSeparator;
38420         }
38421         if(this.allowNegative){
38422             allowed += "-";
38423         }
38424         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38425         var keyPress = function(e){
38426             var k = e.getKey();
38427             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38428                 return;
38429             }
38430             var c = e.getCharCode();
38431             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38432                 e.stopEvent();
38433             }
38434         };
38435         this.el.on("keypress", keyPress, this);
38436     },
38437
38438     // private
38439     validateValue : function(value){
38440         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38441             return false;
38442         }
38443         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38444              return true;
38445         }
38446         var num = this.parseValue(value);
38447         if(isNaN(num)){
38448             this.markInvalid(String.format(this.nanText, value));
38449             return false;
38450         }
38451         if(num < this.minValue){
38452             this.markInvalid(String.format(this.minText, this.minValue));
38453             return false;
38454         }
38455         if(num > this.maxValue){
38456             this.markInvalid(String.format(this.maxText, this.maxValue));
38457             return false;
38458         }
38459         return true;
38460     },
38461
38462     getValue : function(){
38463         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38464     },
38465
38466     // private
38467     parseValue : function(value){
38468         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38469         return isNaN(value) ? '' : value;
38470     },
38471
38472     // private
38473     fixPrecision : function(value){
38474         var nan = isNaN(value);
38475         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38476             return nan ? '' : value;
38477         }
38478         return parseFloat(value).toFixed(this.decimalPrecision);
38479     },
38480
38481     setValue : function(v){
38482         v = this.fixPrecision(v);
38483         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38484     },
38485
38486     // private
38487     decimalPrecisionFcn : function(v){
38488         return Math.floor(v);
38489     },
38490
38491     beforeBlur : function(){
38492         var v = this.parseValue(this.getRawValue());
38493         if(v){
38494             this.setValue(v);
38495         }
38496     }
38497 });/*
38498  * Based on:
38499  * Ext JS Library 1.1.1
38500  * Copyright(c) 2006-2007, Ext JS, LLC.
38501  *
38502  * Originally Released Under LGPL - original licence link has changed is not relivant.
38503  *
38504  * Fork - LGPL
38505  * <script type="text/javascript">
38506  */
38507  
38508 /**
38509  * @class Roo.form.DateField
38510  * @extends Roo.form.TriggerField
38511  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38512 * @constructor
38513 * Create a new DateField
38514 * @param {Object} config
38515  */
38516 Roo.form.DateField = function(config){
38517     Roo.form.DateField.superclass.constructor.call(this, config);
38518     
38519       this.addEvents({
38520          
38521         /**
38522          * @event select
38523          * Fires when a date is selected
38524              * @param {Roo.form.DateField} combo This combo box
38525              * @param {Date} date The date selected
38526              */
38527         'select' : true
38528          
38529     });
38530     
38531     
38532     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38533     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38534     this.ddMatch = null;
38535     if(this.disabledDates){
38536         var dd = this.disabledDates;
38537         var re = "(?:";
38538         for(var i = 0; i < dd.length; i++){
38539             re += dd[i];
38540             if(i != dd.length-1) re += "|";
38541         }
38542         this.ddMatch = new RegExp(re + ")");
38543     }
38544 };
38545
38546 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38547     /**
38548      * @cfg {String} format
38549      * The default date format string which can be overriden for localization support.  The format must be
38550      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38551      */
38552     format : "m/d/y",
38553     /**
38554      * @cfg {String} altFormats
38555      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38556      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38557      */
38558     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38559     /**
38560      * @cfg {Array} disabledDays
38561      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38562      */
38563     disabledDays : null,
38564     /**
38565      * @cfg {String} disabledDaysText
38566      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38567      */
38568     disabledDaysText : "Disabled",
38569     /**
38570      * @cfg {Array} disabledDates
38571      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38572      * expression so they are very powerful. Some examples:
38573      * <ul>
38574      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38575      * <li>["03/08", "09/16"] would disable those days for every year</li>
38576      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38577      * <li>["03/../2006"] would disable every day in March 2006</li>
38578      * <li>["^03"] would disable every day in every March</li>
38579      * </ul>
38580      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38581      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38582      */
38583     disabledDates : null,
38584     /**
38585      * @cfg {String} disabledDatesText
38586      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38587      */
38588     disabledDatesText : "Disabled",
38589     /**
38590      * @cfg {Date/String} minValue
38591      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38592      * valid format (defaults to null).
38593      */
38594     minValue : null,
38595     /**
38596      * @cfg {Date/String} maxValue
38597      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38598      * valid format (defaults to null).
38599      */
38600     maxValue : null,
38601     /**
38602      * @cfg {String} minText
38603      * The error text to display when the date in the cell is before minValue (defaults to
38604      * 'The date in this field must be after {minValue}').
38605      */
38606     minText : "The date in this field must be equal to or after {0}",
38607     /**
38608      * @cfg {String} maxText
38609      * The error text to display when the date in the cell is after maxValue (defaults to
38610      * 'The date in this field must be before {maxValue}').
38611      */
38612     maxText : "The date in this field must be equal to or before {0}",
38613     /**
38614      * @cfg {String} invalidText
38615      * The error text to display when the date in the field is invalid (defaults to
38616      * '{value} is not a valid date - it must be in the format {format}').
38617      */
38618     invalidText : "{0} is not a valid date - it must be in the format {1}",
38619     /**
38620      * @cfg {String} triggerClass
38621      * An additional CSS class used to style the trigger button.  The trigger will always get the
38622      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38623      * which displays a calendar icon).
38624      */
38625     triggerClass : 'x-form-date-trigger',
38626     
38627
38628     /**
38629      * @cfg {Boolean} useIso
38630      * if enabled, then the date field will use a hidden field to store the 
38631      * real value as iso formated date. default (false)
38632      */ 
38633     useIso : false,
38634     /**
38635      * @cfg {String/Object} autoCreate
38636      * A DomHelper element spec, or true for a default element spec (defaults to
38637      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38638      */ 
38639     // private
38640     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38641     
38642     // private
38643     hiddenField: false,
38644     
38645     onRender : function(ct, position)
38646     {
38647         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38648         if (this.useIso) {
38649             //this.el.dom.removeAttribute('name'); 
38650             Roo.log("Changing name?");
38651             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38652             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38653                     'before', true);
38654             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38655             // prevent input submission
38656             this.hiddenName = this.name;
38657         }
38658             
38659             
38660     },
38661     
38662     // private
38663     validateValue : function(value)
38664     {
38665         value = this.formatDate(value);
38666         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38667             Roo.log('super failed');
38668             return false;
38669         }
38670         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38671              return true;
38672         }
38673         var svalue = value;
38674         value = this.parseDate(value);
38675         if(!value){
38676             Roo.log('parse date failed' + svalue);
38677             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38678             return false;
38679         }
38680         var time = value.getTime();
38681         if(this.minValue && time < this.minValue.getTime()){
38682             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38683             return false;
38684         }
38685         if(this.maxValue && time > this.maxValue.getTime()){
38686             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38687             return false;
38688         }
38689         if(this.disabledDays){
38690             var day = value.getDay();
38691             for(var i = 0; i < this.disabledDays.length; i++) {
38692                 if(day === this.disabledDays[i]){
38693                     this.markInvalid(this.disabledDaysText);
38694                     return false;
38695                 }
38696             }
38697         }
38698         var fvalue = this.formatDate(value);
38699         if(this.ddMatch && this.ddMatch.test(fvalue)){
38700             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38701             return false;
38702         }
38703         return true;
38704     },
38705
38706     // private
38707     // Provides logic to override the default TriggerField.validateBlur which just returns true
38708     validateBlur : function(){
38709         return !this.menu || !this.menu.isVisible();
38710     },
38711     
38712     getName: function()
38713     {
38714         // returns hidden if it's set..
38715         if (!this.rendered) {return ''};
38716         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38717         
38718     },
38719
38720     /**
38721      * Returns the current date value of the date field.
38722      * @return {Date} The date value
38723      */
38724     getValue : function(){
38725         
38726         return  this.hiddenField ?
38727                 this.hiddenField.value :
38728                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38729     },
38730
38731     /**
38732      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38733      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38734      * (the default format used is "m/d/y").
38735      * <br />Usage:
38736      * <pre><code>
38737 //All of these calls set the same date value (May 4, 2006)
38738
38739 //Pass a date object:
38740 var dt = new Date('5/4/06');
38741 dateField.setValue(dt);
38742
38743 //Pass a date string (default format):
38744 dateField.setValue('5/4/06');
38745
38746 //Pass a date string (custom format):
38747 dateField.format = 'Y-m-d';
38748 dateField.setValue('2006-5-4');
38749 </code></pre>
38750      * @param {String/Date} date The date or valid date string
38751      */
38752     setValue : function(date){
38753         if (this.hiddenField) {
38754             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38755         }
38756         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38757         // make sure the value field is always stored as a date..
38758         this.value = this.parseDate(date);
38759         
38760         
38761     },
38762
38763     // private
38764     parseDate : function(value){
38765         if(!value || value instanceof Date){
38766             return value;
38767         }
38768         var v = Date.parseDate(value, this.format);
38769          if (!v && this.useIso) {
38770             v = Date.parseDate(value, 'Y-m-d');
38771         }
38772         if(!v && this.altFormats){
38773             if(!this.altFormatsArray){
38774                 this.altFormatsArray = this.altFormats.split("|");
38775             }
38776             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38777                 v = Date.parseDate(value, this.altFormatsArray[i]);
38778             }
38779         }
38780         return v;
38781     },
38782
38783     // private
38784     formatDate : function(date, fmt){
38785         return (!date || !(date instanceof Date)) ?
38786                date : date.dateFormat(fmt || this.format);
38787     },
38788
38789     // private
38790     menuListeners : {
38791         select: function(m, d){
38792             
38793             this.setValue(d);
38794             this.fireEvent('select', this, d);
38795         },
38796         show : function(){ // retain focus styling
38797             this.onFocus();
38798         },
38799         hide : function(){
38800             this.focus.defer(10, this);
38801             var ml = this.menuListeners;
38802             this.menu.un("select", ml.select,  this);
38803             this.menu.un("show", ml.show,  this);
38804             this.menu.un("hide", ml.hide,  this);
38805         }
38806     },
38807
38808     // private
38809     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38810     onTriggerClick : function(){
38811         if(this.disabled){
38812             return;
38813         }
38814         if(this.menu == null){
38815             this.menu = new Roo.menu.DateMenu();
38816         }
38817         Roo.apply(this.menu.picker,  {
38818             showClear: this.allowBlank,
38819             minDate : this.minValue,
38820             maxDate : this.maxValue,
38821             disabledDatesRE : this.ddMatch,
38822             disabledDatesText : this.disabledDatesText,
38823             disabledDays : this.disabledDays,
38824             disabledDaysText : this.disabledDaysText,
38825             format : this.useIso ? 'Y-m-d' : this.format,
38826             minText : String.format(this.minText, this.formatDate(this.minValue)),
38827             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38828         });
38829         this.menu.on(Roo.apply({}, this.menuListeners, {
38830             scope:this
38831         }));
38832         this.menu.picker.setValue(this.getValue() || new Date());
38833         this.menu.show(this.el, "tl-bl?");
38834     },
38835
38836     beforeBlur : function(){
38837         var v = this.parseDate(this.getRawValue());
38838         if(v){
38839             this.setValue(v);
38840         }
38841     },
38842
38843     /*@
38844      * overide
38845      * 
38846      */
38847     isDirty : function() {
38848         if(this.disabled) {
38849             return false;
38850         }
38851         
38852         if(typeof(this.startValue) === 'undefined'){
38853             return false;
38854         }
38855         
38856         return String(this.getValue()) !== String(this.startValue);
38857         
38858     }
38859 });/*
38860  * Based on:
38861  * Ext JS Library 1.1.1
38862  * Copyright(c) 2006-2007, Ext JS, LLC.
38863  *
38864  * Originally Released Under LGPL - original licence link has changed is not relivant.
38865  *
38866  * Fork - LGPL
38867  * <script type="text/javascript">
38868  */
38869  
38870 /**
38871  * @class Roo.form.MonthField
38872  * @extends Roo.form.TriggerField
38873  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38874 * @constructor
38875 * Create a new MonthField
38876 * @param {Object} config
38877  */
38878 Roo.form.MonthField = function(config){
38879     
38880     Roo.form.MonthField.superclass.constructor.call(this, config);
38881     
38882       this.addEvents({
38883          
38884         /**
38885          * @event select
38886          * Fires when a date is selected
38887              * @param {Roo.form.MonthFieeld} combo This combo box
38888              * @param {Date} date The date selected
38889              */
38890         'select' : true
38891          
38892     });
38893     
38894     
38895     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38896     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38897     this.ddMatch = null;
38898     if(this.disabledDates){
38899         var dd = this.disabledDates;
38900         var re = "(?:";
38901         for(var i = 0; i < dd.length; i++){
38902             re += dd[i];
38903             if(i != dd.length-1) re += "|";
38904         }
38905         this.ddMatch = new RegExp(re + ")");
38906     }
38907 };
38908
38909 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38910     /**
38911      * @cfg {String} format
38912      * The default date format string which can be overriden for localization support.  The format must be
38913      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38914      */
38915     format : "M Y",
38916     /**
38917      * @cfg {String} altFormats
38918      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38919      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38920      */
38921     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38922     /**
38923      * @cfg {Array} disabledDays
38924      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38925      */
38926     disabledDays : [0,1,2,3,4,5,6],
38927     /**
38928      * @cfg {String} disabledDaysText
38929      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38930      */
38931     disabledDaysText : "Disabled",
38932     /**
38933      * @cfg {Array} disabledDates
38934      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38935      * expression so they are very powerful. Some examples:
38936      * <ul>
38937      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38938      * <li>["03/08", "09/16"] would disable those days for every year</li>
38939      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38940      * <li>["03/../2006"] would disable every day in March 2006</li>
38941      * <li>["^03"] would disable every day in every March</li>
38942      * </ul>
38943      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38944      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38945      */
38946     disabledDates : null,
38947     /**
38948      * @cfg {String} disabledDatesText
38949      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38950      */
38951     disabledDatesText : "Disabled",
38952     /**
38953      * @cfg {Date/String} minValue
38954      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38955      * valid format (defaults to null).
38956      */
38957     minValue : null,
38958     /**
38959      * @cfg {Date/String} maxValue
38960      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38961      * valid format (defaults to null).
38962      */
38963     maxValue : null,
38964     /**
38965      * @cfg {String} minText
38966      * The error text to display when the date in the cell is before minValue (defaults to
38967      * 'The date in this field must be after {minValue}').
38968      */
38969     minText : "The date in this field must be equal to or after {0}",
38970     /**
38971      * @cfg {String} maxTextf
38972      * The error text to display when the date in the cell is after maxValue (defaults to
38973      * 'The date in this field must be before {maxValue}').
38974      */
38975     maxText : "The date in this field must be equal to or before {0}",
38976     /**
38977      * @cfg {String} invalidText
38978      * The error text to display when the date in the field is invalid (defaults to
38979      * '{value} is not a valid date - it must be in the format {format}').
38980      */
38981     invalidText : "{0} is not a valid date - it must be in the format {1}",
38982     /**
38983      * @cfg {String} triggerClass
38984      * An additional CSS class used to style the trigger button.  The trigger will always get the
38985      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38986      * which displays a calendar icon).
38987      */
38988     triggerClass : 'x-form-date-trigger',
38989     
38990
38991     /**
38992      * @cfg {Boolean} useIso
38993      * if enabled, then the date field will use a hidden field to store the 
38994      * real value as iso formated date. default (true)
38995      */ 
38996     useIso : true,
38997     /**
38998      * @cfg {String/Object} autoCreate
38999      * A DomHelper element spec, or true for a default element spec (defaults to
39000      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
39001      */ 
39002     // private
39003     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
39004     
39005     // private
39006     hiddenField: false,
39007     
39008     hideMonthPicker : false,
39009     
39010     onRender : function(ct, position)
39011     {
39012         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
39013         if (this.useIso) {
39014             this.el.dom.removeAttribute('name'); 
39015             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
39016                     'before', true);
39017             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
39018             // prevent input submission
39019             this.hiddenName = this.name;
39020         }
39021             
39022             
39023     },
39024     
39025     // private
39026     validateValue : function(value)
39027     {
39028         value = this.formatDate(value);
39029         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
39030             return false;
39031         }
39032         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
39033              return true;
39034         }
39035         var svalue = value;
39036         value = this.parseDate(value);
39037         if(!value){
39038             this.markInvalid(String.format(this.invalidText, svalue, this.format));
39039             return false;
39040         }
39041         var time = value.getTime();
39042         if(this.minValue && time < this.minValue.getTime()){
39043             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
39044             return false;
39045         }
39046         if(this.maxValue && time > this.maxValue.getTime()){
39047             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
39048             return false;
39049         }
39050         /*if(this.disabledDays){
39051             var day = value.getDay();
39052             for(var i = 0; i < this.disabledDays.length; i++) {
39053                 if(day === this.disabledDays[i]){
39054                     this.markInvalid(this.disabledDaysText);
39055                     return false;
39056                 }
39057             }
39058         }
39059         */
39060         var fvalue = this.formatDate(value);
39061         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
39062             this.markInvalid(String.format(this.disabledDatesText, fvalue));
39063             return false;
39064         }
39065         */
39066         return true;
39067     },
39068
39069     // private
39070     // Provides logic to override the default TriggerField.validateBlur which just returns true
39071     validateBlur : function(){
39072         return !this.menu || !this.menu.isVisible();
39073     },
39074
39075     /**
39076      * Returns the current date value of the date field.
39077      * @return {Date} The date value
39078      */
39079     getValue : function(){
39080         
39081         
39082         
39083         return  this.hiddenField ?
39084                 this.hiddenField.value :
39085                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39086     },
39087
39088     /**
39089      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39090      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39091      * (the default format used is "m/d/y").
39092      * <br />Usage:
39093      * <pre><code>
39094 //All of these calls set the same date value (May 4, 2006)
39095
39096 //Pass a date object:
39097 var dt = new Date('5/4/06');
39098 monthField.setValue(dt);
39099
39100 //Pass a date string (default format):
39101 monthField.setValue('5/4/06');
39102
39103 //Pass a date string (custom format):
39104 monthField.format = 'Y-m-d';
39105 monthField.setValue('2006-5-4');
39106 </code></pre>
39107      * @param {String/Date} date The date or valid date string
39108      */
39109     setValue : function(date){
39110         Roo.log('month setValue' + date);
39111         // can only be first of month..
39112         
39113         var val = this.parseDate(date);
39114         
39115         if (this.hiddenField) {
39116             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39117         }
39118         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39119         this.value = this.parseDate(date);
39120     },
39121
39122     // private
39123     parseDate : function(value){
39124         if(!value || value instanceof Date){
39125             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39126             return value;
39127         }
39128         var v = Date.parseDate(value, this.format);
39129         if (!v && this.useIso) {
39130             v = Date.parseDate(value, 'Y-m-d');
39131         }
39132         if (v) {
39133             // 
39134             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39135         }
39136         
39137         
39138         if(!v && this.altFormats){
39139             if(!this.altFormatsArray){
39140                 this.altFormatsArray = this.altFormats.split("|");
39141             }
39142             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39143                 v = Date.parseDate(value, this.altFormatsArray[i]);
39144             }
39145         }
39146         return v;
39147     },
39148
39149     // private
39150     formatDate : function(date, fmt){
39151         return (!date || !(date instanceof Date)) ?
39152                date : date.dateFormat(fmt || this.format);
39153     },
39154
39155     // private
39156     menuListeners : {
39157         select: function(m, d){
39158             this.setValue(d);
39159             this.fireEvent('select', this, d);
39160         },
39161         show : function(){ // retain focus styling
39162             this.onFocus();
39163         },
39164         hide : function(){
39165             this.focus.defer(10, this);
39166             var ml = this.menuListeners;
39167             this.menu.un("select", ml.select,  this);
39168             this.menu.un("show", ml.show,  this);
39169             this.menu.un("hide", ml.hide,  this);
39170         }
39171     },
39172     // private
39173     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39174     onTriggerClick : function(){
39175         if(this.disabled){
39176             return;
39177         }
39178         if(this.menu == null){
39179             this.menu = new Roo.menu.DateMenu();
39180            
39181         }
39182         
39183         Roo.apply(this.menu.picker,  {
39184             
39185             showClear: this.allowBlank,
39186             minDate : this.minValue,
39187             maxDate : this.maxValue,
39188             disabledDatesRE : this.ddMatch,
39189             disabledDatesText : this.disabledDatesText,
39190             
39191             format : this.useIso ? 'Y-m-d' : this.format,
39192             minText : String.format(this.minText, this.formatDate(this.minValue)),
39193             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39194             
39195         });
39196          this.menu.on(Roo.apply({}, this.menuListeners, {
39197             scope:this
39198         }));
39199        
39200         
39201         var m = this.menu;
39202         var p = m.picker;
39203         
39204         // hide month picker get's called when we called by 'before hide';
39205         
39206         var ignorehide = true;
39207         p.hideMonthPicker  = function(disableAnim){
39208             if (ignorehide) {
39209                 return;
39210             }
39211              if(this.monthPicker){
39212                 Roo.log("hideMonthPicker called");
39213                 if(disableAnim === true){
39214                     this.monthPicker.hide();
39215                 }else{
39216                     this.monthPicker.slideOut('t', {duration:.2});
39217                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39218                     p.fireEvent("select", this, this.value);
39219                     m.hide();
39220                 }
39221             }
39222         }
39223         
39224         Roo.log('picker set value');
39225         Roo.log(this.getValue());
39226         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39227         m.show(this.el, 'tl-bl?');
39228         ignorehide  = false;
39229         // this will trigger hideMonthPicker..
39230         
39231         
39232         // hidden the day picker
39233         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39234         
39235         
39236         
39237       
39238         
39239         p.showMonthPicker.defer(100, p);
39240     
39241         
39242        
39243     },
39244
39245     beforeBlur : function(){
39246         var v = this.parseDate(this.getRawValue());
39247         if(v){
39248             this.setValue(v);
39249         }
39250     }
39251
39252     /** @cfg {Boolean} grow @hide */
39253     /** @cfg {Number} growMin @hide */
39254     /** @cfg {Number} growMax @hide */
39255     /**
39256      * @hide
39257      * @method autoSize
39258      */
39259 });/*
39260  * Based on:
39261  * Ext JS Library 1.1.1
39262  * Copyright(c) 2006-2007, Ext JS, LLC.
39263  *
39264  * Originally Released Under LGPL - original licence link has changed is not relivant.
39265  *
39266  * Fork - LGPL
39267  * <script type="text/javascript">
39268  */
39269  
39270
39271 /**
39272  * @class Roo.form.ComboBox
39273  * @extends Roo.form.TriggerField
39274  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39275  * @constructor
39276  * Create a new ComboBox.
39277  * @param {Object} config Configuration options
39278  */
39279 Roo.form.ComboBox = function(config){
39280     Roo.form.ComboBox.superclass.constructor.call(this, config);
39281     this.addEvents({
39282         /**
39283          * @event expand
39284          * Fires when the dropdown list is expanded
39285              * @param {Roo.form.ComboBox} combo This combo box
39286              */
39287         'expand' : true,
39288         /**
39289          * @event collapse
39290          * Fires when the dropdown list is collapsed
39291              * @param {Roo.form.ComboBox} combo This combo box
39292              */
39293         'collapse' : true,
39294         /**
39295          * @event beforeselect
39296          * Fires before a list item is selected. Return false to cancel the selection.
39297              * @param {Roo.form.ComboBox} combo This combo box
39298              * @param {Roo.data.Record} record The data record returned from the underlying store
39299              * @param {Number} index The index of the selected item in the dropdown list
39300              */
39301         'beforeselect' : true,
39302         /**
39303          * @event select
39304          * Fires when a list item is selected
39305              * @param {Roo.form.ComboBox} combo This combo box
39306              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39307              * @param {Number} index The index of the selected item in the dropdown list
39308              */
39309         'select' : true,
39310         /**
39311          * @event beforequery
39312          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39313          * The event object passed has these properties:
39314              * @param {Roo.form.ComboBox} combo This combo box
39315              * @param {String} query The query
39316              * @param {Boolean} forceAll true to force "all" query
39317              * @param {Boolean} cancel true to cancel the query
39318              * @param {Object} e The query event object
39319              */
39320         'beforequery': true,
39321          /**
39322          * @event add
39323          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39324              * @param {Roo.form.ComboBox} combo This combo box
39325              */
39326         'add' : true,
39327         /**
39328          * @event edit
39329          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39330              * @param {Roo.form.ComboBox} combo This combo box
39331              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39332              */
39333         'edit' : true
39334         
39335         
39336     });
39337     if(this.transform){
39338         this.allowDomMove = false;
39339         var s = Roo.getDom(this.transform);
39340         if(!this.hiddenName){
39341             this.hiddenName = s.name;
39342         }
39343         if(!this.store){
39344             this.mode = 'local';
39345             var d = [], opts = s.options;
39346             for(var i = 0, len = opts.length;i < len; i++){
39347                 var o = opts[i];
39348                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39349                 if(o.selected) {
39350                     this.value = value;
39351                 }
39352                 d.push([value, o.text]);
39353             }
39354             this.store = new Roo.data.SimpleStore({
39355                 'id': 0,
39356                 fields: ['value', 'text'],
39357                 data : d
39358             });
39359             this.valueField = 'value';
39360             this.displayField = 'text';
39361         }
39362         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39363         if(!this.lazyRender){
39364             this.target = true;
39365             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39366             s.parentNode.removeChild(s); // remove it
39367             this.render(this.el.parentNode);
39368         }else{
39369             s.parentNode.removeChild(s); // remove it
39370         }
39371
39372     }
39373     if (this.store) {
39374         this.store = Roo.factory(this.store, Roo.data);
39375     }
39376     
39377     this.selectedIndex = -1;
39378     if(this.mode == 'local'){
39379         if(config.queryDelay === undefined){
39380             this.queryDelay = 10;
39381         }
39382         if(config.minChars === undefined){
39383             this.minChars = 0;
39384         }
39385     }
39386 };
39387
39388 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39389     /**
39390      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39391      */
39392     /**
39393      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39394      * rendering into an Roo.Editor, defaults to false)
39395      */
39396     /**
39397      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39398      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39399      */
39400     /**
39401      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39402      */
39403     /**
39404      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39405      * the dropdown list (defaults to undefined, with no header element)
39406      */
39407
39408      /**
39409      * @cfg {String/Roo.Template} tpl The template to use to render the output
39410      */
39411      
39412     // private
39413     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39414     /**
39415      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39416      */
39417     listWidth: undefined,
39418     /**
39419      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39420      * mode = 'remote' or 'text' if mode = 'local')
39421      */
39422     displayField: undefined,
39423     /**
39424      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39425      * mode = 'remote' or 'value' if mode = 'local'). 
39426      * Note: use of a valueField requires the user make a selection
39427      * in order for a value to be mapped.
39428      */
39429     valueField: undefined,
39430     
39431     
39432     /**
39433      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39434      * field's data value (defaults to the underlying DOM element's name)
39435      */
39436     hiddenName: undefined,
39437     /**
39438      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39439      */
39440     listClass: '',
39441     /**
39442      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39443      */
39444     selectedClass: 'x-combo-selected',
39445     /**
39446      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39447      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39448      * which displays a downward arrow icon).
39449      */
39450     triggerClass : 'x-form-arrow-trigger',
39451     /**
39452      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39453      */
39454     shadow:'sides',
39455     /**
39456      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39457      * anchor positions (defaults to 'tl-bl')
39458      */
39459     listAlign: 'tl-bl?',
39460     /**
39461      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39462      */
39463     maxHeight: 300,
39464     /**
39465      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39466      * query specified by the allQuery config option (defaults to 'query')
39467      */
39468     triggerAction: 'query',
39469     /**
39470      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39471      * (defaults to 4, does not apply if editable = false)
39472      */
39473     minChars : 4,
39474     /**
39475      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39476      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39477      */
39478     typeAhead: false,
39479     /**
39480      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39481      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39482      */
39483     queryDelay: 500,
39484     /**
39485      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39486      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39487      */
39488     pageSize: 0,
39489     /**
39490      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39491      * when editable = true (defaults to false)
39492      */
39493     selectOnFocus:false,
39494     /**
39495      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39496      */
39497     queryParam: 'query',
39498     /**
39499      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39500      * when mode = 'remote' (defaults to 'Loading...')
39501      */
39502     loadingText: 'Loading...',
39503     /**
39504      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39505      */
39506     resizable: false,
39507     /**
39508      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39509      */
39510     handleHeight : 8,
39511     /**
39512      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39513      * traditional select (defaults to true)
39514      */
39515     editable: true,
39516     /**
39517      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39518      */
39519     allQuery: '',
39520     /**
39521      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39522      */
39523     mode: 'remote',
39524     /**
39525      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39526      * listWidth has a higher value)
39527      */
39528     minListWidth : 70,
39529     /**
39530      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39531      * allow the user to set arbitrary text into the field (defaults to false)
39532      */
39533     forceSelection:false,
39534     /**
39535      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39536      * if typeAhead = true (defaults to 250)
39537      */
39538     typeAheadDelay : 250,
39539     /**
39540      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39541      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39542      */
39543     valueNotFoundText : undefined,
39544     /**
39545      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39546      */
39547     blockFocus : false,
39548     
39549     /**
39550      * @cfg {Boolean} disableClear Disable showing of clear button.
39551      */
39552     disableClear : false,
39553     /**
39554      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39555      */
39556     alwaysQuery : false,
39557     
39558     //private
39559     addicon : false,
39560     editicon: false,
39561     
39562     // element that contains real text value.. (when hidden is used..)
39563      
39564     // private
39565     onRender : function(ct, position){
39566         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39567         if(this.hiddenName){
39568             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39569                     'before', true);
39570             this.hiddenField.value =
39571                 this.hiddenValue !== undefined ? this.hiddenValue :
39572                 this.value !== undefined ? this.value : '';
39573
39574             // prevent input submission
39575             this.el.dom.removeAttribute('name');
39576              
39577              
39578         }
39579         if(Roo.isGecko){
39580             this.el.dom.setAttribute('autocomplete', 'off');
39581         }
39582
39583         var cls = 'x-combo-list';
39584
39585         this.list = new Roo.Layer({
39586             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39587         });
39588
39589         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39590         this.list.setWidth(lw);
39591         this.list.swallowEvent('mousewheel');
39592         this.assetHeight = 0;
39593
39594         if(this.title){
39595             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39596             this.assetHeight += this.header.getHeight();
39597         }
39598
39599         this.innerList = this.list.createChild({cls:cls+'-inner'});
39600         this.innerList.on('mouseover', this.onViewOver, this);
39601         this.innerList.on('mousemove', this.onViewMove, this);
39602         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39603         
39604         if(this.allowBlank && !this.pageSize && !this.disableClear){
39605             this.footer = this.list.createChild({cls:cls+'-ft'});
39606             this.pageTb = new Roo.Toolbar(this.footer);
39607            
39608         }
39609         if(this.pageSize){
39610             this.footer = this.list.createChild({cls:cls+'-ft'});
39611             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39612                     {pageSize: this.pageSize});
39613             
39614         }
39615         
39616         if (this.pageTb && this.allowBlank && !this.disableClear) {
39617             var _this = this;
39618             this.pageTb.add(new Roo.Toolbar.Fill(), {
39619                 cls: 'x-btn-icon x-btn-clear',
39620                 text: '&#160;',
39621                 handler: function()
39622                 {
39623                     _this.collapse();
39624                     _this.clearValue();
39625                     _this.onSelect(false, -1);
39626                 }
39627             });
39628         }
39629         if (this.footer) {
39630             this.assetHeight += this.footer.getHeight();
39631         }
39632         
39633
39634         if(!this.tpl){
39635             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39636         }
39637
39638         this.view = new Roo.View(this.innerList, this.tpl, {
39639             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39640         });
39641
39642         this.view.on('click', this.onViewClick, this);
39643
39644         this.store.on('beforeload', this.onBeforeLoad, this);
39645         this.store.on('load', this.onLoad, this);
39646         this.store.on('loadexception', this.onLoadException, this);
39647
39648         if(this.resizable){
39649             this.resizer = new Roo.Resizable(this.list,  {
39650                pinned:true, handles:'se'
39651             });
39652             this.resizer.on('resize', function(r, w, h){
39653                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39654                 this.listWidth = w;
39655                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39656                 this.restrictHeight();
39657             }, this);
39658             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39659         }
39660         if(!this.editable){
39661             this.editable = true;
39662             this.setEditable(false);
39663         }  
39664         
39665         
39666         if (typeof(this.events.add.listeners) != 'undefined') {
39667             
39668             this.addicon = this.wrap.createChild(
39669                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39670        
39671             this.addicon.on('click', function(e) {
39672                 this.fireEvent('add', this);
39673             }, this);
39674         }
39675         if (typeof(this.events.edit.listeners) != 'undefined') {
39676             
39677             this.editicon = this.wrap.createChild(
39678                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39679             if (this.addicon) {
39680                 this.editicon.setStyle('margin-left', '40px');
39681             }
39682             this.editicon.on('click', function(e) {
39683                 
39684                 // we fire even  if inothing is selected..
39685                 this.fireEvent('edit', this, this.lastData );
39686                 
39687             }, this);
39688         }
39689         
39690         
39691         
39692     },
39693
39694     // private
39695     initEvents : function(){
39696         Roo.form.ComboBox.superclass.initEvents.call(this);
39697
39698         this.keyNav = new Roo.KeyNav(this.el, {
39699             "up" : function(e){
39700                 this.inKeyMode = true;
39701                 this.selectPrev();
39702             },
39703
39704             "down" : function(e){
39705                 if(!this.isExpanded()){
39706                     this.onTriggerClick();
39707                 }else{
39708                     this.inKeyMode = true;
39709                     this.selectNext();
39710                 }
39711             },
39712
39713             "enter" : function(e){
39714                 this.onViewClick();
39715                 //return true;
39716             },
39717
39718             "esc" : function(e){
39719                 this.collapse();
39720             },
39721
39722             "tab" : function(e){
39723                 this.onViewClick(false);
39724                 this.fireEvent("specialkey", this, e);
39725                 return true;
39726             },
39727
39728             scope : this,
39729
39730             doRelay : function(foo, bar, hname){
39731                 if(hname == 'down' || this.scope.isExpanded()){
39732                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39733                 }
39734                 return true;
39735             },
39736
39737             forceKeyDown: true
39738         });
39739         this.queryDelay = Math.max(this.queryDelay || 10,
39740                 this.mode == 'local' ? 10 : 250);
39741         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39742         if(this.typeAhead){
39743             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39744         }
39745         if(this.editable !== false){
39746             this.el.on("keyup", this.onKeyUp, this);
39747         }
39748         if(this.forceSelection){
39749             this.on('blur', this.doForce, this);
39750         }
39751     },
39752
39753     onDestroy : function(){
39754         if(this.view){
39755             this.view.setStore(null);
39756             this.view.el.removeAllListeners();
39757             this.view.el.remove();
39758             this.view.purgeListeners();
39759         }
39760         if(this.list){
39761             this.list.destroy();
39762         }
39763         if(this.store){
39764             this.store.un('beforeload', this.onBeforeLoad, this);
39765             this.store.un('load', this.onLoad, this);
39766             this.store.un('loadexception', this.onLoadException, this);
39767         }
39768         Roo.form.ComboBox.superclass.onDestroy.call(this);
39769     },
39770
39771     // private
39772     fireKey : function(e){
39773         if(e.isNavKeyPress() && !this.list.isVisible()){
39774             this.fireEvent("specialkey", this, e);
39775         }
39776     },
39777
39778     // private
39779     onResize: function(w, h){
39780         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39781         
39782         if(typeof w != 'number'){
39783             // we do not handle it!?!?
39784             return;
39785         }
39786         var tw = this.trigger.getWidth();
39787         tw += this.addicon ? this.addicon.getWidth() : 0;
39788         tw += this.editicon ? this.editicon.getWidth() : 0;
39789         var x = w - tw;
39790         this.el.setWidth( this.adjustWidth('input', x));
39791             
39792         this.trigger.setStyle('left', x+'px');
39793         
39794         if(this.list && this.listWidth === undefined){
39795             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39796             this.list.setWidth(lw);
39797             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39798         }
39799         
39800     
39801         
39802     },
39803
39804     /**
39805      * Allow or prevent the user from directly editing the field text.  If false is passed,
39806      * the user will only be able to select from the items defined in the dropdown list.  This method
39807      * is the runtime equivalent of setting the 'editable' config option at config time.
39808      * @param {Boolean} value True to allow the user to directly edit the field text
39809      */
39810     setEditable : function(value){
39811         if(value == this.editable){
39812             return;
39813         }
39814         this.editable = value;
39815         if(!value){
39816             this.el.dom.setAttribute('readOnly', true);
39817             this.el.on('mousedown', this.onTriggerClick,  this);
39818             this.el.addClass('x-combo-noedit');
39819         }else{
39820             this.el.dom.setAttribute('readOnly', false);
39821             this.el.un('mousedown', this.onTriggerClick,  this);
39822             this.el.removeClass('x-combo-noedit');
39823         }
39824     },
39825
39826     // private
39827     onBeforeLoad : function(){
39828         if(!this.hasFocus){
39829             return;
39830         }
39831         this.innerList.update(this.loadingText ?
39832                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39833         this.restrictHeight();
39834         this.selectedIndex = -1;
39835     },
39836
39837     // private
39838     onLoad : function(){
39839         if(!this.hasFocus){
39840             return;
39841         }
39842         if(this.store.getCount() > 0){
39843             this.expand();
39844             this.restrictHeight();
39845             if(this.lastQuery == this.allQuery){
39846                 if(this.editable){
39847                     this.el.dom.select();
39848                 }
39849                 if(!this.selectByValue(this.value, true)){
39850                     this.select(0, true);
39851                 }
39852             }else{
39853                 this.selectNext();
39854                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39855                     this.taTask.delay(this.typeAheadDelay);
39856                 }
39857             }
39858         }else{
39859             this.onEmptyResults();
39860         }
39861         //this.el.focus();
39862     },
39863     // private
39864     onLoadException : function()
39865     {
39866         this.collapse();
39867         Roo.log(this.store.reader.jsonData);
39868         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39869             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39870         }
39871         
39872         
39873     },
39874     // private
39875     onTypeAhead : function(){
39876         if(this.store.getCount() > 0){
39877             var r = this.store.getAt(0);
39878             var newValue = r.data[this.displayField];
39879             var len = newValue.length;
39880             var selStart = this.getRawValue().length;
39881             if(selStart != len){
39882                 this.setRawValue(newValue);
39883                 this.selectText(selStart, newValue.length);
39884             }
39885         }
39886     },
39887
39888     // private
39889     onSelect : function(record, index){
39890         if(this.fireEvent('beforeselect', this, record, index) !== false){
39891             this.setFromData(index > -1 ? record.data : false);
39892             this.collapse();
39893             this.fireEvent('select', this, record, index);
39894         }
39895     },
39896
39897     /**
39898      * Returns the currently selected field value or empty string if no value is set.
39899      * @return {String} value The selected value
39900      */
39901     getValue : function(){
39902         if(this.valueField){
39903             return typeof this.value != 'undefined' ? this.value : '';
39904         }
39905         return Roo.form.ComboBox.superclass.getValue.call(this);
39906     },
39907
39908     /**
39909      * Clears any text/value currently set in the field
39910      */
39911     clearValue : function(){
39912         if(this.hiddenField){
39913             this.hiddenField.value = '';
39914         }
39915         this.value = '';
39916         this.setRawValue('');
39917         this.lastSelectionText = '';
39918         
39919     },
39920
39921     /**
39922      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39923      * will be displayed in the field.  If the value does not match the data value of an existing item,
39924      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39925      * Otherwise the field will be blank (although the value will still be set).
39926      * @param {String} value The value to match
39927      */
39928     setValue : function(v){
39929         var text = v;
39930         if(this.valueField){
39931             var r = this.findRecord(this.valueField, v);
39932             if(r){
39933                 text = r.data[this.displayField];
39934             }else if(this.valueNotFoundText !== undefined){
39935                 text = this.valueNotFoundText;
39936             }
39937         }
39938         this.lastSelectionText = text;
39939         if(this.hiddenField){
39940             this.hiddenField.value = v;
39941         }
39942         Roo.form.ComboBox.superclass.setValue.call(this, text);
39943         this.value = v;
39944     },
39945     /**
39946      * @property {Object} the last set data for the element
39947      */
39948     
39949     lastData : false,
39950     /**
39951      * Sets the value of the field based on a object which is related to the record format for the store.
39952      * @param {Object} value the value to set as. or false on reset?
39953      */
39954     setFromData : function(o){
39955         var dv = ''; // display value
39956         var vv = ''; // value value..
39957         this.lastData = o;
39958         if (this.displayField) {
39959             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39960         } else {
39961             // this is an error condition!!!
39962             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39963         }
39964         
39965         if(this.valueField){
39966             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39967         }
39968         if(this.hiddenField){
39969             this.hiddenField.value = vv;
39970             
39971             this.lastSelectionText = dv;
39972             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39973             this.value = vv;
39974             return;
39975         }
39976         // no hidden field.. - we store the value in 'value', but still display
39977         // display field!!!!
39978         this.lastSelectionText = dv;
39979         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39980         this.value = vv;
39981         
39982         
39983     },
39984     // private
39985     reset : function(){
39986         // overridden so that last data is reset..
39987         this.setValue(this.resetValue);
39988         this.clearInvalid();
39989         this.lastData = false;
39990         if (this.view) {
39991             this.view.clearSelections();
39992         }
39993     },
39994     // private
39995     findRecord : function(prop, value){
39996         var record;
39997         if(this.store.getCount() > 0){
39998             this.store.each(function(r){
39999                 if(r.data[prop] == value){
40000                     record = r;
40001                     return false;
40002                 }
40003                 return true;
40004             });
40005         }
40006         return record;
40007     },
40008     
40009     getName: function()
40010     {
40011         // returns hidden if it's set..
40012         if (!this.rendered) {return ''};
40013         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40014         
40015     },
40016     // private
40017     onViewMove : function(e, t){
40018         this.inKeyMode = false;
40019     },
40020
40021     // private
40022     onViewOver : function(e, t){
40023         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
40024             return;
40025         }
40026         var item = this.view.findItemFromChild(t);
40027         if(item){
40028             var index = this.view.indexOf(item);
40029             this.select(index, false);
40030         }
40031     },
40032
40033     // private
40034     onViewClick : function(doFocus)
40035     {
40036         var index = this.view.getSelectedIndexes()[0];
40037         var r = this.store.getAt(index);
40038         if(r){
40039             this.onSelect(r, index);
40040         }
40041         if(doFocus !== false && !this.blockFocus){
40042             this.el.focus();
40043         }
40044     },
40045
40046     // private
40047     restrictHeight : function(){
40048         this.innerList.dom.style.height = '';
40049         var inner = this.innerList.dom;
40050         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
40051         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
40052         this.list.beginUpdate();
40053         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
40054         this.list.alignTo(this.el, this.listAlign);
40055         this.list.endUpdate();
40056     },
40057
40058     // private
40059     onEmptyResults : function(){
40060         this.collapse();
40061     },
40062
40063     /**
40064      * Returns true if the dropdown list is expanded, else false.
40065      */
40066     isExpanded : function(){
40067         return this.list.isVisible();
40068     },
40069
40070     /**
40071      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40072      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40073      * @param {String} value The data value of the item to select
40074      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40075      * selected item if it is not currently in view (defaults to true)
40076      * @return {Boolean} True if the value matched an item in the list, else false
40077      */
40078     selectByValue : function(v, scrollIntoView){
40079         if(v !== undefined && v !== null){
40080             var r = this.findRecord(this.valueField || this.displayField, v);
40081             if(r){
40082                 this.select(this.store.indexOf(r), scrollIntoView);
40083                 return true;
40084             }
40085         }
40086         return false;
40087     },
40088
40089     /**
40090      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40091      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40092      * @param {Number} index The zero-based index of the list item to select
40093      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40094      * selected item if it is not currently in view (defaults to true)
40095      */
40096     select : function(index, scrollIntoView){
40097         this.selectedIndex = index;
40098         this.view.select(index);
40099         if(scrollIntoView !== false){
40100             var el = this.view.getNode(index);
40101             if(el){
40102                 this.innerList.scrollChildIntoView(el, false);
40103             }
40104         }
40105     },
40106
40107     // private
40108     selectNext : function(){
40109         var ct = this.store.getCount();
40110         if(ct > 0){
40111             if(this.selectedIndex == -1){
40112                 this.select(0);
40113             }else if(this.selectedIndex < ct-1){
40114                 this.select(this.selectedIndex+1);
40115             }
40116         }
40117     },
40118
40119     // private
40120     selectPrev : function(){
40121         var ct = this.store.getCount();
40122         if(ct > 0){
40123             if(this.selectedIndex == -1){
40124                 this.select(0);
40125             }else if(this.selectedIndex != 0){
40126                 this.select(this.selectedIndex-1);
40127             }
40128         }
40129     },
40130
40131     // private
40132     onKeyUp : function(e){
40133         if(this.editable !== false && !e.isSpecialKey()){
40134             this.lastKey = e.getKey();
40135             this.dqTask.delay(this.queryDelay);
40136         }
40137     },
40138
40139     // private
40140     validateBlur : function(){
40141         return !this.list || !this.list.isVisible();   
40142     },
40143
40144     // private
40145     initQuery : function(){
40146         this.doQuery(this.getRawValue());
40147     },
40148
40149     // private
40150     doForce : function(){
40151         if(this.el.dom.value.length > 0){
40152             this.el.dom.value =
40153                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40154              
40155         }
40156     },
40157
40158     /**
40159      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40160      * query allowing the query action to be canceled if needed.
40161      * @param {String} query The SQL query to execute
40162      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40163      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40164      * saved in the current store (defaults to false)
40165      */
40166     doQuery : function(q, forceAll){
40167         if(q === undefined || q === null){
40168             q = '';
40169         }
40170         var qe = {
40171             query: q,
40172             forceAll: forceAll,
40173             combo: this,
40174             cancel:false
40175         };
40176         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40177             return false;
40178         }
40179         q = qe.query;
40180         forceAll = qe.forceAll;
40181         if(forceAll === true || (q.length >= this.minChars)){
40182             if(this.lastQuery != q || this.alwaysQuery){
40183                 this.lastQuery = q;
40184                 if(this.mode == 'local'){
40185                     this.selectedIndex = -1;
40186                     if(forceAll){
40187                         this.store.clearFilter();
40188                     }else{
40189                         this.store.filter(this.displayField, q);
40190                     }
40191                     this.onLoad();
40192                 }else{
40193                     this.store.baseParams[this.queryParam] = q;
40194                     this.store.load({
40195                         params: this.getParams(q)
40196                     });
40197                     this.expand();
40198                 }
40199             }else{
40200                 this.selectedIndex = -1;
40201                 this.onLoad();   
40202             }
40203         }
40204     },
40205
40206     // private
40207     getParams : function(q){
40208         var p = {};
40209         //p[this.queryParam] = q;
40210         if(this.pageSize){
40211             p.start = 0;
40212             p.limit = this.pageSize;
40213         }
40214         return p;
40215     },
40216
40217     /**
40218      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40219      */
40220     collapse : function(){
40221         if(!this.isExpanded()){
40222             return;
40223         }
40224         this.list.hide();
40225         Roo.get(document).un('mousedown', this.collapseIf, this);
40226         Roo.get(document).un('mousewheel', this.collapseIf, this);
40227         if (!this.editable) {
40228             Roo.get(document).un('keydown', this.listKeyPress, this);
40229         }
40230         this.fireEvent('collapse', this);
40231     },
40232
40233     // private
40234     collapseIf : function(e){
40235         if(!e.within(this.wrap) && !e.within(this.list)){
40236             this.collapse();
40237         }
40238     },
40239
40240     /**
40241      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40242      */
40243     expand : function(){
40244         if(this.isExpanded() || !this.hasFocus){
40245             return;
40246         }
40247         this.list.alignTo(this.el, this.listAlign);
40248         this.list.show();
40249         Roo.get(document).on('mousedown', this.collapseIf, this);
40250         Roo.get(document).on('mousewheel', this.collapseIf, this);
40251         if (!this.editable) {
40252             Roo.get(document).on('keydown', this.listKeyPress, this);
40253         }
40254         
40255         this.fireEvent('expand', this);
40256     },
40257
40258     // private
40259     // Implements the default empty TriggerField.onTriggerClick function
40260     onTriggerClick : function(){
40261         if(this.disabled){
40262             return;
40263         }
40264         if(this.isExpanded()){
40265             this.collapse();
40266             if (!this.blockFocus) {
40267                 this.el.focus();
40268             }
40269             
40270         }else {
40271             this.hasFocus = true;
40272             if(this.triggerAction == 'all') {
40273                 this.doQuery(this.allQuery, true);
40274             } else {
40275                 this.doQuery(this.getRawValue());
40276             }
40277             if (!this.blockFocus) {
40278                 this.el.focus();
40279             }
40280         }
40281     },
40282     listKeyPress : function(e)
40283     {
40284         //Roo.log('listkeypress');
40285         // scroll to first matching element based on key pres..
40286         if (e.isSpecialKey()) {
40287             return false;
40288         }
40289         var k = String.fromCharCode(e.getKey()).toUpperCase();
40290         //Roo.log(k);
40291         var match  = false;
40292         var csel = this.view.getSelectedNodes();
40293         var cselitem = false;
40294         if (csel.length) {
40295             var ix = this.view.indexOf(csel[0]);
40296             cselitem  = this.store.getAt(ix);
40297             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40298                 cselitem = false;
40299             }
40300             
40301         }
40302         
40303         this.store.each(function(v) { 
40304             if (cselitem) {
40305                 // start at existing selection.
40306                 if (cselitem.id == v.id) {
40307                     cselitem = false;
40308                 }
40309                 return;
40310             }
40311                 
40312             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40313                 match = this.store.indexOf(v);
40314                 return false;
40315             }
40316         }, this);
40317         
40318         if (match === false) {
40319             return true; // no more action?
40320         }
40321         // scroll to?
40322         this.view.select(match);
40323         var sn = Roo.get(this.view.getSelectedNodes()[0])
40324         sn.scrollIntoView(sn.dom.parentNode, false);
40325     }
40326
40327     /** 
40328     * @cfg {Boolean} grow 
40329     * @hide 
40330     */
40331     /** 
40332     * @cfg {Number} growMin 
40333     * @hide 
40334     */
40335     /** 
40336     * @cfg {Number} growMax 
40337     * @hide 
40338     */
40339     /**
40340      * @hide
40341      * @method autoSize
40342      */
40343 });/*
40344  * Copyright(c) 2010-2012, Roo J Solutions Limited
40345  *
40346  * Licence LGPL
40347  *
40348  */
40349
40350 /**
40351  * @class Roo.form.ComboBoxArray
40352  * @extends Roo.form.TextField
40353  * A facebook style adder... for lists of email / people / countries  etc...
40354  * pick multiple items from a combo box, and shows each one.
40355  *
40356  *  Fred [x]  Brian [x]  [Pick another |v]
40357  *
40358  *
40359  *  For this to work: it needs various extra information
40360  *    - normal combo problay has
40361  *      name, hiddenName
40362  *    + displayField, valueField
40363  *
40364  *    For our purpose...
40365  *
40366  *
40367  *   If we change from 'extends' to wrapping...
40368  *   
40369  *  
40370  *
40371  
40372  
40373  * @constructor
40374  * Create a new ComboBoxArray.
40375  * @param {Object} config Configuration options
40376  */
40377  
40378
40379 Roo.form.ComboBoxArray = function(config)
40380 {
40381     this.addEvents({
40382         /**
40383          * @event beforeremove
40384          * Fires before remove the value from the list
40385              * @param {Roo.form.ComboBoxArray} _self This combo box array
40386              * @param {Roo.form.ComboBoxArray.Item} item removed item
40387              */
40388         'beforeremove' : true,
40389         /**
40390          * @event remove
40391          * Fires when remove the value from the list
40392              * @param {Roo.form.ComboBoxArray} _self This combo box array
40393              * @param {Roo.form.ComboBoxArray.Item} item removed item
40394              */
40395         'remove' : true
40396         
40397         
40398     });
40399     
40400     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40401     
40402     this.items = new Roo.util.MixedCollection(false);
40403     
40404     // construct the child combo...
40405     
40406     
40407     
40408     
40409    
40410     
40411 }
40412
40413  
40414 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40415
40416     /**
40417      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40418      */
40419     
40420     lastData : false,
40421     
40422     // behavies liek a hiddne field
40423     inputType:      'hidden',
40424     /**
40425      * @cfg {Number} width The width of the box that displays the selected element
40426      */ 
40427     width:          300,
40428
40429     
40430     
40431     /**
40432      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40433      */
40434     name : false,
40435     /**
40436      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40437      */
40438     hiddenName : false,
40439     
40440     
40441     // private the array of items that are displayed..
40442     items  : false,
40443     // private - the hidden field el.
40444     hiddenEl : false,
40445     // private - the filed el..
40446     el : false,
40447     
40448     //validateValue : function() { return true; }, // all values are ok!
40449     //onAddClick: function() { },
40450     
40451     onRender : function(ct, position) 
40452     {
40453         
40454         // create the standard hidden element
40455         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40456         
40457         
40458         // give fake names to child combo;
40459         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40460         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40461         
40462         this.combo = Roo.factory(this.combo, Roo.form);
40463         this.combo.onRender(ct, position);
40464         if (typeof(this.combo.width) != 'undefined') {
40465             this.combo.onResize(this.combo.width,0);
40466         }
40467         
40468         this.combo.initEvents();
40469         
40470         // assigned so form know we need to do this..
40471         this.store          = this.combo.store;
40472         this.valueField     = this.combo.valueField;
40473         this.displayField   = this.combo.displayField ;
40474         
40475         
40476         this.combo.wrap.addClass('x-cbarray-grp');
40477         
40478         var cbwrap = this.combo.wrap.createChild(
40479             {tag: 'div', cls: 'x-cbarray-cb'},
40480             this.combo.el.dom
40481         );
40482         
40483              
40484         this.hiddenEl = this.combo.wrap.createChild({
40485             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40486         });
40487         this.el = this.combo.wrap.createChild({
40488             tag: 'input',  type:'hidden' , name: this.name, value : ''
40489         });
40490          //   this.el.dom.removeAttribute("name");
40491         
40492         
40493         this.outerWrap = this.combo.wrap;
40494         this.wrap = cbwrap;
40495         
40496         this.outerWrap.setWidth(this.width);
40497         this.outerWrap.dom.removeChild(this.el.dom);
40498         
40499         this.wrap.dom.appendChild(this.el.dom);
40500         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40501         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40502         
40503         this.combo.trigger.setStyle('position','relative');
40504         this.combo.trigger.setStyle('left', '0px');
40505         this.combo.trigger.setStyle('top', '2px');
40506         
40507         this.combo.el.setStyle('vertical-align', 'text-bottom');
40508         
40509         //this.trigger.setStyle('vertical-align', 'top');
40510         
40511         // this should use the code from combo really... on('add' ....)
40512         if (this.adder) {
40513             
40514         
40515             this.adder = this.outerWrap.createChild(
40516                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40517             var _t = this;
40518             this.adder.on('click', function(e) {
40519                 _t.fireEvent('adderclick', this, e);
40520             }, _t);
40521         }
40522         //var _t = this;
40523         //this.adder.on('click', this.onAddClick, _t);
40524         
40525         
40526         this.combo.on('select', function(cb, rec, ix) {
40527             this.addItem(rec.data);
40528             
40529             cb.setValue('');
40530             cb.el.dom.value = '';
40531             //cb.lastData = rec.data;
40532             // add to list
40533             
40534         }, this);
40535         
40536         
40537     },
40538     
40539     
40540     getName: function()
40541     {
40542         // returns hidden if it's set..
40543         if (!this.rendered) {return ''};
40544         return  this.hiddenName ? this.hiddenName : this.name;
40545         
40546     },
40547     
40548     
40549     onResize: function(w, h){
40550         
40551         return;
40552         // not sure if this is needed..
40553         //this.combo.onResize(w,h);
40554         
40555         if(typeof w != 'number'){
40556             // we do not handle it!?!?
40557             return;
40558         }
40559         var tw = this.combo.trigger.getWidth();
40560         tw += this.addicon ? this.addicon.getWidth() : 0;
40561         tw += this.editicon ? this.editicon.getWidth() : 0;
40562         var x = w - tw;
40563         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40564             
40565         this.combo.trigger.setStyle('left', '0px');
40566         
40567         if(this.list && this.listWidth === undefined){
40568             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40569             this.list.setWidth(lw);
40570             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40571         }
40572         
40573     
40574         
40575     },
40576     
40577     addItem: function(rec)
40578     {
40579         var valueField = this.combo.valueField;
40580         var displayField = this.combo.displayField;
40581         if (this.items.indexOfKey(rec[valueField]) > -1) {
40582             //console.log("GOT " + rec.data.id);
40583             return;
40584         }
40585         
40586         var x = new Roo.form.ComboBoxArray.Item({
40587             //id : rec[this.idField],
40588             data : rec,
40589             displayField : displayField ,
40590             tipField : displayField ,
40591             cb : this
40592         });
40593         // use the 
40594         this.items.add(rec[valueField],x);
40595         // add it before the element..
40596         this.updateHiddenEl();
40597         x.render(this.outerWrap, this.wrap.dom);
40598         // add the image handler..
40599     },
40600     
40601     updateHiddenEl : function()
40602     {
40603         this.validate();
40604         if (!this.hiddenEl) {
40605             return;
40606         }
40607         var ar = [];
40608         var idField = this.combo.valueField;
40609         
40610         this.items.each(function(f) {
40611             ar.push(f.data[idField]);
40612            
40613         });
40614         this.hiddenEl.dom.value = ar.join(',');
40615         this.validate();
40616     },
40617     
40618     reset : function()
40619     {
40620         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40621         this.items.each(function(f) {
40622            f.remove(); 
40623         });
40624         this.el.dom.value = '';
40625         if (this.hiddenEl) {
40626             this.hiddenEl.dom.value = '';
40627         }
40628         
40629     },
40630     getValue: function()
40631     {
40632         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40633     },
40634     setValue: function(v) // not a valid action - must use addItems..
40635     {
40636          
40637         this.reset();
40638         
40639         
40640         
40641         if (this.store.isLocal && (typeof(v) == 'string')) {
40642             // then we can use the store to find the values..
40643             // comma seperated at present.. this needs to allow JSON based encoding..
40644             this.hiddenEl.value  = v;
40645             var v_ar = [];
40646             Roo.each(v.split(','), function(k) {
40647                 Roo.log("CHECK " + this.valueField + ',' + k);
40648                 var li = this.store.query(this.valueField, k);
40649                 if (!li.length) {
40650                     return;
40651                 }
40652                 var add = {};
40653                 add[this.valueField] = k;
40654                 add[this.displayField] = li.item(0).data[this.displayField];
40655                 
40656                 this.addItem(add);
40657             }, this) 
40658              
40659         }
40660         if (typeof(v) == 'object' ) {
40661             // then let's assume it's an array of objects..
40662             Roo.each(v, function(l) {
40663                 this.addItem(l);
40664             }, this);
40665              
40666         }
40667         
40668         
40669     },
40670     setFromData: function(v)
40671     {
40672         // this recieves an object, if setValues is called.
40673         this.reset();
40674         this.el.dom.value = v[this.displayField];
40675         this.hiddenEl.dom.value = v[this.valueField];
40676         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40677             return;
40678         }
40679         var kv = v[this.valueField];
40680         var dv = v[this.displayField];
40681         kv = typeof(kv) != 'string' ? '' : kv;
40682         dv = typeof(dv) != 'string' ? '' : dv;
40683         
40684         
40685         var keys = kv.split(',');
40686         var display = dv.split(',');
40687         for (var i = 0 ; i < keys.length; i++) {
40688             
40689             add = {};
40690             add[this.valueField] = keys[i];
40691             add[this.displayField] = display[i];
40692             this.addItem(add);
40693         }
40694       
40695         
40696     },
40697     
40698     /**
40699      * Validates the combox array value
40700      * @return {Boolean} True if the value is valid, else false
40701      */
40702     validate : function(){
40703         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40704             this.clearInvalid();
40705             return true;
40706         }
40707         return false;
40708     },
40709     
40710     validateValue : function(value){
40711         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40712         
40713     },
40714     
40715     /*@
40716      * overide
40717      * 
40718      */
40719     isDirty : function() {
40720         if(this.disabled) {
40721             return false;
40722         }
40723         
40724         try {
40725             var d = Roo.decode(String(this.originalValue));
40726         } catch (e) {
40727             return String(this.getValue()) !== String(this.originalValue);
40728         }
40729         
40730         var originalValue = [];
40731         
40732         for (var i = 0; i < d.length; i++){
40733             originalValue.push(d[i][this.valueField]);
40734         }
40735         
40736         return String(this.getValue()) !== String(originalValue.join(','));
40737         
40738     }
40739     
40740 });
40741
40742
40743
40744 /**
40745  * @class Roo.form.ComboBoxArray.Item
40746  * @extends Roo.BoxComponent
40747  * A selected item in the list
40748  *  Fred [x]  Brian [x]  [Pick another |v]
40749  * 
40750  * @constructor
40751  * Create a new item.
40752  * @param {Object} config Configuration options
40753  */
40754  
40755 Roo.form.ComboBoxArray.Item = function(config) {
40756     config.id = Roo.id();
40757     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40758 }
40759
40760 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40761     data : {},
40762     cb: false,
40763     displayField : false,
40764     tipField : false,
40765     
40766     
40767     defaultAutoCreate : {
40768         tag: 'div',
40769         cls: 'x-cbarray-item',
40770         cn : [ 
40771             { tag: 'div' },
40772             {
40773                 tag: 'img',
40774                 width:16,
40775                 height : 16,
40776                 src : Roo.BLANK_IMAGE_URL ,
40777                 align: 'center'
40778             }
40779         ]
40780         
40781     },
40782     
40783  
40784     onRender : function(ct, position)
40785     {
40786         Roo.form.Field.superclass.onRender.call(this, ct, position);
40787         
40788         if(!this.el){
40789             var cfg = this.getAutoCreate();
40790             this.el = ct.createChild(cfg, position);
40791         }
40792         
40793         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40794         
40795         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40796             this.cb.renderer(this.data) :
40797             String.format('{0}',this.data[this.displayField]);
40798         
40799             
40800         this.el.child('div').dom.setAttribute('qtip',
40801                         String.format('{0}',this.data[this.tipField])
40802         );
40803         
40804         this.el.child('img').on('click', this.remove, this);
40805         
40806     },
40807    
40808     remove : function()
40809     {
40810         if(this.cb.disabled){
40811             return;
40812         }
40813         
40814         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
40815             this.cb.items.remove(this);
40816             this.el.child('img').un('click', this.remove, this);
40817             this.el.remove();
40818             this.cb.updateHiddenEl();
40819
40820             this.cb.fireEvent('remove', this.cb, this);
40821         }
40822         
40823     }
40824 });/*
40825  * Based on:
40826  * Ext JS Library 1.1.1
40827  * Copyright(c) 2006-2007, Ext JS, LLC.
40828  *
40829  * Originally Released Under LGPL - original licence link has changed is not relivant.
40830  *
40831  * Fork - LGPL
40832  * <script type="text/javascript">
40833  */
40834 /**
40835  * @class Roo.form.Checkbox
40836  * @extends Roo.form.Field
40837  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40838  * @constructor
40839  * Creates a new Checkbox
40840  * @param {Object} config Configuration options
40841  */
40842 Roo.form.Checkbox = function(config){
40843     Roo.form.Checkbox.superclass.constructor.call(this, config);
40844     this.addEvents({
40845         /**
40846          * @event check
40847          * Fires when the checkbox is checked or unchecked.
40848              * @param {Roo.form.Checkbox} this This checkbox
40849              * @param {Boolean} checked The new checked value
40850              */
40851         check : true
40852     });
40853 };
40854
40855 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40856     /**
40857      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40858      */
40859     focusClass : undefined,
40860     /**
40861      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40862      */
40863     fieldClass: "x-form-field",
40864     /**
40865      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40866      */
40867     checked: false,
40868     /**
40869      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40870      * {tag: "input", type: "checkbox", autocomplete: "off"})
40871      */
40872     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40873     /**
40874      * @cfg {String} boxLabel The text that appears beside the checkbox
40875      */
40876     boxLabel : "",
40877     /**
40878      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40879      */  
40880     inputValue : '1',
40881     /**
40882      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40883      */
40884      valueOff: '0', // value when not checked..
40885
40886     actionMode : 'viewEl', 
40887     //
40888     // private
40889     itemCls : 'x-menu-check-item x-form-item',
40890     groupClass : 'x-menu-group-item',
40891     inputType : 'hidden',
40892     
40893     
40894     inSetChecked: false, // check that we are not calling self...
40895     
40896     inputElement: false, // real input element?
40897     basedOn: false, // ????
40898     
40899     isFormField: true, // not sure where this is needed!!!!
40900
40901     onResize : function(){
40902         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40903         if(!this.boxLabel){
40904             this.el.alignTo(this.wrap, 'c-c');
40905         }
40906     },
40907
40908     initEvents : function(){
40909         Roo.form.Checkbox.superclass.initEvents.call(this);
40910         this.el.on("click", this.onClick,  this);
40911         this.el.on("change", this.onClick,  this);
40912     },
40913
40914
40915     getResizeEl : function(){
40916         return this.wrap;
40917     },
40918
40919     getPositionEl : function(){
40920         return this.wrap;
40921     },
40922
40923     // private
40924     onRender : function(ct, position){
40925         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40926         /*
40927         if(this.inputValue !== undefined){
40928             this.el.dom.value = this.inputValue;
40929         }
40930         */
40931         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40932         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40933         var viewEl = this.wrap.createChild({ 
40934             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40935         this.viewEl = viewEl;   
40936         this.wrap.on('click', this.onClick,  this); 
40937         
40938         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40939         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40940         
40941         
40942         
40943         if(this.boxLabel){
40944             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40945         //    viewEl.on('click', this.onClick,  this); 
40946         }
40947         //if(this.checked){
40948             this.setChecked(this.checked);
40949         //}else{
40950             //this.checked = this.el.dom;
40951         //}
40952
40953     },
40954
40955     // private
40956     initValue : Roo.emptyFn,
40957
40958     /**
40959      * Returns the checked state of the checkbox.
40960      * @return {Boolean} True if checked, else false
40961      */
40962     getValue : function(){
40963         if(this.el){
40964             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40965         }
40966         return this.valueOff;
40967         
40968     },
40969
40970         // private
40971     onClick : function(){ 
40972         if (this.disabled) {
40973             return;
40974         }
40975         this.setChecked(!this.checked);
40976
40977         //if(this.el.dom.checked != this.checked){
40978         //    this.setValue(this.el.dom.checked);
40979        // }
40980     },
40981
40982     /**
40983      * Sets the checked state of the checkbox.
40984      * On is always based on a string comparison between inputValue and the param.
40985      * @param {Boolean/String} value - the value to set 
40986      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40987      */
40988     setValue : function(v,suppressEvent){
40989         
40990         
40991         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40992         //if(this.el && this.el.dom){
40993         //    this.el.dom.checked = this.checked;
40994         //    this.el.dom.defaultChecked = this.checked;
40995         //}
40996         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40997         //this.fireEvent("check", this, this.checked);
40998     },
40999     // private..
41000     setChecked : function(state,suppressEvent)
41001     {
41002         if (this.inSetChecked) {
41003             this.checked = state;
41004             return;
41005         }
41006         
41007     
41008         if(this.wrap){
41009             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
41010         }
41011         this.checked = state;
41012         if(suppressEvent !== true){
41013             this.fireEvent('check', this, state);
41014         }
41015         this.inSetChecked = true;
41016         this.el.dom.value = state ? this.inputValue : this.valueOff;
41017         this.inSetChecked = false;
41018         
41019     },
41020     // handle setting of hidden value by some other method!!?!?
41021     setFromHidden: function()
41022     {
41023         if(!this.el){
41024             return;
41025         }
41026         //console.log("SET FROM HIDDEN");
41027         //alert('setFrom hidden');
41028         this.setValue(this.el.dom.value);
41029     },
41030     
41031     onDestroy : function()
41032     {
41033         if(this.viewEl){
41034             Roo.get(this.viewEl).remove();
41035         }
41036          
41037         Roo.form.Checkbox.superclass.onDestroy.call(this);
41038     }
41039
41040 });/*
41041  * Based on:
41042  * Ext JS Library 1.1.1
41043  * Copyright(c) 2006-2007, Ext JS, LLC.
41044  *
41045  * Originally Released Under LGPL - original licence link has changed is not relivant.
41046  *
41047  * Fork - LGPL
41048  * <script type="text/javascript">
41049  */
41050  
41051 /**
41052  * @class Roo.form.Radio
41053  * @extends Roo.form.Checkbox
41054  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
41055  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
41056  * @constructor
41057  * Creates a new Radio
41058  * @param {Object} config Configuration options
41059  */
41060 Roo.form.Radio = function(){
41061     Roo.form.Radio.superclass.constructor.apply(this, arguments);
41062 };
41063 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
41064     inputType: 'radio',
41065
41066     /**
41067      * If this radio is part of a group, it will return the selected value
41068      * @return {String}
41069      */
41070     getGroupValue : function(){
41071         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
41072     },
41073     
41074     
41075     onRender : function(ct, position){
41076         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
41077         
41078         if(this.inputValue !== undefined){
41079             this.el.dom.value = this.inputValue;
41080         }
41081          
41082         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41083         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41084         //var viewEl = this.wrap.createChild({ 
41085         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41086         //this.viewEl = viewEl;   
41087         //this.wrap.on('click', this.onClick,  this); 
41088         
41089         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41090         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41091         
41092         
41093         
41094         if(this.boxLabel){
41095             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41096         //    viewEl.on('click', this.onClick,  this); 
41097         }
41098          if(this.checked){
41099             this.el.dom.checked =   'checked' ;
41100         }
41101          
41102     } 
41103     
41104     
41105 });//<script type="text/javascript">
41106
41107 /*
41108  * Based  Ext JS Library 1.1.1
41109  * Copyright(c) 2006-2007, Ext JS, LLC.
41110  * LGPL
41111  *
41112  */
41113  
41114 /**
41115  * @class Roo.HtmlEditorCore
41116  * @extends Roo.Component
41117  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41118  *
41119  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41120  */
41121
41122 Roo.HtmlEditorCore = function(config){
41123     
41124     
41125     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41126     
41127     
41128     this.addEvents({
41129         /**
41130          * @event initialize
41131          * Fires when the editor is fully initialized (including the iframe)
41132          * @param {Roo.HtmlEditorCore} this
41133          */
41134         initialize: true,
41135         /**
41136          * @event activate
41137          * Fires when the editor is first receives the focus. Any insertion must wait
41138          * until after this event.
41139          * @param {Roo.HtmlEditorCore} this
41140          */
41141         activate: true,
41142          /**
41143          * @event beforesync
41144          * Fires before the textarea is updated with content from the editor iframe. Return false
41145          * to cancel the sync.
41146          * @param {Roo.HtmlEditorCore} this
41147          * @param {String} html
41148          */
41149         beforesync: true,
41150          /**
41151          * @event beforepush
41152          * Fires before the iframe editor is updated with content from the textarea. Return false
41153          * to cancel the push.
41154          * @param {Roo.HtmlEditorCore} this
41155          * @param {String} html
41156          */
41157         beforepush: true,
41158          /**
41159          * @event sync
41160          * Fires when the textarea is updated with content from the editor iframe.
41161          * @param {Roo.HtmlEditorCore} this
41162          * @param {String} html
41163          */
41164         sync: true,
41165          /**
41166          * @event push
41167          * Fires when the iframe editor is updated with content from the textarea.
41168          * @param {Roo.HtmlEditorCore} this
41169          * @param {String} html
41170          */
41171         push: true,
41172         
41173         /**
41174          * @event editorevent
41175          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41176          * @param {Roo.HtmlEditorCore} this
41177          */
41178         editorevent: true
41179         
41180     });
41181     
41182     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41183     
41184     // defaults : white / black...
41185     this.applyBlacklists();
41186     
41187     
41188     
41189 };
41190
41191
41192 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41193
41194
41195      /**
41196      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41197      */
41198     
41199     owner : false,
41200     
41201      /**
41202      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41203      *                        Roo.resizable.
41204      */
41205     resizable : false,
41206      /**
41207      * @cfg {Number} height (in pixels)
41208      */   
41209     height: 300,
41210    /**
41211      * @cfg {Number} width (in pixels)
41212      */   
41213     width: 500,
41214     
41215     /**
41216      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41217      * 
41218      */
41219     stylesheets: false,
41220     
41221     // id of frame..
41222     frameId: false,
41223     
41224     // private properties
41225     validationEvent : false,
41226     deferHeight: true,
41227     initialized : false,
41228     activated : false,
41229     sourceEditMode : false,
41230     onFocus : Roo.emptyFn,
41231     iframePad:3,
41232     hideMode:'offsets',
41233     
41234     clearUp: true,
41235     
41236     // blacklist + whitelisted elements..
41237     black: false,
41238     white: false,
41239      
41240     
41241
41242     /**
41243      * Protected method that will not generally be called directly. It
41244      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41245      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41246      */
41247     getDocMarkup : function(){
41248         // body styles..
41249         var st = '';
41250         
41251         // inherit styels from page...?? 
41252         if (this.stylesheets === false) {
41253             
41254             Roo.get(document.head).select('style').each(function(node) {
41255                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41256             });
41257             
41258             Roo.get(document.head).select('link').each(function(node) { 
41259                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41260             });
41261             
41262         } else if (!this.stylesheets.length) {
41263                 // simple..
41264                 st = '<style type="text/css">' +
41265                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41266                    '</style>';
41267         } else { 
41268             
41269         }
41270         
41271         st +=  '<style type="text/css">' +
41272             'IMG { cursor: pointer } ' +
41273         '</style>';
41274
41275         
41276         return '<html><head>' + st  +
41277             //<style type="text/css">' +
41278             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41279             //'</style>' +
41280             ' </head><body class="roo-htmleditor-body"></body></html>';
41281     },
41282
41283     // private
41284     onRender : function(ct, position)
41285     {
41286         var _t = this;
41287         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41288         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41289         
41290         
41291         this.el.dom.style.border = '0 none';
41292         this.el.dom.setAttribute('tabIndex', -1);
41293         this.el.addClass('x-hidden hide');
41294         
41295         
41296         
41297         if(Roo.isIE){ // fix IE 1px bogus margin
41298             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41299         }
41300        
41301         
41302         this.frameId = Roo.id();
41303         
41304          
41305         
41306         var iframe = this.owner.wrap.createChild({
41307             tag: 'iframe',
41308             cls: 'form-control', // bootstrap..
41309             id: this.frameId,
41310             name: this.frameId,
41311             frameBorder : 'no',
41312             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41313         }, this.el
41314         );
41315         
41316         
41317         this.iframe = iframe.dom;
41318
41319          this.assignDocWin();
41320         
41321         this.doc.designMode = 'on';
41322        
41323         this.doc.open();
41324         this.doc.write(this.getDocMarkup());
41325         this.doc.close();
41326
41327         
41328         var task = { // must defer to wait for browser to be ready
41329             run : function(){
41330                 //console.log("run task?" + this.doc.readyState);
41331                 this.assignDocWin();
41332                 if(this.doc.body || this.doc.readyState == 'complete'){
41333                     try {
41334                         this.doc.designMode="on";
41335                     } catch (e) {
41336                         return;
41337                     }
41338                     Roo.TaskMgr.stop(task);
41339                     this.initEditor.defer(10, this);
41340                 }
41341             },
41342             interval : 10,
41343             duration: 10000,
41344             scope: this
41345         };
41346         Roo.TaskMgr.start(task);
41347
41348     },
41349
41350     // private
41351     onResize : function(w, h)
41352     {
41353          Roo.log('resize: ' +w + ',' + h );
41354         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41355         if(!this.iframe){
41356             return;
41357         }
41358         if(typeof w == 'number'){
41359             
41360             this.iframe.style.width = w + 'px';
41361         }
41362         if(typeof h == 'number'){
41363             
41364             this.iframe.style.height = h + 'px';
41365             if(this.doc){
41366                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41367             }
41368         }
41369         
41370     },
41371
41372     /**
41373      * Toggles the editor between standard and source edit mode.
41374      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41375      */
41376     toggleSourceEdit : function(sourceEditMode){
41377         
41378         this.sourceEditMode = sourceEditMode === true;
41379         
41380         if(this.sourceEditMode){
41381  
41382             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41383             
41384         }else{
41385             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41386             //this.iframe.className = '';
41387             this.deferFocus();
41388         }
41389         //this.setSize(this.owner.wrap.getSize());
41390         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41391     },
41392
41393     
41394   
41395
41396     /**
41397      * Protected method that will not generally be called directly. If you need/want
41398      * custom HTML cleanup, this is the method you should override.
41399      * @param {String} html The HTML to be cleaned
41400      * return {String} The cleaned HTML
41401      */
41402     cleanHtml : function(html){
41403         html = String(html);
41404         if(html.length > 5){
41405             if(Roo.isSafari){ // strip safari nonsense
41406                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41407             }
41408         }
41409         if(html == '&nbsp;'){
41410             html = '';
41411         }
41412         return html;
41413     },
41414
41415     /**
41416      * HTML Editor -> Textarea
41417      * Protected method that will not generally be called directly. Syncs the contents
41418      * of the editor iframe with the textarea.
41419      */
41420     syncValue : function(){
41421         if(this.initialized){
41422             var bd = (this.doc.body || this.doc.documentElement);
41423             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41424             var html = bd.innerHTML;
41425             if(Roo.isSafari){
41426                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41427                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41428                 if(m && m[1]){
41429                     html = '<div style="'+m[0]+'">' + html + '</div>';
41430                 }
41431             }
41432             html = this.cleanHtml(html);
41433             // fix up the special chars.. normaly like back quotes in word...
41434             // however we do not want to do this with chinese..
41435             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41436                 var cc = b.charCodeAt();
41437                 if (
41438                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41439                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41440                     (cc >= 0xf900 && cc < 0xfb00 )
41441                 ) {
41442                         return b;
41443                 }
41444                 return "&#"+cc+";" 
41445             });
41446             if(this.owner.fireEvent('beforesync', this, html) !== false){
41447                 this.el.dom.value = html;
41448                 this.owner.fireEvent('sync', this, html);
41449             }
41450         }
41451     },
41452
41453     /**
41454      * Protected method that will not generally be called directly. Pushes the value of the textarea
41455      * into the iframe editor.
41456      */
41457     pushValue : function(){
41458         if(this.initialized){
41459             var v = this.el.dom.value.trim();
41460             
41461 //            if(v.length < 1){
41462 //                v = '&#160;';
41463 //            }
41464             
41465             if(this.owner.fireEvent('beforepush', this, v) !== false){
41466                 var d = (this.doc.body || this.doc.documentElement);
41467                 d.innerHTML = v;
41468                 this.cleanUpPaste();
41469                 this.el.dom.value = d.innerHTML;
41470                 this.owner.fireEvent('push', this, v);
41471             }
41472         }
41473     },
41474
41475     // private
41476     deferFocus : function(){
41477         this.focus.defer(10, this);
41478     },
41479
41480     // doc'ed in Field
41481     focus : function(){
41482         if(this.win && !this.sourceEditMode){
41483             this.win.focus();
41484         }else{
41485             this.el.focus();
41486         }
41487     },
41488     
41489     assignDocWin: function()
41490     {
41491         var iframe = this.iframe;
41492         
41493          if(Roo.isIE){
41494             this.doc = iframe.contentWindow.document;
41495             this.win = iframe.contentWindow;
41496         } else {
41497 //            if (!Roo.get(this.frameId)) {
41498 //                return;
41499 //            }
41500 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41501 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41502             
41503             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41504                 return;
41505             }
41506             
41507             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41508             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41509         }
41510     },
41511     
41512     // private
41513     initEditor : function(){
41514         //console.log("INIT EDITOR");
41515         this.assignDocWin();
41516         
41517         
41518         
41519         this.doc.designMode="on";
41520         this.doc.open();
41521         this.doc.write(this.getDocMarkup());
41522         this.doc.close();
41523         
41524         var dbody = (this.doc.body || this.doc.documentElement);
41525         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41526         // this copies styles from the containing element into thsi one..
41527         // not sure why we need all of this..
41528         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41529         
41530         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41531         //ss['background-attachment'] = 'fixed'; // w3c
41532         dbody.bgProperties = 'fixed'; // ie
41533         //Roo.DomHelper.applyStyles(dbody, ss);
41534         Roo.EventManager.on(this.doc, {
41535             //'mousedown': this.onEditorEvent,
41536             'mouseup': this.onEditorEvent,
41537             'dblclick': this.onEditorEvent,
41538             'click': this.onEditorEvent,
41539             'keyup': this.onEditorEvent,
41540             buffer:100,
41541             scope: this
41542         });
41543         if(Roo.isGecko){
41544             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41545         }
41546         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41547             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41548         }
41549         this.initialized = true;
41550
41551         this.owner.fireEvent('initialize', this);
41552         this.pushValue();
41553     },
41554
41555     // private
41556     onDestroy : function(){
41557         
41558         
41559         
41560         if(this.rendered){
41561             
41562             //for (var i =0; i < this.toolbars.length;i++) {
41563             //    // fixme - ask toolbars for heights?
41564             //    this.toolbars[i].onDestroy();
41565            // }
41566             
41567             //this.wrap.dom.innerHTML = '';
41568             //this.wrap.remove();
41569         }
41570     },
41571
41572     // private
41573     onFirstFocus : function(){
41574         
41575         this.assignDocWin();
41576         
41577         
41578         this.activated = true;
41579          
41580     
41581         if(Roo.isGecko){ // prevent silly gecko errors
41582             this.win.focus();
41583             var s = this.win.getSelection();
41584             if(!s.focusNode || s.focusNode.nodeType != 3){
41585                 var r = s.getRangeAt(0);
41586                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41587                 r.collapse(true);
41588                 this.deferFocus();
41589             }
41590             try{
41591                 this.execCmd('useCSS', true);
41592                 this.execCmd('styleWithCSS', false);
41593             }catch(e){}
41594         }
41595         this.owner.fireEvent('activate', this);
41596     },
41597
41598     // private
41599     adjustFont: function(btn){
41600         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41601         //if(Roo.isSafari){ // safari
41602         //    adjust *= 2;
41603        // }
41604         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41605         if(Roo.isSafari){ // safari
41606             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41607             v =  (v < 10) ? 10 : v;
41608             v =  (v > 48) ? 48 : v;
41609             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41610             
41611         }
41612         
41613         
41614         v = Math.max(1, v+adjust);
41615         
41616         this.execCmd('FontSize', v  );
41617     },
41618
41619     onEditorEvent : function(e)
41620     {
41621         this.owner.fireEvent('editorevent', this, e);
41622       //  this.updateToolbar();
41623         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41624     },
41625
41626     insertTag : function(tg)
41627     {
41628         // could be a bit smarter... -> wrap the current selected tRoo..
41629         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41630             
41631             range = this.createRange(this.getSelection());
41632             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41633             wrappingNode.appendChild(range.extractContents());
41634             range.insertNode(wrappingNode);
41635
41636             return;
41637             
41638             
41639             
41640         }
41641         this.execCmd("formatblock",   tg);
41642         
41643     },
41644     
41645     insertText : function(txt)
41646     {
41647         
41648         
41649         var range = this.createRange();
41650         range.deleteContents();
41651                //alert(Sender.getAttribute('label'));
41652                
41653         range.insertNode(this.doc.createTextNode(txt));
41654     } ,
41655     
41656      
41657
41658     /**
41659      * Executes a Midas editor command on the editor document and performs necessary focus and
41660      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41661      * @param {String} cmd The Midas command
41662      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41663      */
41664     relayCmd : function(cmd, value){
41665         this.win.focus();
41666         this.execCmd(cmd, value);
41667         this.owner.fireEvent('editorevent', this);
41668         //this.updateToolbar();
41669         this.owner.deferFocus();
41670     },
41671
41672     /**
41673      * Executes a Midas editor command directly on the editor document.
41674      * For visual commands, you should use {@link #relayCmd} instead.
41675      * <b>This should only be called after the editor is initialized.</b>
41676      * @param {String} cmd The Midas command
41677      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41678      */
41679     execCmd : function(cmd, value){
41680         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41681         this.syncValue();
41682     },
41683  
41684  
41685    
41686     /**
41687      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41688      * to insert tRoo.
41689      * @param {String} text | dom node.. 
41690      */
41691     insertAtCursor : function(text)
41692     {
41693         
41694         
41695         
41696         if(!this.activated){
41697             return;
41698         }
41699         /*
41700         if(Roo.isIE){
41701             this.win.focus();
41702             var r = this.doc.selection.createRange();
41703             if(r){
41704                 r.collapse(true);
41705                 r.pasteHTML(text);
41706                 this.syncValue();
41707                 this.deferFocus();
41708             
41709             }
41710             return;
41711         }
41712         */
41713         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41714             this.win.focus();
41715             
41716             
41717             // from jquery ui (MIT licenced)
41718             var range, node;
41719             var win = this.win;
41720             
41721             if (win.getSelection && win.getSelection().getRangeAt) {
41722                 range = win.getSelection().getRangeAt(0);
41723                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41724                 range.insertNode(node);
41725             } else if (win.document.selection && win.document.selection.createRange) {
41726                 // no firefox support
41727                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41728                 win.document.selection.createRange().pasteHTML(txt);
41729             } else {
41730                 // no firefox support
41731                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41732                 this.execCmd('InsertHTML', txt);
41733             } 
41734             
41735             this.syncValue();
41736             
41737             this.deferFocus();
41738         }
41739     },
41740  // private
41741     mozKeyPress : function(e){
41742         if(e.ctrlKey){
41743             var c = e.getCharCode(), cmd;
41744           
41745             if(c > 0){
41746                 c = String.fromCharCode(c).toLowerCase();
41747                 switch(c){
41748                     case 'b':
41749                         cmd = 'bold';
41750                         break;
41751                     case 'i':
41752                         cmd = 'italic';
41753                         break;
41754                     
41755                     case 'u':
41756                         cmd = 'underline';
41757                         break;
41758                     
41759                     case 'v':
41760                         this.cleanUpPaste.defer(100, this);
41761                         return;
41762                         
41763                 }
41764                 if(cmd){
41765                     this.win.focus();
41766                     this.execCmd(cmd);
41767                     this.deferFocus();
41768                     e.preventDefault();
41769                 }
41770                 
41771             }
41772         }
41773     },
41774
41775     // private
41776     fixKeys : function(){ // load time branching for fastest keydown performance
41777         if(Roo.isIE){
41778             return function(e){
41779                 var k = e.getKey(), r;
41780                 if(k == e.TAB){
41781                     e.stopEvent();
41782                     r = this.doc.selection.createRange();
41783                     if(r){
41784                         r.collapse(true);
41785                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41786                         this.deferFocus();
41787                     }
41788                     return;
41789                 }
41790                 
41791                 if(k == e.ENTER){
41792                     r = this.doc.selection.createRange();
41793                     if(r){
41794                         var target = r.parentElement();
41795                         if(!target || target.tagName.toLowerCase() != 'li'){
41796                             e.stopEvent();
41797                             r.pasteHTML('<br />');
41798                             r.collapse(false);
41799                             r.select();
41800                         }
41801                     }
41802                 }
41803                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41804                     this.cleanUpPaste.defer(100, this);
41805                     return;
41806                 }
41807                 
41808                 
41809             };
41810         }else if(Roo.isOpera){
41811             return function(e){
41812                 var k = e.getKey();
41813                 if(k == e.TAB){
41814                     e.stopEvent();
41815                     this.win.focus();
41816                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41817                     this.deferFocus();
41818                 }
41819                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41820                     this.cleanUpPaste.defer(100, this);
41821                     return;
41822                 }
41823                 
41824             };
41825         }else if(Roo.isSafari){
41826             return function(e){
41827                 var k = e.getKey();
41828                 
41829                 if(k == e.TAB){
41830                     e.stopEvent();
41831                     this.execCmd('InsertText','\t');
41832                     this.deferFocus();
41833                     return;
41834                 }
41835                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41836                     this.cleanUpPaste.defer(100, this);
41837                     return;
41838                 }
41839                 
41840              };
41841         }
41842     }(),
41843     
41844     getAllAncestors: function()
41845     {
41846         var p = this.getSelectedNode();
41847         var a = [];
41848         if (!p) {
41849             a.push(p); // push blank onto stack..
41850             p = this.getParentElement();
41851         }
41852         
41853         
41854         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41855             a.push(p);
41856             p = p.parentNode;
41857         }
41858         a.push(this.doc.body);
41859         return a;
41860     },
41861     lastSel : false,
41862     lastSelNode : false,
41863     
41864     
41865     getSelection : function() 
41866     {
41867         this.assignDocWin();
41868         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41869     },
41870     
41871     getSelectedNode: function() 
41872     {
41873         // this may only work on Gecko!!!
41874         
41875         // should we cache this!!!!
41876         
41877         
41878         
41879          
41880         var range = this.createRange(this.getSelection()).cloneRange();
41881         
41882         if (Roo.isIE) {
41883             var parent = range.parentElement();
41884             while (true) {
41885                 var testRange = range.duplicate();
41886                 testRange.moveToElementText(parent);
41887                 if (testRange.inRange(range)) {
41888                     break;
41889                 }
41890                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41891                     break;
41892                 }
41893                 parent = parent.parentElement;
41894             }
41895             return parent;
41896         }
41897         
41898         // is ancestor a text element.
41899         var ac =  range.commonAncestorContainer;
41900         if (ac.nodeType == 3) {
41901             ac = ac.parentNode;
41902         }
41903         
41904         var ar = ac.childNodes;
41905          
41906         var nodes = [];
41907         var other_nodes = [];
41908         var has_other_nodes = false;
41909         for (var i=0;i<ar.length;i++) {
41910             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41911                 continue;
41912             }
41913             // fullly contained node.
41914             
41915             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41916                 nodes.push(ar[i]);
41917                 continue;
41918             }
41919             
41920             // probably selected..
41921             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41922                 other_nodes.push(ar[i]);
41923                 continue;
41924             }
41925             // outer..
41926             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41927                 continue;
41928             }
41929             
41930             
41931             has_other_nodes = true;
41932         }
41933         if (!nodes.length && other_nodes.length) {
41934             nodes= other_nodes;
41935         }
41936         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41937             return false;
41938         }
41939         
41940         return nodes[0];
41941     },
41942     createRange: function(sel)
41943     {
41944         // this has strange effects when using with 
41945         // top toolbar - not sure if it's a great idea.
41946         //this.editor.contentWindow.focus();
41947         if (typeof sel != "undefined") {
41948             try {
41949                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41950             } catch(e) {
41951                 return this.doc.createRange();
41952             }
41953         } else {
41954             return this.doc.createRange();
41955         }
41956     },
41957     getParentElement: function()
41958     {
41959         
41960         this.assignDocWin();
41961         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41962         
41963         var range = this.createRange(sel);
41964          
41965         try {
41966             var p = range.commonAncestorContainer;
41967             while (p.nodeType == 3) { // text node
41968                 p = p.parentNode;
41969             }
41970             return p;
41971         } catch (e) {
41972             return null;
41973         }
41974     
41975     },
41976     /***
41977      *
41978      * Range intersection.. the hard stuff...
41979      *  '-1' = before
41980      *  '0' = hits..
41981      *  '1' = after.
41982      *         [ -- selected range --- ]
41983      *   [fail]                        [fail]
41984      *
41985      *    basically..
41986      *      if end is before start or  hits it. fail.
41987      *      if start is after end or hits it fail.
41988      *
41989      *   if either hits (but other is outside. - then it's not 
41990      *   
41991      *    
41992      **/
41993     
41994     
41995     // @see http://www.thismuchiknow.co.uk/?p=64.
41996     rangeIntersectsNode : function(range, node)
41997     {
41998         var nodeRange = node.ownerDocument.createRange();
41999         try {
42000             nodeRange.selectNode(node);
42001         } catch (e) {
42002             nodeRange.selectNodeContents(node);
42003         }
42004     
42005         var rangeStartRange = range.cloneRange();
42006         rangeStartRange.collapse(true);
42007     
42008         var rangeEndRange = range.cloneRange();
42009         rangeEndRange.collapse(false);
42010     
42011         var nodeStartRange = nodeRange.cloneRange();
42012         nodeStartRange.collapse(true);
42013     
42014         var nodeEndRange = nodeRange.cloneRange();
42015         nodeEndRange.collapse(false);
42016     
42017         return rangeStartRange.compareBoundaryPoints(
42018                  Range.START_TO_START, nodeEndRange) == -1 &&
42019                rangeEndRange.compareBoundaryPoints(
42020                  Range.START_TO_START, nodeStartRange) == 1;
42021         
42022          
42023     },
42024     rangeCompareNode : function(range, node)
42025     {
42026         var nodeRange = node.ownerDocument.createRange();
42027         try {
42028             nodeRange.selectNode(node);
42029         } catch (e) {
42030             nodeRange.selectNodeContents(node);
42031         }
42032         
42033         
42034         range.collapse(true);
42035     
42036         nodeRange.collapse(true);
42037      
42038         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
42039         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
42040          
42041         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
42042         
42043         var nodeIsBefore   =  ss == 1;
42044         var nodeIsAfter    = ee == -1;
42045         
42046         if (nodeIsBefore && nodeIsAfter)
42047             return 0; // outer
42048         if (!nodeIsBefore && nodeIsAfter)
42049             return 1; //right trailed.
42050         
42051         if (nodeIsBefore && !nodeIsAfter)
42052             return 2;  // left trailed.
42053         // fully contined.
42054         return 3;
42055     },
42056
42057     // private? - in a new class?
42058     cleanUpPaste :  function()
42059     {
42060         // cleans up the whole document..
42061         Roo.log('cleanuppaste');
42062         
42063         this.cleanUpChildren(this.doc.body);
42064         var clean = this.cleanWordChars(this.doc.body.innerHTML);
42065         if (clean != this.doc.body.innerHTML) {
42066             this.doc.body.innerHTML = clean;
42067         }
42068         
42069     },
42070     
42071     cleanWordChars : function(input) {// change the chars to hex code
42072         var he = Roo.HtmlEditorCore;
42073         
42074         var output = input;
42075         Roo.each(he.swapCodes, function(sw) { 
42076             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42077             
42078             output = output.replace(swapper, sw[1]);
42079         });
42080         
42081         return output;
42082     },
42083     
42084     
42085     cleanUpChildren : function (n)
42086     {
42087         if (!n.childNodes.length) {
42088             return;
42089         }
42090         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42091            this.cleanUpChild(n.childNodes[i]);
42092         }
42093     },
42094     
42095     
42096         
42097     
42098     cleanUpChild : function (node)
42099     {
42100         var ed = this;
42101         //console.log(node);
42102         if (node.nodeName == "#text") {
42103             // clean up silly Windows -- stuff?
42104             return; 
42105         }
42106         if (node.nodeName == "#comment") {
42107             node.parentNode.removeChild(node);
42108             // clean up silly Windows -- stuff?
42109             return; 
42110         }
42111         var lcname = node.tagName.toLowerCase();
42112         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42113         // whitelist of tags..
42114         
42115         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42116             // remove node.
42117             node.parentNode.removeChild(node);
42118             return;
42119             
42120         }
42121         
42122         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42123         
42124         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42125         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42126         
42127         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42128         //    remove_keep_children = true;
42129         //}
42130         
42131         if (remove_keep_children) {
42132             this.cleanUpChildren(node);
42133             // inserts everything just before this node...
42134             while (node.childNodes.length) {
42135                 var cn = node.childNodes[0];
42136                 node.removeChild(cn);
42137                 node.parentNode.insertBefore(cn, node);
42138             }
42139             node.parentNode.removeChild(node);
42140             return;
42141         }
42142         
42143         if (!node.attributes || !node.attributes.length) {
42144             this.cleanUpChildren(node);
42145             return;
42146         }
42147         
42148         function cleanAttr(n,v)
42149         {
42150             
42151             if (v.match(/^\./) || v.match(/^\//)) {
42152                 return;
42153             }
42154             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42155                 return;
42156             }
42157             if (v.match(/^#/)) {
42158                 return;
42159             }
42160 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42161             node.removeAttribute(n);
42162             
42163         }
42164         
42165         var cwhite = this.cwhite;
42166         var cblack = this.cblack;
42167             
42168         function cleanStyle(n,v)
42169         {
42170             if (v.match(/expression/)) { //XSS?? should we even bother..
42171                 node.removeAttribute(n);
42172                 return;
42173             }
42174             
42175             var parts = v.split(/;/);
42176             var clean = [];
42177             
42178             Roo.each(parts, function(p) {
42179                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42180                 if (!p.length) {
42181                     return true;
42182                 }
42183                 var l = p.split(':').shift().replace(/\s+/g,'');
42184                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42185                 
42186                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42187 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42188                     //node.removeAttribute(n);
42189                     return true;
42190                 }
42191                 //Roo.log()
42192                 // only allow 'c whitelisted system attributes'
42193                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42194 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42195                     //node.removeAttribute(n);
42196                     return true;
42197                 }
42198                 
42199                 
42200                  
42201                 
42202                 clean.push(p);
42203                 return true;
42204             });
42205             if (clean.length) { 
42206                 node.setAttribute(n, clean.join(';'));
42207             } else {
42208                 node.removeAttribute(n);
42209             }
42210             
42211         }
42212         
42213         
42214         for (var i = node.attributes.length-1; i > -1 ; i--) {
42215             var a = node.attributes[i];
42216             //console.log(a);
42217             
42218             if (a.name.toLowerCase().substr(0,2)=='on')  {
42219                 node.removeAttribute(a.name);
42220                 continue;
42221             }
42222             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42223                 node.removeAttribute(a.name);
42224                 continue;
42225             }
42226             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42227                 cleanAttr(a.name,a.value); // fixme..
42228                 continue;
42229             }
42230             if (a.name == 'style') {
42231                 cleanStyle(a.name,a.value);
42232                 continue;
42233             }
42234             /// clean up MS crap..
42235             // tecnically this should be a list of valid class'es..
42236             
42237             
42238             if (a.name == 'class') {
42239                 if (a.value.match(/^Mso/)) {
42240                     node.className = '';
42241                 }
42242                 
42243                 if (a.value.match(/body/)) {
42244                     node.className = '';
42245                 }
42246                 continue;
42247             }
42248             
42249             // style cleanup!?
42250             // class cleanup?
42251             
42252         }
42253         
42254         
42255         this.cleanUpChildren(node);
42256         
42257         
42258     },
42259     
42260     /**
42261      * Clean up MS wordisms...
42262      */
42263     cleanWord : function(node)
42264     {
42265         
42266         
42267         if (!node) {
42268             this.cleanWord(this.doc.body);
42269             return;
42270         }
42271         if (node.nodeName == "#text") {
42272             // clean up silly Windows -- stuff?
42273             return; 
42274         }
42275         if (node.nodeName == "#comment") {
42276             node.parentNode.removeChild(node);
42277             // clean up silly Windows -- stuff?
42278             return; 
42279         }
42280         
42281         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42282             node.parentNode.removeChild(node);
42283             return;
42284         }
42285         
42286         // remove - but keep children..
42287         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42288             while (node.childNodes.length) {
42289                 var cn = node.childNodes[0];
42290                 node.removeChild(cn);
42291                 node.parentNode.insertBefore(cn, node);
42292             }
42293             node.parentNode.removeChild(node);
42294             this.iterateChildren(node, this.cleanWord);
42295             return;
42296         }
42297         // clean styles
42298         if (node.className.length) {
42299             
42300             var cn = node.className.split(/\W+/);
42301             var cna = [];
42302             Roo.each(cn, function(cls) {
42303                 if (cls.match(/Mso[a-zA-Z]+/)) {
42304                     return;
42305                 }
42306                 cna.push(cls);
42307             });
42308             node.className = cna.length ? cna.join(' ') : '';
42309             if (!cna.length) {
42310                 node.removeAttribute("class");
42311             }
42312         }
42313         
42314         if (node.hasAttribute("lang")) {
42315             node.removeAttribute("lang");
42316         }
42317         
42318         if (node.hasAttribute("style")) {
42319             
42320             var styles = node.getAttribute("style").split(";");
42321             var nstyle = [];
42322             Roo.each(styles, function(s) {
42323                 if (!s.match(/:/)) {
42324                     return;
42325                 }
42326                 var kv = s.split(":");
42327                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42328                     return;
42329                 }
42330                 // what ever is left... we allow.
42331                 nstyle.push(s);
42332             });
42333             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42334             if (!nstyle.length) {
42335                 node.removeAttribute('style');
42336             }
42337         }
42338         this.iterateChildren(node, this.cleanWord);
42339         
42340         
42341         
42342     },
42343     /**
42344      * iterateChildren of a Node, calling fn each time, using this as the scole..
42345      * @param {DomNode} node node to iterate children of.
42346      * @param {Function} fn method of this class to call on each item.
42347      */
42348     iterateChildren : function(node, fn)
42349     {
42350         if (!node.childNodes.length) {
42351                 return;
42352         }
42353         for (var i = node.childNodes.length-1; i > -1 ; i--) {
42354            fn.call(this, node.childNodes[i])
42355         }
42356     },
42357     
42358     
42359     /**
42360      * cleanTableWidths.
42361      *
42362      * Quite often pasting from word etc.. results in tables with column and widths.
42363      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
42364      *
42365      */
42366     cleanTableWidths : function(node)
42367     {
42368          
42369          
42370         if (!node) {
42371             this.cleanTableWidths(this.doc.body);
42372             return;
42373         }
42374         
42375         // ignore list...
42376         if (node.nodeName == "#text" || node.nodeName == "#comment") {
42377             return; 
42378         }
42379         Roo.log(node.tagName);
42380         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
42381             this.iterateChildren(node, this.cleanTableWidths);
42382             return;
42383         }
42384         if (node.hasAttribute('width')) {
42385             node.removeAttribute('width');
42386         }
42387         
42388          
42389         if (node.hasAttribute("style")) {
42390             // pretty basic...
42391             
42392             var styles = node.getAttribute("style").split(";");
42393             var nstyle = [];
42394             Roo.each(styles, function(s) {
42395                 if (!s.match(/:/)) {
42396                     return;
42397                 }
42398                 var kv = s.split(":");
42399                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
42400                     return;
42401                 }
42402                 // what ever is left... we allow.
42403                 nstyle.push(s);
42404             });
42405             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42406             if (!nstyle.length) {
42407                 node.removeAttribute('style');
42408             }
42409         }
42410         
42411         this.iterateChildren(node, this.cleanTableWidths);
42412         
42413         
42414     },
42415     
42416     
42417     
42418     
42419     domToHTML : function(currentElement, depth, nopadtext) {
42420         
42421         depth = depth || 0;
42422         nopadtext = nopadtext || false;
42423     
42424         if (!currentElement) {
42425             return this.domToHTML(this.doc.body);
42426         }
42427         
42428         //Roo.log(currentElement);
42429         var j;
42430         var allText = false;
42431         var nodeName = currentElement.nodeName;
42432         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42433         
42434         if  (nodeName == '#text') {
42435             
42436             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
42437         }
42438         
42439         
42440         var ret = '';
42441         if (nodeName != 'BODY') {
42442              
42443             var i = 0;
42444             // Prints the node tagName, such as <A>, <IMG>, etc
42445             if (tagName) {
42446                 var attr = [];
42447                 for(i = 0; i < currentElement.attributes.length;i++) {
42448                     // quoting?
42449                     var aname = currentElement.attributes.item(i).name;
42450                     if (!currentElement.attributes.item(i).value.length) {
42451                         continue;
42452                     }
42453                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42454                 }
42455                 
42456                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42457             } 
42458             else {
42459                 
42460                 // eack
42461             }
42462         } else {
42463             tagName = false;
42464         }
42465         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42466             return ret;
42467         }
42468         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42469             nopadtext = true;
42470         }
42471         
42472         
42473         // Traverse the tree
42474         i = 0;
42475         var currentElementChild = currentElement.childNodes.item(i);
42476         var allText = true;
42477         var innerHTML  = '';
42478         lastnode = '';
42479         while (currentElementChild) {
42480             // Formatting code (indent the tree so it looks nice on the screen)
42481             var nopad = nopadtext;
42482             if (lastnode == 'SPAN') {
42483                 nopad  = true;
42484             }
42485             // text
42486             if  (currentElementChild.nodeName == '#text') {
42487                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42488                 toadd = nopadtext ? toadd : toadd.trim();
42489                 if (!nopad && toadd.length > 80) {
42490                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42491                 }
42492                 innerHTML  += toadd;
42493                 
42494                 i++;
42495                 currentElementChild = currentElement.childNodes.item(i);
42496                 lastNode = '';
42497                 continue;
42498             }
42499             allText = false;
42500             
42501             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42502                 
42503             // Recursively traverse the tree structure of the child node
42504             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42505             lastnode = currentElementChild.nodeName;
42506             i++;
42507             currentElementChild=currentElement.childNodes.item(i);
42508         }
42509         
42510         ret += innerHTML;
42511         
42512         if (!allText) {
42513                 // The remaining code is mostly for formatting the tree
42514             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42515         }
42516         
42517         
42518         if (tagName) {
42519             ret+= "</"+tagName+">";
42520         }
42521         return ret;
42522         
42523     },
42524         
42525     applyBlacklists : function()
42526     {
42527         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42528         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42529         
42530         this.white = [];
42531         this.black = [];
42532         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42533             if (b.indexOf(tag) > -1) {
42534                 return;
42535             }
42536             this.white.push(tag);
42537             
42538         }, this);
42539         
42540         Roo.each(w, function(tag) {
42541             if (b.indexOf(tag) > -1) {
42542                 return;
42543             }
42544             if (this.white.indexOf(tag) > -1) {
42545                 return;
42546             }
42547             this.white.push(tag);
42548             
42549         }, this);
42550         
42551         
42552         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42553             if (w.indexOf(tag) > -1) {
42554                 return;
42555             }
42556             this.black.push(tag);
42557             
42558         }, this);
42559         
42560         Roo.each(b, function(tag) {
42561             if (w.indexOf(tag) > -1) {
42562                 return;
42563             }
42564             if (this.black.indexOf(tag) > -1) {
42565                 return;
42566             }
42567             this.black.push(tag);
42568             
42569         }, this);
42570         
42571         
42572         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42573         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42574         
42575         this.cwhite = [];
42576         this.cblack = [];
42577         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42578             if (b.indexOf(tag) > -1) {
42579                 return;
42580             }
42581             this.cwhite.push(tag);
42582             
42583         }, this);
42584         
42585         Roo.each(w, function(tag) {
42586             if (b.indexOf(tag) > -1) {
42587                 return;
42588             }
42589             if (this.cwhite.indexOf(tag) > -1) {
42590                 return;
42591             }
42592             this.cwhite.push(tag);
42593             
42594         }, this);
42595         
42596         
42597         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42598             if (w.indexOf(tag) > -1) {
42599                 return;
42600             }
42601             this.cblack.push(tag);
42602             
42603         }, this);
42604         
42605         Roo.each(b, function(tag) {
42606             if (w.indexOf(tag) > -1) {
42607                 return;
42608             }
42609             if (this.cblack.indexOf(tag) > -1) {
42610                 return;
42611             }
42612             this.cblack.push(tag);
42613             
42614         }, this);
42615     },
42616     
42617     setStylesheets : function(stylesheets)
42618     {
42619         if(typeof(stylesheets) == 'string'){
42620             Roo.get(this.iframe.contentDocument.head).createChild({
42621                 tag : 'link',
42622                 rel : 'stylesheet',
42623                 type : 'text/css',
42624                 href : stylesheets
42625             });
42626             
42627             return;
42628         }
42629         var _this = this;
42630      
42631         Roo.each(stylesheets, function(s) {
42632             if(!s.length){
42633                 return;
42634             }
42635             
42636             Roo.get(_this.iframe.contentDocument.head).createChild({
42637                 tag : 'link',
42638                 rel : 'stylesheet',
42639                 type : 'text/css',
42640                 href : s
42641             });
42642         });
42643
42644         
42645     },
42646     
42647     removeStylesheets : function()
42648     {
42649         var _this = this;
42650         
42651         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
42652             s.remove();
42653         });
42654     }
42655     
42656     // hide stuff that is not compatible
42657     /**
42658      * @event blur
42659      * @hide
42660      */
42661     /**
42662      * @event change
42663      * @hide
42664      */
42665     /**
42666      * @event focus
42667      * @hide
42668      */
42669     /**
42670      * @event specialkey
42671      * @hide
42672      */
42673     /**
42674      * @cfg {String} fieldClass @hide
42675      */
42676     /**
42677      * @cfg {String} focusClass @hide
42678      */
42679     /**
42680      * @cfg {String} autoCreate @hide
42681      */
42682     /**
42683      * @cfg {String} inputType @hide
42684      */
42685     /**
42686      * @cfg {String} invalidClass @hide
42687      */
42688     /**
42689      * @cfg {String} invalidText @hide
42690      */
42691     /**
42692      * @cfg {String} msgFx @hide
42693      */
42694     /**
42695      * @cfg {String} validateOnBlur @hide
42696      */
42697 });
42698
42699 Roo.HtmlEditorCore.white = [
42700         'area', 'br', 'img', 'input', 'hr', 'wbr',
42701         
42702        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42703        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42704        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42705        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42706        'table',   'ul',         'xmp', 
42707        
42708        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42709       'thead',   'tr', 
42710      
42711       'dir', 'menu', 'ol', 'ul', 'dl',
42712        
42713       'embed',  'object'
42714 ];
42715
42716
42717 Roo.HtmlEditorCore.black = [
42718     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42719         'applet', // 
42720         'base',   'basefont', 'bgsound', 'blink',  'body', 
42721         'frame',  'frameset', 'head',    'html',   'ilayer', 
42722         'iframe', 'layer',  'link',     'meta',    'object',   
42723         'script', 'style' ,'title',  'xml' // clean later..
42724 ];
42725 Roo.HtmlEditorCore.clean = [
42726     'script', 'style', 'title', 'xml'
42727 ];
42728 Roo.HtmlEditorCore.remove = [
42729     'font'
42730 ];
42731 // attributes..
42732
42733 Roo.HtmlEditorCore.ablack = [
42734     'on'
42735 ];
42736     
42737 Roo.HtmlEditorCore.aclean = [ 
42738     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42739 ];
42740
42741 // protocols..
42742 Roo.HtmlEditorCore.pwhite= [
42743         'http',  'https',  'mailto'
42744 ];
42745
42746 // white listed style attributes.
42747 Roo.HtmlEditorCore.cwhite= [
42748       //  'text-align', /// default is to allow most things..
42749       
42750          
42751 //        'font-size'//??
42752 ];
42753
42754 // black listed style attributes.
42755 Roo.HtmlEditorCore.cblack= [
42756       //  'font-size' -- this can be set by the project 
42757 ];
42758
42759
42760 Roo.HtmlEditorCore.swapCodes   =[ 
42761     [    8211, "--" ], 
42762     [    8212, "--" ], 
42763     [    8216,  "'" ],  
42764     [    8217, "'" ],  
42765     [    8220, '"' ],  
42766     [    8221, '"' ],  
42767     [    8226, "*" ],  
42768     [    8230, "..." ]
42769 ]; 
42770
42771     //<script type="text/javascript">
42772
42773 /*
42774  * Ext JS Library 1.1.1
42775  * Copyright(c) 2006-2007, Ext JS, LLC.
42776  * Licence LGPL
42777  * 
42778  */
42779  
42780  
42781 Roo.form.HtmlEditor = function(config){
42782     
42783     
42784     
42785     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42786     
42787     if (!this.toolbars) {
42788         this.toolbars = [];
42789     }
42790     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42791     
42792     
42793 };
42794
42795 /**
42796  * @class Roo.form.HtmlEditor
42797  * @extends Roo.form.Field
42798  * Provides a lightweight HTML Editor component.
42799  *
42800  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42801  * 
42802  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42803  * supported by this editor.</b><br/><br/>
42804  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42805  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42806  */
42807 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42808     /**
42809      * @cfg {Boolean} clearUp
42810      */
42811     clearUp : true,
42812       /**
42813      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42814      */
42815     toolbars : false,
42816    
42817      /**
42818      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42819      *                        Roo.resizable.
42820      */
42821     resizable : false,
42822      /**
42823      * @cfg {Number} height (in pixels)
42824      */   
42825     height: 300,
42826    /**
42827      * @cfg {Number} width (in pixels)
42828      */   
42829     width: 500,
42830     
42831     /**
42832      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42833      * 
42834      */
42835     stylesheets: false,
42836     
42837     
42838      /**
42839      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42840      * 
42841      */
42842     cblack: false,
42843     /**
42844      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42845      * 
42846      */
42847     cwhite: false,
42848     
42849      /**
42850      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42851      * 
42852      */
42853     black: false,
42854     /**
42855      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42856      * 
42857      */
42858     white: false,
42859     
42860     // id of frame..
42861     frameId: false,
42862     
42863     // private properties
42864     validationEvent : false,
42865     deferHeight: true,
42866     initialized : false,
42867     activated : false,
42868     
42869     onFocus : Roo.emptyFn,
42870     iframePad:3,
42871     hideMode:'offsets',
42872     
42873     actionMode : 'container', // defaults to hiding it...
42874     
42875     defaultAutoCreate : { // modified by initCompnoent..
42876         tag: "textarea",
42877         style:"width:500px;height:300px;",
42878         autocomplete: "new-password"
42879     },
42880
42881     // private
42882     initComponent : function(){
42883         this.addEvents({
42884             /**
42885              * @event initialize
42886              * Fires when the editor is fully initialized (including the iframe)
42887              * @param {HtmlEditor} this
42888              */
42889             initialize: true,
42890             /**
42891              * @event activate
42892              * Fires when the editor is first receives the focus. Any insertion must wait
42893              * until after this event.
42894              * @param {HtmlEditor} this
42895              */
42896             activate: true,
42897              /**
42898              * @event beforesync
42899              * Fires before the textarea is updated with content from the editor iframe. Return false
42900              * to cancel the sync.
42901              * @param {HtmlEditor} this
42902              * @param {String} html
42903              */
42904             beforesync: true,
42905              /**
42906              * @event beforepush
42907              * Fires before the iframe editor is updated with content from the textarea. Return false
42908              * to cancel the push.
42909              * @param {HtmlEditor} this
42910              * @param {String} html
42911              */
42912             beforepush: true,
42913              /**
42914              * @event sync
42915              * Fires when the textarea is updated with content from the editor iframe.
42916              * @param {HtmlEditor} this
42917              * @param {String} html
42918              */
42919             sync: true,
42920              /**
42921              * @event push
42922              * Fires when the iframe editor is updated with content from the textarea.
42923              * @param {HtmlEditor} this
42924              * @param {String} html
42925              */
42926             push: true,
42927              /**
42928              * @event editmodechange
42929              * Fires when the editor switches edit modes
42930              * @param {HtmlEditor} this
42931              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42932              */
42933             editmodechange: true,
42934             /**
42935              * @event editorevent
42936              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42937              * @param {HtmlEditor} this
42938              */
42939             editorevent: true,
42940             /**
42941              * @event firstfocus
42942              * Fires when on first focus - needed by toolbars..
42943              * @param {HtmlEditor} this
42944              */
42945             firstfocus: true,
42946             /**
42947              * @event autosave
42948              * Auto save the htmlEditor value as a file into Events
42949              * @param {HtmlEditor} this
42950              */
42951             autosave: true,
42952             /**
42953              * @event savedpreview
42954              * preview the saved version of htmlEditor
42955              * @param {HtmlEditor} this
42956              */
42957             savedpreview: true,
42958             
42959             /**
42960             * @event stylesheetsclick
42961             * Fires when press the Sytlesheets button
42962             * @param {Roo.HtmlEditorCore} this
42963             */
42964             stylesheetsclick: true
42965         });
42966         this.defaultAutoCreate =  {
42967             tag: "textarea",
42968             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42969             autocomplete: "new-password"
42970         };
42971     },
42972
42973     /**
42974      * Protected method that will not generally be called directly. It
42975      * is called when the editor creates its toolbar. Override this method if you need to
42976      * add custom toolbar buttons.
42977      * @param {HtmlEditor} editor
42978      */
42979     createToolbar : function(editor){
42980         Roo.log("create toolbars");
42981         if (!editor.toolbars || !editor.toolbars.length) {
42982             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42983         }
42984         
42985         for (var i =0 ; i < editor.toolbars.length;i++) {
42986             editor.toolbars[i] = Roo.factory(
42987                     typeof(editor.toolbars[i]) == 'string' ?
42988                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42989                 Roo.form.HtmlEditor);
42990             editor.toolbars[i].init(editor);
42991         }
42992          
42993         
42994     },
42995
42996      
42997     // private
42998     onRender : function(ct, position)
42999     {
43000         var _t = this;
43001         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
43002         
43003         this.wrap = this.el.wrap({
43004             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
43005         });
43006         
43007         this.editorcore.onRender(ct, position);
43008          
43009         if (this.resizable) {
43010             this.resizeEl = new Roo.Resizable(this.wrap, {
43011                 pinned : true,
43012                 wrap: true,
43013                 dynamic : true,
43014                 minHeight : this.height,
43015                 height: this.height,
43016                 handles : this.resizable,
43017                 width: this.width,
43018                 listeners : {
43019                     resize : function(r, w, h) {
43020                         _t.onResize(w,h); // -something
43021                     }
43022                 }
43023             });
43024             
43025         }
43026         this.createToolbar(this);
43027        
43028         
43029         if(!this.width){
43030             this.setSize(this.wrap.getSize());
43031         }
43032         if (this.resizeEl) {
43033             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
43034             // should trigger onReize..
43035         }
43036         
43037         this.keyNav = new Roo.KeyNav(this.el, {
43038             
43039             "tab" : function(e){
43040                 e.preventDefault();
43041                 
43042                 var value = this.getValue();
43043                 
43044                 var start = this.el.dom.selectionStart;
43045                 var end = this.el.dom.selectionEnd;
43046                 
43047                 if(!e.shiftKey){
43048                     
43049                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
43050                     this.el.dom.setSelectionRange(end + 1, end + 1);
43051                     return;
43052                 }
43053                 
43054                 var f = value.substring(0, start).split("\t");
43055                 
43056                 if(f.pop().length != 0){
43057                     return;
43058                 }
43059                 
43060                 this.setValue(f.join("\t") + value.substring(end));
43061                 this.el.dom.setSelectionRange(start - 1, start - 1);
43062                 
43063             },
43064             
43065             "home" : function(e){
43066                 e.preventDefault();
43067                 
43068                 var curr = this.el.dom.selectionStart;
43069                 var lines = this.getValue().split("\n");
43070                 
43071                 if(!lines.length){
43072                     return;
43073                 }
43074                 
43075                 if(e.ctrlKey){
43076                     this.el.dom.setSelectionRange(0, 0);
43077                     return;
43078                 }
43079                 
43080                 var pos = 0;
43081                 
43082                 for (var i = 0; i < lines.length;i++) {
43083                     pos += lines[i].length;
43084                     
43085                     if(i != 0){
43086                         pos += 1;
43087                     }
43088                     
43089                     if(pos < curr){
43090                         continue;
43091                     }
43092                     
43093                     pos -= lines[i].length;
43094                     
43095                     break;
43096                 }
43097                 
43098                 if(!e.shiftKey){
43099                     this.el.dom.setSelectionRange(pos, pos);
43100                     return;
43101                 }
43102                 
43103                 this.el.dom.selectionStart = pos;
43104                 this.el.dom.selectionEnd = curr;
43105             },
43106             
43107             "end" : function(e){
43108                 e.preventDefault();
43109                 
43110                 var curr = this.el.dom.selectionStart;
43111                 var lines = this.getValue().split("\n");
43112                 
43113                 if(!lines.length){
43114                     return;
43115                 }
43116                 
43117                 if(e.ctrlKey){
43118                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
43119                     return;
43120                 }
43121                 
43122                 var pos = 0;
43123                 
43124                 for (var i = 0; i < lines.length;i++) {
43125                     
43126                     pos += lines[i].length;
43127                     
43128                     if(i != 0){
43129                         pos += 1;
43130                     }
43131                     
43132                     if(pos < curr){
43133                         continue;
43134                     }
43135                     
43136                     break;
43137                 }
43138                 
43139                 if(!e.shiftKey){
43140                     this.el.dom.setSelectionRange(pos, pos);
43141                     return;
43142                 }
43143                 
43144                 this.el.dom.selectionStart = curr;
43145                 this.el.dom.selectionEnd = pos;
43146             },
43147
43148             scope : this,
43149
43150             doRelay : function(foo, bar, hname){
43151                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43152             },
43153
43154             forceKeyDown: true
43155         });
43156         
43157 //        if(this.autosave && this.w){
43158 //            this.autoSaveFn = setInterval(this.autosave, 1000);
43159 //        }
43160     },
43161
43162     // private
43163     onResize : function(w, h)
43164     {
43165         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
43166         var ew = false;
43167         var eh = false;
43168         
43169         if(this.el ){
43170             if(typeof w == 'number'){
43171                 var aw = w - this.wrap.getFrameWidth('lr');
43172                 this.el.setWidth(this.adjustWidth('textarea', aw));
43173                 ew = aw;
43174             }
43175             if(typeof h == 'number'){
43176                 var tbh = 0;
43177                 for (var i =0; i < this.toolbars.length;i++) {
43178                     // fixme - ask toolbars for heights?
43179                     tbh += this.toolbars[i].tb.el.getHeight();
43180                     if (this.toolbars[i].footer) {
43181                         tbh += this.toolbars[i].footer.el.getHeight();
43182                     }
43183                 }
43184                 
43185                 
43186                 
43187                 
43188                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
43189                 ah -= 5; // knock a few pixes off for look..
43190 //                Roo.log(ah);
43191                 this.el.setHeight(this.adjustWidth('textarea', ah));
43192                 var eh = ah;
43193             }
43194         }
43195         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
43196         this.editorcore.onResize(ew,eh);
43197         
43198     },
43199
43200     /**
43201      * Toggles the editor between standard and source edit mode.
43202      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43203      */
43204     toggleSourceEdit : function(sourceEditMode)
43205     {
43206         this.editorcore.toggleSourceEdit(sourceEditMode);
43207         
43208         if(this.editorcore.sourceEditMode){
43209             Roo.log('editor - showing textarea');
43210             
43211 //            Roo.log('in');
43212 //            Roo.log(this.syncValue());
43213             this.editorcore.syncValue();
43214             this.el.removeClass('x-hidden');
43215             this.el.dom.removeAttribute('tabIndex');
43216             this.el.focus();
43217             
43218             for (var i = 0; i < this.toolbars.length; i++) {
43219                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43220                     this.toolbars[i].tb.hide();
43221                     this.toolbars[i].footer.hide();
43222                 }
43223             }
43224             
43225         }else{
43226             Roo.log('editor - hiding textarea');
43227 //            Roo.log('out')
43228 //            Roo.log(this.pushValue()); 
43229             this.editorcore.pushValue();
43230             
43231             this.el.addClass('x-hidden');
43232             this.el.dom.setAttribute('tabIndex', -1);
43233             
43234             for (var i = 0; i < this.toolbars.length; i++) {
43235                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
43236                     this.toolbars[i].tb.show();
43237                     this.toolbars[i].footer.show();
43238                 }
43239             }
43240             
43241             //this.deferFocus();
43242         }
43243         
43244         this.setSize(this.wrap.getSize());
43245         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
43246         
43247         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
43248     },
43249  
43250     // private (for BoxComponent)
43251     adjustSize : Roo.BoxComponent.prototype.adjustSize,
43252
43253     // private (for BoxComponent)
43254     getResizeEl : function(){
43255         return this.wrap;
43256     },
43257
43258     // private (for BoxComponent)
43259     getPositionEl : function(){
43260         return this.wrap;
43261     },
43262
43263     // private
43264     initEvents : function(){
43265         this.originalValue = this.getValue();
43266     },
43267
43268     /**
43269      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43270      * @method
43271      */
43272     markInvalid : Roo.emptyFn,
43273     /**
43274      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
43275      * @method
43276      */
43277     clearInvalid : Roo.emptyFn,
43278
43279     setValue : function(v){
43280         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
43281         this.editorcore.pushValue();
43282     },
43283
43284      
43285     // private
43286     deferFocus : function(){
43287         this.focus.defer(10, this);
43288     },
43289
43290     // doc'ed in Field
43291     focus : function(){
43292         this.editorcore.focus();
43293         
43294     },
43295       
43296
43297     // private
43298     onDestroy : function(){
43299         
43300         
43301         
43302         if(this.rendered){
43303             
43304             for (var i =0; i < this.toolbars.length;i++) {
43305                 // fixme - ask toolbars for heights?
43306                 this.toolbars[i].onDestroy();
43307             }
43308             
43309             this.wrap.dom.innerHTML = '';
43310             this.wrap.remove();
43311         }
43312     },
43313
43314     // private
43315     onFirstFocus : function(){
43316         //Roo.log("onFirstFocus");
43317         this.editorcore.onFirstFocus();
43318          for (var i =0; i < this.toolbars.length;i++) {
43319             this.toolbars[i].onFirstFocus();
43320         }
43321         
43322     },
43323     
43324     // private
43325     syncValue : function()
43326     {
43327         this.editorcore.syncValue();
43328     },
43329     
43330     pushValue : function()
43331     {
43332         this.editorcore.pushValue();
43333     },
43334     
43335     setStylesheets : function(stylesheets)
43336     {
43337         this.editorcore.setStylesheets(stylesheets);
43338     },
43339     
43340     removeStylesheets : function()
43341     {
43342         this.editorcore.removeStylesheets();
43343     }
43344      
43345     
43346     // hide stuff that is not compatible
43347     /**
43348      * @event blur
43349      * @hide
43350      */
43351     /**
43352      * @event change
43353      * @hide
43354      */
43355     /**
43356      * @event focus
43357      * @hide
43358      */
43359     /**
43360      * @event specialkey
43361      * @hide
43362      */
43363     /**
43364      * @cfg {String} fieldClass @hide
43365      */
43366     /**
43367      * @cfg {String} focusClass @hide
43368      */
43369     /**
43370      * @cfg {String} autoCreate @hide
43371      */
43372     /**
43373      * @cfg {String} inputType @hide
43374      */
43375     /**
43376      * @cfg {String} invalidClass @hide
43377      */
43378     /**
43379      * @cfg {String} invalidText @hide
43380      */
43381     /**
43382      * @cfg {String} msgFx @hide
43383      */
43384     /**
43385      * @cfg {String} validateOnBlur @hide
43386      */
43387 });
43388  
43389     // <script type="text/javascript">
43390 /*
43391  * Based on
43392  * Ext JS Library 1.1.1
43393  * Copyright(c) 2006-2007, Ext JS, LLC.
43394  *  
43395  
43396  */
43397
43398 /**
43399  * @class Roo.form.HtmlEditorToolbar1
43400  * Basic Toolbar
43401  * 
43402  * Usage:
43403  *
43404  new Roo.form.HtmlEditor({
43405     ....
43406     toolbars : [
43407         new Roo.form.HtmlEditorToolbar1({
43408             disable : { fonts: 1 , format: 1, ..., ... , ...],
43409             btns : [ .... ]
43410         })
43411     }
43412      
43413  * 
43414  * @cfg {Object} disable List of elements to disable..
43415  * @cfg {Array} btns List of additional buttons.
43416  * 
43417  * 
43418  * NEEDS Extra CSS? 
43419  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43420  */
43421  
43422 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43423 {
43424     
43425     Roo.apply(this, config);
43426     
43427     // default disabled, based on 'good practice'..
43428     this.disable = this.disable || {};
43429     Roo.applyIf(this.disable, {
43430         fontSize : true,
43431         colors : true,
43432         specialElements : true
43433     });
43434     
43435     
43436     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43437     // dont call parent... till later.
43438 }
43439
43440 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43441     
43442     tb: false,
43443     
43444     rendered: false,
43445     
43446     editor : false,
43447     editorcore : false,
43448     /**
43449      * @cfg {Object} disable  List of toolbar elements to disable
43450          
43451      */
43452     disable : false,
43453     
43454     
43455      /**
43456      * @cfg {String} createLinkText The default text for the create link prompt
43457      */
43458     createLinkText : 'Please enter the URL for the link:',
43459     /**
43460      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43461      */
43462     defaultLinkValue : 'http:/'+'/',
43463    
43464     
43465       /**
43466      * @cfg {Array} fontFamilies An array of available font families
43467      */
43468     fontFamilies : [
43469         'Arial',
43470         'Courier New',
43471         'Tahoma',
43472         'Times New Roman',
43473         'Verdana'
43474     ],
43475     
43476     specialChars : [
43477            "&#169;",
43478           "&#174;",     
43479           "&#8482;",    
43480           "&#163;" ,    
43481          // "&#8212;",    
43482           "&#8230;",    
43483           "&#247;" ,    
43484         //  "&#225;" ,     ?? a acute?
43485            "&#8364;"    , //Euro
43486        //   "&#8220;"    ,
43487         //  "&#8221;"    ,
43488         //  "&#8226;"    ,
43489           "&#176;"  //   , // degrees
43490
43491          // "&#233;"     , // e ecute
43492          // "&#250;"     , // u ecute?
43493     ],
43494     
43495     specialElements : [
43496         {
43497             text: "Insert Table",
43498             xtype: 'MenuItem',
43499             xns : Roo.Menu,
43500             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43501                 
43502         },
43503         {    
43504             text: "Insert Image",
43505             xtype: 'MenuItem',
43506             xns : Roo.Menu,
43507             ihtml : '<img src="about:blank"/>'
43508             
43509         }
43510         
43511          
43512     ],
43513     
43514     
43515     inputElements : [ 
43516             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43517             "input:submit", "input:button", "select", "textarea", "label" ],
43518     formats : [
43519         ["p"] ,  
43520         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43521         ["pre"],[ "code"], 
43522         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43523         ['div'],['span']
43524     ],
43525     
43526     cleanStyles : [
43527         "font-size"
43528     ],
43529      /**
43530      * @cfg {String} defaultFont default font to use.
43531      */
43532     defaultFont: 'tahoma',
43533    
43534     fontSelect : false,
43535     
43536     
43537     formatCombo : false,
43538     
43539     init : function(editor)
43540     {
43541         this.editor = editor;
43542         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43543         var editorcore = this.editorcore;
43544         
43545         var _t = this;
43546         
43547         var fid = editorcore.frameId;
43548         var etb = this;
43549         function btn(id, toggle, handler){
43550             var xid = fid + '-'+ id ;
43551             return {
43552                 id : xid,
43553                 cmd : id,
43554                 cls : 'x-btn-icon x-edit-'+id,
43555                 enableToggle:toggle !== false,
43556                 scope: _t, // was editor...
43557                 handler:handler||_t.relayBtnCmd,
43558                 clickEvent:'mousedown',
43559                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43560                 tabIndex:-1
43561             };
43562         }
43563         
43564         
43565         
43566         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43567         this.tb = tb;
43568          // stop form submits
43569         tb.el.on('click', function(e){
43570             e.preventDefault(); // what does this do?
43571         });
43572
43573         if(!this.disable.font) { // && !Roo.isSafari){
43574             /* why no safari for fonts 
43575             editor.fontSelect = tb.el.createChild({
43576                 tag:'select',
43577                 tabIndex: -1,
43578                 cls:'x-font-select',
43579                 html: this.createFontOptions()
43580             });
43581             
43582             editor.fontSelect.on('change', function(){
43583                 var font = editor.fontSelect.dom.value;
43584                 editor.relayCmd('fontname', font);
43585                 editor.deferFocus();
43586             }, editor);
43587             
43588             tb.add(
43589                 editor.fontSelect.dom,
43590                 '-'
43591             );
43592             */
43593             
43594         };
43595         if(!this.disable.formats){
43596             this.formatCombo = new Roo.form.ComboBox({
43597                 store: new Roo.data.SimpleStore({
43598                     id : 'tag',
43599                     fields: ['tag'],
43600                     data : this.formats // from states.js
43601                 }),
43602                 blockFocus : true,
43603                 name : '',
43604                 //autoCreate : {tag: "div",  size: "20"},
43605                 displayField:'tag',
43606                 typeAhead: false,
43607                 mode: 'local',
43608                 editable : false,
43609                 triggerAction: 'all',
43610                 emptyText:'Add tag',
43611                 selectOnFocus:true,
43612                 width:135,
43613                 listeners : {
43614                     'select': function(c, r, i) {
43615                         editorcore.insertTag(r.get('tag'));
43616                         editor.focus();
43617                     }
43618                 }
43619
43620             });
43621             tb.addField(this.formatCombo);
43622             
43623         }
43624         
43625         if(!this.disable.format){
43626             tb.add(
43627                 btn('bold'),
43628                 btn('italic'),
43629                 btn('underline')
43630             );
43631         };
43632         if(!this.disable.fontSize){
43633             tb.add(
43634                 '-',
43635                 
43636                 
43637                 btn('increasefontsize', false, editorcore.adjustFont),
43638                 btn('decreasefontsize', false, editorcore.adjustFont)
43639             );
43640         };
43641         
43642         
43643         if(!this.disable.colors){
43644             tb.add(
43645                 '-', {
43646                     id:editorcore.frameId +'-forecolor',
43647                     cls:'x-btn-icon x-edit-forecolor',
43648                     clickEvent:'mousedown',
43649                     tooltip: this.buttonTips['forecolor'] || undefined,
43650                     tabIndex:-1,
43651                     menu : new Roo.menu.ColorMenu({
43652                         allowReselect: true,
43653                         focus: Roo.emptyFn,
43654                         value:'000000',
43655                         plain:true,
43656                         selectHandler: function(cp, color){
43657                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43658                             editor.deferFocus();
43659                         },
43660                         scope: editorcore,
43661                         clickEvent:'mousedown'
43662                     })
43663                 }, {
43664                     id:editorcore.frameId +'backcolor',
43665                     cls:'x-btn-icon x-edit-backcolor',
43666                     clickEvent:'mousedown',
43667                     tooltip: this.buttonTips['backcolor'] || undefined,
43668                     tabIndex:-1,
43669                     menu : new Roo.menu.ColorMenu({
43670                         focus: Roo.emptyFn,
43671                         value:'FFFFFF',
43672                         plain:true,
43673                         allowReselect: true,
43674                         selectHandler: function(cp, color){
43675                             if(Roo.isGecko){
43676                                 editorcore.execCmd('useCSS', false);
43677                                 editorcore.execCmd('hilitecolor', color);
43678                                 editorcore.execCmd('useCSS', true);
43679                                 editor.deferFocus();
43680                             }else{
43681                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43682                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43683                                 editor.deferFocus();
43684                             }
43685                         },
43686                         scope:editorcore,
43687                         clickEvent:'mousedown'
43688                     })
43689                 }
43690             );
43691         };
43692         // now add all the items...
43693         
43694
43695         if(!this.disable.alignments){
43696             tb.add(
43697                 '-',
43698                 btn('justifyleft'),
43699                 btn('justifycenter'),
43700                 btn('justifyright')
43701             );
43702         };
43703
43704         //if(!Roo.isSafari){
43705             if(!this.disable.links){
43706                 tb.add(
43707                     '-',
43708                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43709                 );
43710             };
43711
43712             if(!this.disable.lists){
43713                 tb.add(
43714                     '-',
43715                     btn('insertorderedlist'),
43716                     btn('insertunorderedlist')
43717                 );
43718             }
43719             if(!this.disable.sourceEdit){
43720                 tb.add(
43721                     '-',
43722                     btn('sourceedit', true, function(btn){
43723                         this.toggleSourceEdit(btn.pressed);
43724                     })
43725                 );
43726             }
43727         //}
43728         
43729         var smenu = { };
43730         // special menu.. - needs to be tidied up..
43731         if (!this.disable.special) {
43732             smenu = {
43733                 text: "&#169;",
43734                 cls: 'x-edit-none',
43735                 
43736                 menu : {
43737                     items : []
43738                 }
43739             };
43740             for (var i =0; i < this.specialChars.length; i++) {
43741                 smenu.menu.items.push({
43742                     
43743                     html: this.specialChars[i],
43744                     handler: function(a,b) {
43745                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43746                         //editor.insertAtCursor(a.html);
43747                         
43748                     },
43749                     tabIndex:-1
43750                 });
43751             }
43752             
43753             
43754             tb.add(smenu);
43755             
43756             
43757         }
43758         
43759         var cmenu = { };
43760         if (!this.disable.cleanStyles) {
43761             cmenu = {
43762                 cls: 'x-btn-icon x-btn-clear',
43763                 
43764                 menu : {
43765                     items : []
43766                 }
43767             };
43768             for (var i =0; i < this.cleanStyles.length; i++) {
43769                 cmenu.menu.items.push({
43770                     actiontype : this.cleanStyles[i],
43771                     html: 'Remove ' + this.cleanStyles[i],
43772                     handler: function(a,b) {
43773 //                        Roo.log(a);
43774 //                        Roo.log(b);
43775                         var c = Roo.get(editorcore.doc.body);
43776                         c.select('[style]').each(function(s) {
43777                             s.dom.style.removeProperty(a.actiontype);
43778                         });
43779                         editorcore.syncValue();
43780                     },
43781                     tabIndex:-1
43782                 });
43783             }
43784              cmenu.menu.items.push({
43785                 actiontype : 'tablewidths',
43786                 html: 'Remove Table Widths',
43787                 handler: function(a,b) {
43788                     editorcore.cleanTableWidths();
43789                     editorcore.syncValue();
43790                 },
43791                 tabIndex:-1
43792             });
43793             cmenu.menu.items.push({
43794                 actiontype : 'word',
43795                 html: 'Remove MS Word Formating',
43796                 handler: function(a,b) {
43797                     editorcore.cleanWord();
43798                     editorcore.syncValue();
43799                 },
43800                 tabIndex:-1
43801             });
43802             
43803             cmenu.menu.items.push({
43804                 actiontype : 'all',
43805                 html: 'Remove All Styles',
43806                 handler: function(a,b) {
43807                     
43808                     var c = Roo.get(editorcore.doc.body);
43809                     c.select('[style]').each(function(s) {
43810                         s.dom.removeAttribute('style');
43811                     });
43812                     editorcore.syncValue();
43813                 },
43814                 tabIndex:-1
43815             });
43816             
43817             cmenu.menu.items.push({
43818                 actiontype : 'all',
43819                 html: 'Remove All CSS Classes',
43820                 handler: function(a,b) {
43821                     
43822                     var c = Roo.get(editorcore.doc.body);
43823                     c.select('[class]').each(function(s) {
43824                         s.dom.className = '';
43825                     });
43826                     editorcore.syncValue();
43827                 },
43828                 tabIndex:-1
43829             });
43830             
43831              cmenu.menu.items.push({
43832                 actiontype : 'tidy',
43833                 html: 'Tidy HTML Source',
43834                 handler: function(a,b) {
43835                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43836                     editorcore.syncValue();
43837                 },
43838                 tabIndex:-1
43839             });
43840             
43841             
43842             tb.add(cmenu);
43843         }
43844          
43845         if (!this.disable.specialElements) {
43846             var semenu = {
43847                 text: "Other;",
43848                 cls: 'x-edit-none',
43849                 menu : {
43850                     items : []
43851                 }
43852             };
43853             for (var i =0; i < this.specialElements.length; i++) {
43854                 semenu.menu.items.push(
43855                     Roo.apply({ 
43856                         handler: function(a,b) {
43857                             editor.insertAtCursor(this.ihtml);
43858                         }
43859                     }, this.specialElements[i])
43860                 );
43861                     
43862             }
43863             
43864             tb.add(semenu);
43865             
43866             
43867         }
43868          
43869         
43870         if (this.btns) {
43871             for(var i =0; i< this.btns.length;i++) {
43872                 var b = Roo.factory(this.btns[i],Roo.form);
43873                 b.cls =  'x-edit-none';
43874                 
43875                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
43876                     b.cls += ' x-init-enable';
43877                 }
43878                 
43879                 b.scope = editorcore;
43880                 tb.add(b);
43881             }
43882         
43883         }
43884         
43885         
43886         
43887         // disable everything...
43888         
43889         this.tb.items.each(function(item){
43890             
43891            if(
43892                 item.id != editorcore.frameId+ '-sourceedit' && 
43893                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
43894             ){
43895                 
43896                 item.disable();
43897             }
43898         });
43899         this.rendered = true;
43900         
43901         // the all the btns;
43902         editor.on('editorevent', this.updateToolbar, this);
43903         // other toolbars need to implement this..
43904         //editor.on('editmodechange', this.updateToolbar, this);
43905     },
43906     
43907     
43908     relayBtnCmd : function(btn) {
43909         this.editorcore.relayCmd(btn.cmd);
43910     },
43911     // private used internally
43912     createLink : function(){
43913         Roo.log("create link?");
43914         var url = prompt(this.createLinkText, this.defaultLinkValue);
43915         if(url && url != 'http:/'+'/'){
43916             this.editorcore.relayCmd('createlink', url);
43917         }
43918     },
43919
43920     
43921     /**
43922      * Protected method that will not generally be called directly. It triggers
43923      * a toolbar update by reading the markup state of the current selection in the editor.
43924      */
43925     updateToolbar: function(){
43926
43927         if(!this.editorcore.activated){
43928             this.editor.onFirstFocus();
43929             return;
43930         }
43931
43932         var btns = this.tb.items.map, 
43933             doc = this.editorcore.doc,
43934             frameId = this.editorcore.frameId;
43935
43936         if(!this.disable.font && !Roo.isSafari){
43937             /*
43938             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43939             if(name != this.fontSelect.dom.value){
43940                 this.fontSelect.dom.value = name;
43941             }
43942             */
43943         }
43944         if(!this.disable.format){
43945             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43946             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43947             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43948         }
43949         if(!this.disable.alignments){
43950             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43951             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43952             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43953         }
43954         if(!Roo.isSafari && !this.disable.lists){
43955             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43956             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43957         }
43958         
43959         var ans = this.editorcore.getAllAncestors();
43960         if (this.formatCombo) {
43961             
43962             
43963             var store = this.formatCombo.store;
43964             this.formatCombo.setValue("");
43965             for (var i =0; i < ans.length;i++) {
43966                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43967                     // select it..
43968                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43969                     break;
43970                 }
43971             }
43972         }
43973         
43974         
43975         
43976         // hides menus... - so this cant be on a menu...
43977         Roo.menu.MenuMgr.hideAll();
43978
43979         //this.editorsyncValue();
43980     },
43981    
43982     
43983     createFontOptions : function(){
43984         var buf = [], fs = this.fontFamilies, ff, lc;
43985         
43986         
43987         
43988         for(var i = 0, len = fs.length; i< len; i++){
43989             ff = fs[i];
43990             lc = ff.toLowerCase();
43991             buf.push(
43992                 '<option value="',lc,'" style="font-family:',ff,';"',
43993                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43994                     ff,
43995                 '</option>'
43996             );
43997         }
43998         return buf.join('');
43999     },
44000     
44001     toggleSourceEdit : function(sourceEditMode){
44002         
44003         Roo.log("toolbar toogle");
44004         if(sourceEditMode === undefined){
44005             sourceEditMode = !this.sourceEditMode;
44006         }
44007         this.sourceEditMode = sourceEditMode === true;
44008         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
44009         // just toggle the button?
44010         if(btn.pressed !== this.sourceEditMode){
44011             btn.toggle(this.sourceEditMode);
44012             return;
44013         }
44014         
44015         if(sourceEditMode){
44016             Roo.log("disabling buttons");
44017             this.tb.items.each(function(item){
44018                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
44019                     item.disable();
44020                 }
44021             });
44022           
44023         }else{
44024             Roo.log("enabling buttons");
44025             if(this.editorcore.initialized){
44026                 this.tb.items.each(function(item){
44027                     item.enable();
44028                 });
44029             }
44030             
44031         }
44032         Roo.log("calling toggole on editor");
44033         // tell the editor that it's been pressed..
44034         this.editor.toggleSourceEdit(sourceEditMode);
44035        
44036     },
44037      /**
44038      * Object collection of toolbar tooltips for the buttons in the editor. The key
44039      * is the command id associated with that button and the value is a valid QuickTips object.
44040      * For example:
44041 <pre><code>
44042 {
44043     bold : {
44044         title: 'Bold (Ctrl+B)',
44045         text: 'Make the selected text bold.',
44046         cls: 'x-html-editor-tip'
44047     },
44048     italic : {
44049         title: 'Italic (Ctrl+I)',
44050         text: 'Make the selected text italic.',
44051         cls: 'x-html-editor-tip'
44052     },
44053     ...
44054 </code></pre>
44055     * @type Object
44056      */
44057     buttonTips : {
44058         bold : {
44059             title: 'Bold (Ctrl+B)',
44060             text: 'Make the selected text bold.',
44061             cls: 'x-html-editor-tip'
44062         },
44063         italic : {
44064             title: 'Italic (Ctrl+I)',
44065             text: 'Make the selected text italic.',
44066             cls: 'x-html-editor-tip'
44067         },
44068         underline : {
44069             title: 'Underline (Ctrl+U)',
44070             text: 'Underline the selected text.',
44071             cls: 'x-html-editor-tip'
44072         },
44073         increasefontsize : {
44074             title: 'Grow Text',
44075             text: 'Increase the font size.',
44076             cls: 'x-html-editor-tip'
44077         },
44078         decreasefontsize : {
44079             title: 'Shrink Text',
44080             text: 'Decrease the font size.',
44081             cls: 'x-html-editor-tip'
44082         },
44083         backcolor : {
44084             title: 'Text Highlight Color',
44085             text: 'Change the background color of the selected text.',
44086             cls: 'x-html-editor-tip'
44087         },
44088         forecolor : {
44089             title: 'Font Color',
44090             text: 'Change the color of the selected text.',
44091             cls: 'x-html-editor-tip'
44092         },
44093         justifyleft : {
44094             title: 'Align Text Left',
44095             text: 'Align text to the left.',
44096             cls: 'x-html-editor-tip'
44097         },
44098         justifycenter : {
44099             title: 'Center Text',
44100             text: 'Center text in the editor.',
44101             cls: 'x-html-editor-tip'
44102         },
44103         justifyright : {
44104             title: 'Align Text Right',
44105             text: 'Align text to the right.',
44106             cls: 'x-html-editor-tip'
44107         },
44108         insertunorderedlist : {
44109             title: 'Bullet List',
44110             text: 'Start a bulleted list.',
44111             cls: 'x-html-editor-tip'
44112         },
44113         insertorderedlist : {
44114             title: 'Numbered List',
44115             text: 'Start a numbered list.',
44116             cls: 'x-html-editor-tip'
44117         },
44118         createlink : {
44119             title: 'Hyperlink',
44120             text: 'Make the selected text a hyperlink.',
44121             cls: 'x-html-editor-tip'
44122         },
44123         sourceedit : {
44124             title: 'Source Edit',
44125             text: 'Switch to source editing mode.',
44126             cls: 'x-html-editor-tip'
44127         }
44128     },
44129     // private
44130     onDestroy : function(){
44131         if(this.rendered){
44132             
44133             this.tb.items.each(function(item){
44134                 if(item.menu){
44135                     item.menu.removeAll();
44136                     if(item.menu.el){
44137                         item.menu.el.destroy();
44138                     }
44139                 }
44140                 item.destroy();
44141             });
44142              
44143         }
44144     },
44145     onFirstFocus: function() {
44146         this.tb.items.each(function(item){
44147            item.enable();
44148         });
44149     }
44150 });
44151
44152
44153
44154
44155 // <script type="text/javascript">
44156 /*
44157  * Based on
44158  * Ext JS Library 1.1.1
44159  * Copyright(c) 2006-2007, Ext JS, LLC.
44160  *  
44161  
44162  */
44163
44164  
44165 /**
44166  * @class Roo.form.HtmlEditor.ToolbarContext
44167  * Context Toolbar
44168  * 
44169  * Usage:
44170  *
44171  new Roo.form.HtmlEditor({
44172     ....
44173     toolbars : [
44174         { xtype: 'ToolbarStandard', styles : {} }
44175         { xtype: 'ToolbarContext', disable : {} }
44176     ]
44177 })
44178
44179      
44180  * 
44181  * @config : {Object} disable List of elements to disable.. (not done yet.)
44182  * @config : {Object} styles  Map of styles available.
44183  * 
44184  */
44185
44186 Roo.form.HtmlEditor.ToolbarContext = function(config)
44187 {
44188     
44189     Roo.apply(this, config);
44190     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
44191     // dont call parent... till later.
44192     this.styles = this.styles || {};
44193 }
44194
44195  
44196
44197 Roo.form.HtmlEditor.ToolbarContext.types = {
44198     'IMG' : {
44199         width : {
44200             title: "Width",
44201             width: 40
44202         },
44203         height:  {
44204             title: "Height",
44205             width: 40
44206         },
44207         align: {
44208             title: "Align",
44209             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
44210             width : 80
44211             
44212         },
44213         border: {
44214             title: "Border",
44215             width: 40
44216         },
44217         alt: {
44218             title: "Alt",
44219             width: 120
44220         },
44221         src : {
44222             title: "Src",
44223             width: 220
44224         }
44225         
44226     },
44227     'A' : {
44228         name : {
44229             title: "Name",
44230             width: 50
44231         },
44232         target:  {
44233             title: "Target",
44234             width: 120
44235         },
44236         href:  {
44237             title: "Href",
44238             width: 220
44239         } // border?
44240         
44241     },
44242     'TABLE' : {
44243         rows : {
44244             title: "Rows",
44245             width: 20
44246         },
44247         cols : {
44248             title: "Cols",
44249             width: 20
44250         },
44251         width : {
44252             title: "Width",
44253             width: 40
44254         },
44255         height : {
44256             title: "Height",
44257             width: 40
44258         },
44259         border : {
44260             title: "Border",
44261             width: 20
44262         }
44263     },
44264     'TD' : {
44265         width : {
44266             title: "Width",
44267             width: 40
44268         },
44269         height : {
44270             title: "Height",
44271             width: 40
44272         },   
44273         align: {
44274             title: "Align",
44275             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
44276             width: 80
44277         },
44278         valign: {
44279             title: "Valign",
44280             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
44281             width: 80
44282         },
44283         colspan: {
44284             title: "Colspan",
44285             width: 20
44286             
44287         },
44288          'font-family'  : {
44289             title : "Font",
44290             style : 'fontFamily',
44291             displayField: 'display',
44292             optname : 'font-family',
44293             width: 140
44294         }
44295     },
44296     'INPUT' : {
44297         name : {
44298             title: "name",
44299             width: 120
44300         },
44301         value : {
44302             title: "Value",
44303             width: 120
44304         },
44305         width : {
44306             title: "Width",
44307             width: 40
44308         }
44309     },
44310     'LABEL' : {
44311         'for' : {
44312             title: "For",
44313             width: 120
44314         }
44315     },
44316     'TEXTAREA' : {
44317           name : {
44318             title: "name",
44319             width: 120
44320         },
44321         rows : {
44322             title: "Rows",
44323             width: 20
44324         },
44325         cols : {
44326             title: "Cols",
44327             width: 20
44328         }
44329     },
44330     'SELECT' : {
44331         name : {
44332             title: "name",
44333             width: 120
44334         },
44335         selectoptions : {
44336             title: "Options",
44337             width: 200
44338         }
44339     },
44340     
44341     // should we really allow this??
44342     // should this just be 
44343     'BODY' : {
44344         title : {
44345             title: "Title",
44346             width: 200,
44347             disabled : true
44348         }
44349     },
44350     'SPAN' : {
44351         'font-family'  : {
44352             title : "Font",
44353             style : 'fontFamily',
44354             displayField: 'display',
44355             optname : 'font-family',
44356             width: 140
44357         }
44358     },
44359     'DIV' : {
44360         'font-family'  : {
44361             title : "Font",
44362             style : 'fontFamily',
44363             displayField: 'display',
44364             optname : 'font-family',
44365             width: 140
44366         }
44367     },
44368      'P' : {
44369         'font-family'  : {
44370             title : "Font",
44371             style : 'fontFamily',
44372             displayField: 'display',
44373             optname : 'font-family',
44374             width: 140
44375         }
44376     },
44377     
44378     '*' : {
44379         // empty..
44380     }
44381
44382 };
44383
44384 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44385 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44386
44387 Roo.form.HtmlEditor.ToolbarContext.options = {
44388         'font-family'  : [ 
44389                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44390                 [ 'Courier New', 'Courier New'],
44391                 [ 'Tahoma', 'Tahoma'],
44392                 [ 'Times New Roman,serif', 'Times'],
44393                 [ 'Verdana','Verdana' ]
44394         ]
44395 };
44396
44397 // fixme - these need to be configurable..
44398  
44399
44400 Roo.form.HtmlEditor.ToolbarContext.types
44401
44402
44403 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44404     
44405     tb: false,
44406     
44407     rendered: false,
44408     
44409     editor : false,
44410     editorcore : false,
44411     /**
44412      * @cfg {Object} disable  List of toolbar elements to disable
44413          
44414      */
44415     disable : false,
44416     /**
44417      * @cfg {Object} styles List of styles 
44418      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44419      *
44420      * These must be defined in the page, so they get rendered correctly..
44421      * .headline { }
44422      * TD.underline { }
44423      * 
44424      */
44425     styles : false,
44426     
44427     options: false,
44428     
44429     toolbars : false,
44430     
44431     init : function(editor)
44432     {
44433         this.editor = editor;
44434         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44435         var editorcore = this.editorcore;
44436         
44437         var fid = editorcore.frameId;
44438         var etb = this;
44439         function btn(id, toggle, handler){
44440             var xid = fid + '-'+ id ;
44441             return {
44442                 id : xid,
44443                 cmd : id,
44444                 cls : 'x-btn-icon x-edit-'+id,
44445                 enableToggle:toggle !== false,
44446                 scope: editorcore, // was editor...
44447                 handler:handler||editorcore.relayBtnCmd,
44448                 clickEvent:'mousedown',
44449                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44450                 tabIndex:-1
44451             };
44452         }
44453         // create a new element.
44454         var wdiv = editor.wrap.createChild({
44455                 tag: 'div'
44456             }, editor.wrap.dom.firstChild.nextSibling, true);
44457         
44458         // can we do this more than once??
44459         
44460          // stop form submits
44461       
44462  
44463         // disable everything...
44464         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44465         this.toolbars = {};
44466            
44467         for (var i in  ty) {
44468           
44469             this.toolbars[i] = this.buildToolbar(ty[i],i);
44470         }
44471         this.tb = this.toolbars.BODY;
44472         this.tb.el.show();
44473         this.buildFooter();
44474         this.footer.show();
44475         editor.on('hide', function( ) { this.footer.hide() }, this);
44476         editor.on('show', function( ) { this.footer.show() }, this);
44477         
44478          
44479         this.rendered = true;
44480         
44481         // the all the btns;
44482         editor.on('editorevent', this.updateToolbar, this);
44483         // other toolbars need to implement this..
44484         //editor.on('editmodechange', this.updateToolbar, this);
44485     },
44486     
44487     
44488     
44489     /**
44490      * Protected method that will not generally be called directly. It triggers
44491      * a toolbar update by reading the markup state of the current selection in the editor.
44492      *
44493      * Note you can force an update by calling on('editorevent', scope, false)
44494      */
44495     updateToolbar: function(editor,ev,sel){
44496
44497         //Roo.log(ev);
44498         // capture mouse up - this is handy for selecting images..
44499         // perhaps should go somewhere else...
44500         if(!this.editorcore.activated){
44501              this.editor.onFirstFocus();
44502             return;
44503         }
44504         
44505         
44506         
44507         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44508         // selectNode - might want to handle IE?
44509         if (ev &&
44510             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44511             ev.target && ev.target.tagName == 'IMG') {
44512             // they have click on an image...
44513             // let's see if we can change the selection...
44514             sel = ev.target;
44515          
44516               var nodeRange = sel.ownerDocument.createRange();
44517             try {
44518                 nodeRange.selectNode(sel);
44519             } catch (e) {
44520                 nodeRange.selectNodeContents(sel);
44521             }
44522             //nodeRange.collapse(true);
44523             var s = this.editorcore.win.getSelection();
44524             s.removeAllRanges();
44525             s.addRange(nodeRange);
44526         }  
44527         
44528       
44529         var updateFooter = sel ? false : true;
44530         
44531         
44532         var ans = this.editorcore.getAllAncestors();
44533         
44534         // pick
44535         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44536         
44537         if (!sel) { 
44538             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44539             sel = sel ? sel : this.editorcore.doc.body;
44540             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44541             
44542         }
44543         // pick a menu that exists..
44544         var tn = sel.tagName.toUpperCase();
44545         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44546         
44547         tn = sel.tagName.toUpperCase();
44548         
44549         var lastSel = this.tb.selectedNode
44550         
44551         this.tb.selectedNode = sel;
44552         
44553         // if current menu does not match..
44554         
44555         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
44556                 
44557             this.tb.el.hide();
44558             ///console.log("show: " + tn);
44559             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44560             this.tb.el.show();
44561             // update name
44562             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44563             
44564             
44565             // update attributes
44566             if (this.tb.fields) {
44567                 this.tb.fields.each(function(e) {
44568                     if (e.stylename) {
44569                         e.setValue(sel.style[e.stylename]);
44570                         return;
44571                     } 
44572                    e.setValue(sel.getAttribute(e.attrname));
44573                 });
44574             }
44575             
44576             var hasStyles = false;
44577             for(var i in this.styles) {
44578                 hasStyles = true;
44579                 break;
44580             }
44581             
44582             // update styles
44583             if (hasStyles) { 
44584                 var st = this.tb.fields.item(0);
44585                 
44586                 st.store.removeAll();
44587                
44588                 
44589                 var cn = sel.className.split(/\s+/);
44590                 
44591                 var avs = [];
44592                 if (this.styles['*']) {
44593                     
44594                     Roo.each(this.styles['*'], function(v) {
44595                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44596                     });
44597                 }
44598                 if (this.styles[tn]) { 
44599                     Roo.each(this.styles[tn], function(v) {
44600                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44601                     });
44602                 }
44603                 
44604                 st.store.loadData(avs);
44605                 st.collapse();
44606                 st.setValue(cn);
44607             }
44608             // flag our selected Node.
44609             this.tb.selectedNode = sel;
44610            
44611            
44612             Roo.menu.MenuMgr.hideAll();
44613
44614         }
44615         
44616         if (!updateFooter) {
44617             //this.footDisp.dom.innerHTML = ''; 
44618             return;
44619         }
44620         // update the footer
44621         //
44622         var html = '';
44623         
44624         this.footerEls = ans.reverse();
44625         Roo.each(this.footerEls, function(a,i) {
44626             if (!a) { return; }
44627             html += html.length ? ' &gt; '  :  '';
44628             
44629             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44630             
44631         });
44632        
44633         // 
44634         var sz = this.footDisp.up('td').getSize();
44635         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44636         this.footDisp.dom.style.marginLeft = '5px';
44637         
44638         this.footDisp.dom.style.overflow = 'hidden';
44639         
44640         this.footDisp.dom.innerHTML = html;
44641             
44642         //this.editorsyncValue();
44643     },
44644      
44645     
44646    
44647        
44648     // private
44649     onDestroy : function(){
44650         if(this.rendered){
44651             
44652             this.tb.items.each(function(item){
44653                 if(item.menu){
44654                     item.menu.removeAll();
44655                     if(item.menu.el){
44656                         item.menu.el.destroy();
44657                     }
44658                 }
44659                 item.destroy();
44660             });
44661              
44662         }
44663     },
44664     onFirstFocus: function() {
44665         // need to do this for all the toolbars..
44666         this.tb.items.each(function(item){
44667            item.enable();
44668         });
44669     },
44670     buildToolbar: function(tlist, nm)
44671     {
44672         var editor = this.editor;
44673         var editorcore = this.editorcore;
44674          // create a new element.
44675         var wdiv = editor.wrap.createChild({
44676                 tag: 'div'
44677             }, editor.wrap.dom.firstChild.nextSibling, true);
44678         
44679        
44680         var tb = new Roo.Toolbar(wdiv);
44681         // add the name..
44682         
44683         tb.add(nm+ ":&nbsp;");
44684         
44685         var styles = [];
44686         for(var i in this.styles) {
44687             styles.push(i);
44688         }
44689         
44690         // styles...
44691         if (styles && styles.length) {
44692             
44693             // this needs a multi-select checkbox...
44694             tb.addField( new Roo.form.ComboBox({
44695                 store: new Roo.data.SimpleStore({
44696                     id : 'val',
44697                     fields: ['val', 'selected'],
44698                     data : [] 
44699                 }),
44700                 name : '-roo-edit-className',
44701                 attrname : 'className',
44702                 displayField: 'val',
44703                 typeAhead: false,
44704                 mode: 'local',
44705                 editable : false,
44706                 triggerAction: 'all',
44707                 emptyText:'Select Style',
44708                 selectOnFocus:true,
44709                 width: 130,
44710                 listeners : {
44711                     'select': function(c, r, i) {
44712                         // initial support only for on class per el..
44713                         tb.selectedNode.className =  r ? r.get('val') : '';
44714                         editorcore.syncValue();
44715                     }
44716                 }
44717     
44718             }));
44719         }
44720         
44721         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44722         var tbops = tbc.options;
44723         
44724         for (var i in tlist) {
44725             
44726             var item = tlist[i];
44727             tb.add(item.title + ":&nbsp;");
44728             
44729             
44730             //optname == used so you can configure the options available..
44731             var opts = item.opts ? item.opts : false;
44732             if (item.optname) {
44733                 opts = tbops[item.optname];
44734            
44735             }
44736             
44737             if (opts) {
44738                 // opts == pulldown..
44739                 tb.addField( new Roo.form.ComboBox({
44740                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44741                         id : 'val',
44742                         fields: ['val', 'display'],
44743                         data : opts  
44744                     }),
44745                     name : '-roo-edit-' + i,
44746                     attrname : i,
44747                     stylename : item.style ? item.style : false,
44748                     displayField: item.displayField ? item.displayField : 'val',
44749                     valueField :  'val',
44750                     typeAhead: false,
44751                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44752                     editable : false,
44753                     triggerAction: 'all',
44754                     emptyText:'Select',
44755                     selectOnFocus:true,
44756                     width: item.width ? item.width  : 130,
44757                     listeners : {
44758                         'select': function(c, r, i) {
44759                             if (c.stylename) {
44760                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44761                                 return;
44762                             }
44763                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44764                         }
44765                     }
44766
44767                 }));
44768                 continue;
44769                     
44770                  
44771                 
44772                 tb.addField( new Roo.form.TextField({
44773                     name: i,
44774                     width: 100,
44775                     //allowBlank:false,
44776                     value: ''
44777                 }));
44778                 continue;
44779             }
44780             tb.addField( new Roo.form.TextField({
44781                 name: '-roo-edit-' + i,
44782                 attrname : i,
44783                 
44784                 width: item.width,
44785                 //allowBlank:true,
44786                 value: '',
44787                 listeners: {
44788                     'change' : function(f, nv, ov) {
44789                         tb.selectedNode.setAttribute(f.attrname, nv);
44790                     }
44791                 }
44792             }));
44793              
44794         }
44795         
44796         var _this = this;
44797         
44798         if(nm == 'BODY'){
44799             tb.addSeparator();
44800         
44801             tb.addButton( {
44802                 text: 'Stylesheets',
44803
44804                 listeners : {
44805                     click : function ()
44806                     {
44807                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
44808                     }
44809                 }
44810             });
44811         }
44812         
44813         tb.addFill();
44814         tb.addButton( {
44815             text: 'Remove Tag',
44816     
44817             listeners : {
44818                 click : function ()
44819                 {
44820                     // remove
44821                     // undo does not work.
44822                      
44823                     var sn = tb.selectedNode;
44824                     
44825                     var pn = sn.parentNode;
44826                     
44827                     var stn =  sn.childNodes[0];
44828                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44829                     while (sn.childNodes.length) {
44830                         var node = sn.childNodes[0];
44831                         sn.removeChild(node);
44832                         //Roo.log(node);
44833                         pn.insertBefore(node, sn);
44834                         
44835                     }
44836                     pn.removeChild(sn);
44837                     var range = editorcore.createRange();
44838         
44839                     range.setStart(stn,0);
44840                     range.setEnd(en,0); //????
44841                     //range.selectNode(sel);
44842                     
44843                     
44844                     var selection = editorcore.getSelection();
44845                     selection.removeAllRanges();
44846                     selection.addRange(range);
44847                     
44848                     
44849                     
44850                     //_this.updateToolbar(null, null, pn);
44851                     _this.updateToolbar(null, null, null);
44852                     _this.footDisp.dom.innerHTML = ''; 
44853                 }
44854             }
44855             
44856                     
44857                 
44858             
44859         });
44860         
44861         
44862         tb.el.on('click', function(e){
44863             e.preventDefault(); // what does this do?
44864         });
44865         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44866         tb.el.hide();
44867         tb.name = nm;
44868         // dont need to disable them... as they will get hidden
44869         return tb;
44870          
44871         
44872     },
44873     buildFooter : function()
44874     {
44875         
44876         var fel = this.editor.wrap.createChild();
44877         this.footer = new Roo.Toolbar(fel);
44878         // toolbar has scrolly on left / right?
44879         var footDisp= new Roo.Toolbar.Fill();
44880         var _t = this;
44881         this.footer.add(
44882             {
44883                 text : '&lt;',
44884                 xtype: 'Button',
44885                 handler : function() {
44886                     _t.footDisp.scrollTo('left',0,true)
44887                 }
44888             }
44889         );
44890         this.footer.add( footDisp );
44891         this.footer.add( 
44892             {
44893                 text : '&gt;',
44894                 xtype: 'Button',
44895                 handler : function() {
44896                     // no animation..
44897                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44898                 }
44899             }
44900         );
44901         var fel = Roo.get(footDisp.el);
44902         fel.addClass('x-editor-context');
44903         this.footDispWrap = fel; 
44904         this.footDispWrap.overflow  = 'hidden';
44905         
44906         this.footDisp = fel.createChild();
44907         this.footDispWrap.on('click', this.onContextClick, this)
44908         
44909         
44910     },
44911     onContextClick : function (ev,dom)
44912     {
44913         ev.preventDefault();
44914         var  cn = dom.className;
44915         //Roo.log(cn);
44916         if (!cn.match(/x-ed-loc-/)) {
44917             return;
44918         }
44919         var n = cn.split('-').pop();
44920         var ans = this.footerEls;
44921         var sel = ans[n];
44922         
44923          // pick
44924         var range = this.editorcore.createRange();
44925         
44926         range.selectNodeContents(sel);
44927         //range.selectNode(sel);
44928         
44929         
44930         var selection = this.editorcore.getSelection();
44931         selection.removeAllRanges();
44932         selection.addRange(range);
44933         
44934         
44935         
44936         this.updateToolbar(null, null, sel);
44937         
44938         
44939     }
44940     
44941     
44942     
44943     
44944     
44945 });
44946
44947
44948
44949
44950
44951 /*
44952  * Based on:
44953  * Ext JS Library 1.1.1
44954  * Copyright(c) 2006-2007, Ext JS, LLC.
44955  *
44956  * Originally Released Under LGPL - original licence link has changed is not relivant.
44957  *
44958  * Fork - LGPL
44959  * <script type="text/javascript">
44960  */
44961  
44962 /**
44963  * @class Roo.form.BasicForm
44964  * @extends Roo.util.Observable
44965  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44966  * @constructor
44967  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44968  * @param {Object} config Configuration options
44969  */
44970 Roo.form.BasicForm = function(el, config){
44971     this.allItems = [];
44972     this.childForms = [];
44973     Roo.apply(this, config);
44974     /*
44975      * The Roo.form.Field items in this form.
44976      * @type MixedCollection
44977      */
44978      
44979      
44980     this.items = new Roo.util.MixedCollection(false, function(o){
44981         return o.id || (o.id = Roo.id());
44982     });
44983     this.addEvents({
44984         /**
44985          * @event beforeaction
44986          * Fires before any action is performed. Return false to cancel the action.
44987          * @param {Form} this
44988          * @param {Action} action The action to be performed
44989          */
44990         beforeaction: true,
44991         /**
44992          * @event actionfailed
44993          * Fires when an action fails.
44994          * @param {Form} this
44995          * @param {Action} action The action that failed
44996          */
44997         actionfailed : true,
44998         /**
44999          * @event actioncomplete
45000          * Fires when an action is completed.
45001          * @param {Form} this
45002          * @param {Action} action The action that completed
45003          */
45004         actioncomplete : true
45005     });
45006     if(el){
45007         this.initEl(el);
45008     }
45009     Roo.form.BasicForm.superclass.constructor.call(this);
45010 };
45011
45012 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
45013     /**
45014      * @cfg {String} method
45015      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
45016      */
45017     /**
45018      * @cfg {DataReader} reader
45019      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
45020      * This is optional as there is built-in support for processing JSON.
45021      */
45022     /**
45023      * @cfg {DataReader} errorReader
45024      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
45025      * This is completely optional as there is built-in support for processing JSON.
45026      */
45027     /**
45028      * @cfg {String} url
45029      * The URL to use for form actions if one isn't supplied in the action options.
45030      */
45031     /**
45032      * @cfg {Boolean} fileUpload
45033      * Set to true if this form is a file upload.
45034      */
45035      
45036     /**
45037      * @cfg {Object} baseParams
45038      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
45039      */
45040      /**
45041      
45042     /**
45043      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
45044      */
45045     timeout: 30,
45046
45047     // private
45048     activeAction : null,
45049
45050     /**
45051      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
45052      * or setValues() data instead of when the form was first created.
45053      */
45054     trackResetOnLoad : false,
45055     
45056     
45057     /**
45058      * childForms - used for multi-tab forms
45059      * @type {Array}
45060      */
45061     childForms : false,
45062     
45063     /**
45064      * allItems - full list of fields.
45065      * @type {Array}
45066      */
45067     allItems : false,
45068     
45069     /**
45070      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
45071      * element by passing it or its id or mask the form itself by passing in true.
45072      * @type Mixed
45073      */
45074     waitMsgTarget : false,
45075
45076     // private
45077     initEl : function(el){
45078         this.el = Roo.get(el);
45079         this.id = this.el.id || Roo.id();
45080         this.el.on('submit', this.onSubmit, this);
45081         this.el.addClass('x-form');
45082     },
45083
45084     // private
45085     onSubmit : function(e){
45086         e.stopEvent();
45087     },
45088
45089     /**
45090      * Returns true if client-side validation on the form is successful.
45091      * @return Boolean
45092      */
45093     isValid : function(){
45094         var valid = true;
45095         this.items.each(function(f){
45096            if(!f.validate()){
45097                valid = false;
45098            }
45099         });
45100         return valid;
45101     },
45102
45103     /**
45104      * Returns true if any fields in this form have changed since their original load.
45105      * @return Boolean
45106      */
45107     isDirty : function(){
45108         var dirty = false;
45109         this.items.each(function(f){
45110            if(f.isDirty()){
45111                dirty = true;
45112                return false;
45113            }
45114         });
45115         return dirty;
45116     },
45117
45118     /**
45119      * Performs a predefined action (submit or load) or custom actions you define on this form.
45120      * @param {String} actionName The name of the action type
45121      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
45122      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
45123      * accept other config options):
45124      * <pre>
45125 Property          Type             Description
45126 ----------------  ---------------  ----------------------------------------------------------------------------------
45127 url               String           The url for the action (defaults to the form's url)
45128 method            String           The form method to use (defaults to the form's method, or POST if not defined)
45129 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
45130 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
45131                                    validate the form on the client (defaults to false)
45132      * </pre>
45133      * @return {BasicForm} this
45134      */
45135     doAction : function(action, options){
45136         if(typeof action == 'string'){
45137             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
45138         }
45139         if(this.fireEvent('beforeaction', this, action) !== false){
45140             this.beforeAction(action);
45141             action.run.defer(100, action);
45142         }
45143         return this;
45144     },
45145
45146     /**
45147      * Shortcut to do a submit action.
45148      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45149      * @return {BasicForm} this
45150      */
45151     submit : function(options){
45152         this.doAction('submit', options);
45153         return this;
45154     },
45155
45156     /**
45157      * Shortcut to do a load action.
45158      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
45159      * @return {BasicForm} this
45160      */
45161     load : function(options){
45162         this.doAction('load', options);
45163         return this;
45164     },
45165
45166     /**
45167      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
45168      * @param {Record} record The record to edit
45169      * @return {BasicForm} this
45170      */
45171     updateRecord : function(record){
45172         record.beginEdit();
45173         var fs = record.fields;
45174         fs.each(function(f){
45175             var field = this.findField(f.name);
45176             if(field){
45177                 record.set(f.name, field.getValue());
45178             }
45179         }, this);
45180         record.endEdit();
45181         return this;
45182     },
45183
45184     /**
45185      * Loads an Roo.data.Record into this form.
45186      * @param {Record} record The record to load
45187      * @return {BasicForm} this
45188      */
45189     loadRecord : function(record){
45190         this.setValues(record.data);
45191         return this;
45192     },
45193
45194     // private
45195     beforeAction : function(action){
45196         var o = action.options;
45197         
45198        
45199         if(this.waitMsgTarget === true){
45200             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
45201         }else if(this.waitMsgTarget){
45202             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
45203             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
45204         }else {
45205             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
45206         }
45207          
45208     },
45209
45210     // private
45211     afterAction : function(action, success){
45212         this.activeAction = null;
45213         var o = action.options;
45214         
45215         if(this.waitMsgTarget === true){
45216             this.el.unmask();
45217         }else if(this.waitMsgTarget){
45218             this.waitMsgTarget.unmask();
45219         }else{
45220             Roo.MessageBox.updateProgress(1);
45221             Roo.MessageBox.hide();
45222         }
45223          
45224         if(success){
45225             if(o.reset){
45226                 this.reset();
45227             }
45228             Roo.callback(o.success, o.scope, [this, action]);
45229             this.fireEvent('actioncomplete', this, action);
45230             
45231         }else{
45232             
45233             // failure condition..
45234             // we have a scenario where updates need confirming.
45235             // eg. if a locking scenario exists..
45236             // we look for { errors : { needs_confirm : true }} in the response.
45237             if (
45238                 (typeof(action.result) != 'undefined')  &&
45239                 (typeof(action.result.errors) != 'undefined')  &&
45240                 (typeof(action.result.errors.needs_confirm) != 'undefined')
45241            ){
45242                 var _t = this;
45243                 Roo.MessageBox.confirm(
45244                     "Change requires confirmation",
45245                     action.result.errorMsg,
45246                     function(r) {
45247                         if (r != 'yes') {
45248                             return;
45249                         }
45250                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
45251                     }
45252                     
45253                 );
45254                 
45255                 
45256                 
45257                 return;
45258             }
45259             
45260             Roo.callback(o.failure, o.scope, [this, action]);
45261             // show an error message if no failed handler is set..
45262             if (!this.hasListener('actionfailed')) {
45263                 Roo.MessageBox.alert("Error",
45264                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
45265                         action.result.errorMsg :
45266                         "Saving Failed, please check your entries or try again"
45267                 );
45268             }
45269             
45270             this.fireEvent('actionfailed', this, action);
45271         }
45272         
45273     },
45274
45275     /**
45276      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
45277      * @param {String} id The value to search for
45278      * @return Field
45279      */
45280     findField : function(id){
45281         var field = this.items.get(id);
45282         if(!field){
45283             this.items.each(function(f){
45284                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
45285                     field = f;
45286                     return false;
45287                 }
45288             });
45289         }
45290         return field || null;
45291     },
45292
45293     /**
45294      * Add a secondary form to this one, 
45295      * Used to provide tabbed forms. One form is primary, with hidden values 
45296      * which mirror the elements from the other forms.
45297      * 
45298      * @param {Roo.form.Form} form to add.
45299      * 
45300      */
45301     addForm : function(form)
45302     {
45303        
45304         if (this.childForms.indexOf(form) > -1) {
45305             // already added..
45306             return;
45307         }
45308         this.childForms.push(form);
45309         var n = '';
45310         Roo.each(form.allItems, function (fe) {
45311             
45312             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
45313             if (this.findField(n)) { // already added..
45314                 return;
45315             }
45316             var add = new Roo.form.Hidden({
45317                 name : n
45318             });
45319             add.render(this.el);
45320             
45321             this.add( add );
45322         }, this);
45323         
45324     },
45325     /**
45326      * Mark fields in this form invalid in bulk.
45327      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
45328      * @return {BasicForm} this
45329      */
45330     markInvalid : function(errors){
45331         if(errors instanceof Array){
45332             for(var i = 0, len = errors.length; i < len; i++){
45333                 var fieldError = errors[i];
45334                 var f = this.findField(fieldError.id);
45335                 if(f){
45336                     f.markInvalid(fieldError.msg);
45337                 }
45338             }
45339         }else{
45340             var field, id;
45341             for(id in errors){
45342                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
45343                     field.markInvalid(errors[id]);
45344                 }
45345             }
45346         }
45347         Roo.each(this.childForms || [], function (f) {
45348             f.markInvalid(errors);
45349         });
45350         
45351         return this;
45352     },
45353
45354     /**
45355      * Set values for fields in this form in bulk.
45356      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
45357      * @return {BasicForm} this
45358      */
45359     setValues : function(values){
45360         if(values instanceof Array){ // array of objects
45361             for(var i = 0, len = values.length; i < len; i++){
45362                 var v = values[i];
45363                 var f = this.findField(v.id);
45364                 if(f){
45365                     f.setValue(v.value);
45366                     if(this.trackResetOnLoad){
45367                         f.originalValue = f.getValue();
45368                     }
45369                 }
45370             }
45371         }else{ // object hash
45372             var field, id;
45373             for(id in values){
45374                 if(typeof values[id] != 'function' && (field = this.findField(id))){
45375                     
45376                     if (field.setFromData && 
45377                         field.valueField && 
45378                         field.displayField &&
45379                         // combos' with local stores can 
45380                         // be queried via setValue()
45381                         // to set their value..
45382                         (field.store && !field.store.isLocal)
45383                         ) {
45384                         // it's a combo
45385                         var sd = { };
45386                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
45387                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
45388                         field.setFromData(sd);
45389                         
45390                     } else {
45391                         field.setValue(values[id]);
45392                     }
45393                     
45394                     
45395                     if(this.trackResetOnLoad){
45396                         field.originalValue = field.getValue();
45397                     }
45398                 }
45399             }
45400         }
45401          
45402         Roo.each(this.childForms || [], function (f) {
45403             f.setValues(values);
45404         });
45405                 
45406         return this;
45407     },
45408
45409     /**
45410      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45411      * they are returned as an array.
45412      * @param {Boolean} asString
45413      * @return {Object}
45414      */
45415     getValues : function(asString){
45416         if (this.childForms) {
45417             // copy values from the child forms
45418             Roo.each(this.childForms, function (f) {
45419                 this.setValues(f.getValues());
45420             }, this);
45421         }
45422         
45423         
45424         
45425         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45426         if(asString === true){
45427             return fs;
45428         }
45429         return Roo.urlDecode(fs);
45430     },
45431     
45432     /**
45433      * Returns the fields in this form as an object with key/value pairs. 
45434      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45435      * @return {Object}
45436      */
45437     getFieldValues : function(with_hidden)
45438     {
45439         if (this.childForms) {
45440             // copy values from the child forms
45441             // should this call getFieldValues - probably not as we do not currently copy
45442             // hidden fields when we generate..
45443             Roo.each(this.childForms, function (f) {
45444                 this.setValues(f.getValues());
45445             }, this);
45446         }
45447         
45448         var ret = {};
45449         this.items.each(function(f){
45450             if (!f.getName()) {
45451                 return;
45452             }
45453             var v = f.getValue();
45454             if (f.inputType =='radio') {
45455                 if (typeof(ret[f.getName()]) == 'undefined') {
45456                     ret[f.getName()] = ''; // empty..
45457                 }
45458                 
45459                 if (!f.el.dom.checked) {
45460                     return;
45461                     
45462                 }
45463                 v = f.el.dom.value;
45464                 
45465             }
45466             
45467             // not sure if this supported any more..
45468             if ((typeof(v) == 'object') && f.getRawValue) {
45469                 v = f.getRawValue() ; // dates..
45470             }
45471             // combo boxes where name != hiddenName...
45472             if (f.name != f.getName()) {
45473                 ret[f.name] = f.getRawValue();
45474             }
45475             ret[f.getName()] = v;
45476         });
45477         
45478         return ret;
45479     },
45480
45481     /**
45482      * Clears all invalid messages in this form.
45483      * @return {BasicForm} this
45484      */
45485     clearInvalid : function(){
45486         this.items.each(function(f){
45487            f.clearInvalid();
45488         });
45489         
45490         Roo.each(this.childForms || [], function (f) {
45491             f.clearInvalid();
45492         });
45493         
45494         
45495         return this;
45496     },
45497
45498     /**
45499      * Resets this form.
45500      * @return {BasicForm} this
45501      */
45502     reset : function(){
45503         this.items.each(function(f){
45504             f.reset();
45505         });
45506         
45507         Roo.each(this.childForms || [], function (f) {
45508             f.reset();
45509         });
45510        
45511         
45512         return this;
45513     },
45514
45515     /**
45516      * Add Roo.form components to this form.
45517      * @param {Field} field1
45518      * @param {Field} field2 (optional)
45519      * @param {Field} etc (optional)
45520      * @return {BasicForm} this
45521      */
45522     add : function(){
45523         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45524         return this;
45525     },
45526
45527
45528     /**
45529      * Removes a field from the items collection (does NOT remove its markup).
45530      * @param {Field} field
45531      * @return {BasicForm} this
45532      */
45533     remove : function(field){
45534         this.items.remove(field);
45535         return this;
45536     },
45537
45538     /**
45539      * Looks at the fields in this form, checks them for an id attribute,
45540      * and calls applyTo on the existing dom element with that id.
45541      * @return {BasicForm} this
45542      */
45543     render : function(){
45544         this.items.each(function(f){
45545             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45546                 f.applyTo(f.id);
45547             }
45548         });
45549         return this;
45550     },
45551
45552     /**
45553      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45554      * @param {Object} values
45555      * @return {BasicForm} this
45556      */
45557     applyToFields : function(o){
45558         this.items.each(function(f){
45559            Roo.apply(f, o);
45560         });
45561         return this;
45562     },
45563
45564     /**
45565      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45566      * @param {Object} values
45567      * @return {BasicForm} this
45568      */
45569     applyIfToFields : function(o){
45570         this.items.each(function(f){
45571            Roo.applyIf(f, o);
45572         });
45573         return this;
45574     }
45575 });
45576
45577 // back compat
45578 Roo.BasicForm = Roo.form.BasicForm;/*
45579  * Based on:
45580  * Ext JS Library 1.1.1
45581  * Copyright(c) 2006-2007, Ext JS, LLC.
45582  *
45583  * Originally Released Under LGPL - original licence link has changed is not relivant.
45584  *
45585  * Fork - LGPL
45586  * <script type="text/javascript">
45587  */
45588
45589 /**
45590  * @class Roo.form.Form
45591  * @extends Roo.form.BasicForm
45592  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45593  * @constructor
45594  * @param {Object} config Configuration options
45595  */
45596 Roo.form.Form = function(config){
45597     var xitems =  [];
45598     if (config.items) {
45599         xitems = config.items;
45600         delete config.items;
45601     }
45602    
45603     
45604     Roo.form.Form.superclass.constructor.call(this, null, config);
45605     this.url = this.url || this.action;
45606     if(!this.root){
45607         this.root = new Roo.form.Layout(Roo.applyIf({
45608             id: Roo.id()
45609         }, config));
45610     }
45611     this.active = this.root;
45612     /**
45613      * Array of all the buttons that have been added to this form via {@link addButton}
45614      * @type Array
45615      */
45616     this.buttons = [];
45617     this.allItems = [];
45618     this.addEvents({
45619         /**
45620          * @event clientvalidation
45621          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45622          * @param {Form} this
45623          * @param {Boolean} valid true if the form has passed client-side validation
45624          */
45625         clientvalidation: true,
45626         /**
45627          * @event rendered
45628          * Fires when the form is rendered
45629          * @param {Roo.form.Form} form
45630          */
45631         rendered : true
45632     });
45633     
45634     if (this.progressUrl) {
45635             // push a hidden field onto the list of fields..
45636             this.addxtype( {
45637                     xns: Roo.form, 
45638                     xtype : 'Hidden', 
45639                     name : 'UPLOAD_IDENTIFIER' 
45640             });
45641         }
45642         
45643     
45644     Roo.each(xitems, this.addxtype, this);
45645     
45646     
45647     
45648 };
45649
45650 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45651     /**
45652      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45653      */
45654     /**
45655      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45656      */
45657     /**
45658      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45659      */
45660     buttonAlign:'center',
45661
45662     /**
45663      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45664      */
45665     minButtonWidth:75,
45666
45667     /**
45668      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45669      * This property cascades to child containers if not set.
45670      */
45671     labelAlign:'left',
45672
45673     /**
45674      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45675      * fires a looping event with that state. This is required to bind buttons to the valid
45676      * state using the config value formBind:true on the button.
45677      */
45678     monitorValid : false,
45679
45680     /**
45681      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45682      */
45683     monitorPoll : 200,
45684     
45685     /**
45686      * @cfg {String} progressUrl - Url to return progress data 
45687      */
45688     
45689     progressUrl : false,
45690   
45691     /**
45692      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45693      * fields are added and the column is closed. If no fields are passed the column remains open
45694      * until end() is called.
45695      * @param {Object} config The config to pass to the column
45696      * @param {Field} field1 (optional)
45697      * @param {Field} field2 (optional)
45698      * @param {Field} etc (optional)
45699      * @return Column The column container object
45700      */
45701     column : function(c){
45702         var col = new Roo.form.Column(c);
45703         this.start(col);
45704         if(arguments.length > 1){ // duplicate code required because of Opera
45705             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45706             this.end();
45707         }
45708         return col;
45709     },
45710
45711     /**
45712      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45713      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45714      * until end() is called.
45715      * @param {Object} config The config to pass to the fieldset
45716      * @param {Field} field1 (optional)
45717      * @param {Field} field2 (optional)
45718      * @param {Field} etc (optional)
45719      * @return FieldSet The fieldset container object
45720      */
45721     fieldset : function(c){
45722         var fs = new Roo.form.FieldSet(c);
45723         this.start(fs);
45724         if(arguments.length > 1){ // duplicate code required because of Opera
45725             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45726             this.end();
45727         }
45728         return fs;
45729     },
45730
45731     /**
45732      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45733      * fields are added and the container is closed. If no fields are passed the container remains open
45734      * until end() is called.
45735      * @param {Object} config The config to pass to the Layout
45736      * @param {Field} field1 (optional)
45737      * @param {Field} field2 (optional)
45738      * @param {Field} etc (optional)
45739      * @return Layout The container object
45740      */
45741     container : function(c){
45742         var l = new Roo.form.Layout(c);
45743         this.start(l);
45744         if(arguments.length > 1){ // duplicate code required because of Opera
45745             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45746             this.end();
45747         }
45748         return l;
45749     },
45750
45751     /**
45752      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45753      * @param {Object} container A Roo.form.Layout or subclass of Layout
45754      * @return {Form} this
45755      */
45756     start : function(c){
45757         // cascade label info
45758         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45759         this.active.stack.push(c);
45760         c.ownerCt = this.active;
45761         this.active = c;
45762         return this;
45763     },
45764
45765     /**
45766      * Closes the current open container
45767      * @return {Form} this
45768      */
45769     end : function(){
45770         if(this.active == this.root){
45771             return this;
45772         }
45773         this.active = this.active.ownerCt;
45774         return this;
45775     },
45776
45777     /**
45778      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45779      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45780      * as the label of the field.
45781      * @param {Field} field1
45782      * @param {Field} field2 (optional)
45783      * @param {Field} etc. (optional)
45784      * @return {Form} this
45785      */
45786     add : function(){
45787         this.active.stack.push.apply(this.active.stack, arguments);
45788         this.allItems.push.apply(this.allItems,arguments);
45789         var r = [];
45790         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45791             if(a[i].isFormField){
45792                 r.push(a[i]);
45793             }
45794         }
45795         if(r.length > 0){
45796             Roo.form.Form.superclass.add.apply(this, r);
45797         }
45798         return this;
45799     },
45800     
45801
45802     
45803     
45804     
45805      /**
45806      * Find any element that has been added to a form, using it's ID or name
45807      * This can include framesets, columns etc. along with regular fields..
45808      * @param {String} id - id or name to find.
45809      
45810      * @return {Element} e - or false if nothing found.
45811      */
45812     findbyId : function(id)
45813     {
45814         var ret = false;
45815         if (!id) {
45816             return ret;
45817         }
45818         Roo.each(this.allItems, function(f){
45819             if (f.id == id || f.name == id ){
45820                 ret = f;
45821                 return false;
45822             }
45823         });
45824         return ret;
45825     },
45826
45827     
45828     
45829     /**
45830      * Render this form into the passed container. This should only be called once!
45831      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45832      * @return {Form} this
45833      */
45834     render : function(ct)
45835     {
45836         
45837         
45838         
45839         ct = Roo.get(ct);
45840         var o = this.autoCreate || {
45841             tag: 'form',
45842             method : this.method || 'POST',
45843             id : this.id || Roo.id()
45844         };
45845         this.initEl(ct.createChild(o));
45846
45847         this.root.render(this.el);
45848         
45849        
45850              
45851         this.items.each(function(f){
45852             f.render('x-form-el-'+f.id);
45853         });
45854
45855         if(this.buttons.length > 0){
45856             // tables are required to maintain order and for correct IE layout
45857             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45858                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45859                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45860             }}, null, true);
45861             var tr = tb.getElementsByTagName('tr')[0];
45862             for(var i = 0, len = this.buttons.length; i < len; i++) {
45863                 var b = this.buttons[i];
45864                 var td = document.createElement('td');
45865                 td.className = 'x-form-btn-td';
45866                 b.render(tr.appendChild(td));
45867             }
45868         }
45869         if(this.monitorValid){ // initialize after render
45870             this.startMonitoring();
45871         }
45872         this.fireEvent('rendered', this);
45873         return this;
45874     },
45875
45876     /**
45877      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45878      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45879      * object or a valid Roo.DomHelper element config
45880      * @param {Function} handler The function called when the button is clicked
45881      * @param {Object} scope (optional) The scope of the handler function
45882      * @return {Roo.Button}
45883      */
45884     addButton : function(config, handler, scope){
45885         var bc = {
45886             handler: handler,
45887             scope: scope,
45888             minWidth: this.minButtonWidth,
45889             hideParent:true
45890         };
45891         if(typeof config == "string"){
45892             bc.text = config;
45893         }else{
45894             Roo.apply(bc, config);
45895         }
45896         var btn = new Roo.Button(null, bc);
45897         this.buttons.push(btn);
45898         return btn;
45899     },
45900
45901      /**
45902      * Adds a series of form elements (using the xtype property as the factory method.
45903      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45904      * @param {Object} config 
45905      */
45906     
45907     addxtype : function()
45908     {
45909         var ar = Array.prototype.slice.call(arguments, 0);
45910         var ret = false;
45911         for(var i = 0; i < ar.length; i++) {
45912             if (!ar[i]) {
45913                 continue; // skip -- if this happends something invalid got sent, we 
45914                 // should ignore it, as basically that interface element will not show up
45915                 // and that should be pretty obvious!!
45916             }
45917             
45918             if (Roo.form[ar[i].xtype]) {
45919                 ar[i].form = this;
45920                 var fe = Roo.factory(ar[i], Roo.form);
45921                 if (!ret) {
45922                     ret = fe;
45923                 }
45924                 fe.form = this;
45925                 if (fe.store) {
45926                     fe.store.form = this;
45927                 }
45928                 if (fe.isLayout) {  
45929                          
45930                     this.start(fe);
45931                     this.allItems.push(fe);
45932                     if (fe.items && fe.addxtype) {
45933                         fe.addxtype.apply(fe, fe.items);
45934                         delete fe.items;
45935                     }
45936                      this.end();
45937                     continue;
45938                 }
45939                 
45940                 
45941                  
45942                 this.add(fe);
45943               //  console.log('adding ' + ar[i].xtype);
45944             }
45945             if (ar[i].xtype == 'Button') {  
45946                 //console.log('adding button');
45947                 //console.log(ar[i]);
45948                 this.addButton(ar[i]);
45949                 this.allItems.push(fe);
45950                 continue;
45951             }
45952             
45953             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45954                 alert('end is not supported on xtype any more, use items');
45955             //    this.end();
45956             //    //console.log('adding end');
45957             }
45958             
45959         }
45960         return ret;
45961     },
45962     
45963     /**
45964      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45965      * option "monitorValid"
45966      */
45967     startMonitoring : function(){
45968         if(!this.bound){
45969             this.bound = true;
45970             Roo.TaskMgr.start({
45971                 run : this.bindHandler,
45972                 interval : this.monitorPoll || 200,
45973                 scope: this
45974             });
45975         }
45976     },
45977
45978     /**
45979      * Stops monitoring of the valid state of this form
45980      */
45981     stopMonitoring : function(){
45982         this.bound = false;
45983     },
45984
45985     // private
45986     bindHandler : function(){
45987         if(!this.bound){
45988             return false; // stops binding
45989         }
45990         var valid = true;
45991         this.items.each(function(f){
45992             if(!f.isValid(true)){
45993                 valid = false;
45994                 return false;
45995             }
45996         });
45997         for(var i = 0, len = this.buttons.length; i < len; i++){
45998             var btn = this.buttons[i];
45999             if(btn.formBind === true && btn.disabled === valid){
46000                 btn.setDisabled(!valid);
46001             }
46002         }
46003         this.fireEvent('clientvalidation', this, valid);
46004     }
46005     
46006     
46007     
46008     
46009     
46010     
46011     
46012     
46013 });
46014
46015
46016 // back compat
46017 Roo.Form = Roo.form.Form;
46018 /*
46019  * Based on:
46020  * Ext JS Library 1.1.1
46021  * Copyright(c) 2006-2007, Ext JS, LLC.
46022  *
46023  * Originally Released Under LGPL - original licence link has changed is not relivant.
46024  *
46025  * Fork - LGPL
46026  * <script type="text/javascript">
46027  */
46028
46029 // as we use this in bootstrap.
46030 Roo.namespace('Roo.form');
46031  /**
46032  * @class Roo.form.Action
46033  * Internal Class used to handle form actions
46034  * @constructor
46035  * @param {Roo.form.BasicForm} el The form element or its id
46036  * @param {Object} config Configuration options
46037  */
46038
46039  
46040  
46041 // define the action interface
46042 Roo.form.Action = function(form, options){
46043     this.form = form;
46044     this.options = options || {};
46045 };
46046 /**
46047  * Client Validation Failed
46048  * @const 
46049  */
46050 Roo.form.Action.CLIENT_INVALID = 'client';
46051 /**
46052  * Server Validation Failed
46053  * @const 
46054  */
46055 Roo.form.Action.SERVER_INVALID = 'server';
46056  /**
46057  * Connect to Server Failed
46058  * @const 
46059  */
46060 Roo.form.Action.CONNECT_FAILURE = 'connect';
46061 /**
46062  * Reading Data from Server Failed
46063  * @const 
46064  */
46065 Roo.form.Action.LOAD_FAILURE = 'load';
46066
46067 Roo.form.Action.prototype = {
46068     type : 'default',
46069     failureType : undefined,
46070     response : undefined,
46071     result : undefined,
46072
46073     // interface method
46074     run : function(options){
46075
46076     },
46077
46078     // interface method
46079     success : function(response){
46080
46081     },
46082
46083     // interface method
46084     handleResponse : function(response){
46085
46086     },
46087
46088     // default connection failure
46089     failure : function(response){
46090         
46091         this.response = response;
46092         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46093         this.form.afterAction(this, false);
46094     },
46095
46096     processResponse : function(response){
46097         this.response = response;
46098         if(!response.responseText){
46099             return true;
46100         }
46101         this.result = this.handleResponse(response);
46102         return this.result;
46103     },
46104
46105     // utility functions used internally
46106     getUrl : function(appendParams){
46107         var url = this.options.url || this.form.url || this.form.el.dom.action;
46108         if(appendParams){
46109             var p = this.getParams();
46110             if(p){
46111                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
46112             }
46113         }
46114         return url;
46115     },
46116
46117     getMethod : function(){
46118         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
46119     },
46120
46121     getParams : function(){
46122         var bp = this.form.baseParams;
46123         var p = this.options.params;
46124         if(p){
46125             if(typeof p == "object"){
46126                 p = Roo.urlEncode(Roo.applyIf(p, bp));
46127             }else if(typeof p == 'string' && bp){
46128                 p += '&' + Roo.urlEncode(bp);
46129             }
46130         }else if(bp){
46131             p = Roo.urlEncode(bp);
46132         }
46133         return p;
46134     },
46135
46136     createCallback : function(){
46137         return {
46138             success: this.success,
46139             failure: this.failure,
46140             scope: this,
46141             timeout: (this.form.timeout*1000),
46142             upload: this.form.fileUpload ? this.success : undefined
46143         };
46144     }
46145 };
46146
46147 Roo.form.Action.Submit = function(form, options){
46148     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
46149 };
46150
46151 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
46152     type : 'submit',
46153
46154     haveProgress : false,
46155     uploadComplete : false,
46156     
46157     // uploadProgress indicator.
46158     uploadProgress : function()
46159     {
46160         if (!this.form.progressUrl) {
46161             return;
46162         }
46163         
46164         if (!this.haveProgress) {
46165             Roo.MessageBox.progress("Uploading", "Uploading");
46166         }
46167         if (this.uploadComplete) {
46168            Roo.MessageBox.hide();
46169            return;
46170         }
46171         
46172         this.haveProgress = true;
46173    
46174         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
46175         
46176         var c = new Roo.data.Connection();
46177         c.request({
46178             url : this.form.progressUrl,
46179             params: {
46180                 id : uid
46181             },
46182             method: 'GET',
46183             success : function(req){
46184                //console.log(data);
46185                 var rdata = false;
46186                 var edata;
46187                 try  {
46188                    rdata = Roo.decode(req.responseText)
46189                 } catch (e) {
46190                     Roo.log("Invalid data from server..");
46191                     Roo.log(edata);
46192                     return;
46193                 }
46194                 if (!rdata || !rdata.success) {
46195                     Roo.log(rdata);
46196                     Roo.MessageBox.alert(Roo.encode(rdata));
46197                     return;
46198                 }
46199                 var data = rdata.data;
46200                 
46201                 if (this.uploadComplete) {
46202                    Roo.MessageBox.hide();
46203                    return;
46204                 }
46205                    
46206                 if (data){
46207                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
46208                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
46209                     );
46210                 }
46211                 this.uploadProgress.defer(2000,this);
46212             },
46213        
46214             failure: function(data) {
46215                 Roo.log('progress url failed ');
46216                 Roo.log(data);
46217             },
46218             scope : this
46219         });
46220            
46221     },
46222     
46223     
46224     run : function()
46225     {
46226         // run get Values on the form, so it syncs any secondary forms.
46227         this.form.getValues();
46228         
46229         var o = this.options;
46230         var method = this.getMethod();
46231         var isPost = method == 'POST';
46232         if(o.clientValidation === false || this.form.isValid()){
46233             
46234             if (this.form.progressUrl) {
46235                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
46236                     (new Date() * 1) + '' + Math.random());
46237                     
46238             } 
46239             
46240             
46241             Roo.Ajax.request(Roo.apply(this.createCallback(), {
46242                 form:this.form.el.dom,
46243                 url:this.getUrl(!isPost),
46244                 method: method,
46245                 params:isPost ? this.getParams() : null,
46246                 isUpload: this.form.fileUpload
46247             }));
46248             
46249             this.uploadProgress();
46250
46251         }else if (o.clientValidation !== false){ // client validation failed
46252             this.failureType = Roo.form.Action.CLIENT_INVALID;
46253             this.form.afterAction(this, false);
46254         }
46255     },
46256
46257     success : function(response)
46258     {
46259         this.uploadComplete= true;
46260         if (this.haveProgress) {
46261             Roo.MessageBox.hide();
46262         }
46263         
46264         
46265         var result = this.processResponse(response);
46266         if(result === true || result.success){
46267             this.form.afterAction(this, true);
46268             return;
46269         }
46270         if(result.errors){
46271             this.form.markInvalid(result.errors);
46272             this.failureType = Roo.form.Action.SERVER_INVALID;
46273         }
46274         this.form.afterAction(this, false);
46275     },
46276     failure : function(response)
46277     {
46278         this.uploadComplete= true;
46279         if (this.haveProgress) {
46280             Roo.MessageBox.hide();
46281         }
46282         
46283         this.response = response;
46284         this.failureType = Roo.form.Action.CONNECT_FAILURE;
46285         this.form.afterAction(this, false);
46286     },
46287     
46288     handleResponse : function(response){
46289         if(this.form.errorReader){
46290             var rs = this.form.errorReader.read(response);
46291             var errors = [];
46292             if(rs.records){
46293                 for(var i = 0, len = rs.records.length; i < len; i++) {
46294                     var r = rs.records[i];
46295                     errors[i] = r.data;
46296                 }
46297             }
46298             if(errors.length < 1){
46299                 errors = null;
46300             }
46301             return {
46302                 success : rs.success,
46303                 errors : errors
46304             };
46305         }
46306         var ret = false;
46307         try {
46308             ret = Roo.decode(response.responseText);
46309         } catch (e) {
46310             ret = {
46311                 success: false,
46312                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
46313                 errors : []
46314             };
46315         }
46316         return ret;
46317         
46318     }
46319 });
46320
46321
46322 Roo.form.Action.Load = function(form, options){
46323     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
46324     this.reader = this.form.reader;
46325 };
46326
46327 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
46328     type : 'load',
46329
46330     run : function(){
46331         
46332         Roo.Ajax.request(Roo.apply(
46333                 this.createCallback(), {
46334                     method:this.getMethod(),
46335                     url:this.getUrl(false),
46336                     params:this.getParams()
46337         }));
46338     },
46339
46340     success : function(response){
46341         
46342         var result = this.processResponse(response);
46343         if(result === true || !result.success || !result.data){
46344             this.failureType = Roo.form.Action.LOAD_FAILURE;
46345             this.form.afterAction(this, false);
46346             return;
46347         }
46348         this.form.clearInvalid();
46349         this.form.setValues(result.data);
46350         this.form.afterAction(this, true);
46351     },
46352
46353     handleResponse : function(response){
46354         if(this.form.reader){
46355             var rs = this.form.reader.read(response);
46356             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
46357             return {
46358                 success : rs.success,
46359                 data : data
46360             };
46361         }
46362         return Roo.decode(response.responseText);
46363     }
46364 });
46365
46366 Roo.form.Action.ACTION_TYPES = {
46367     'load' : Roo.form.Action.Load,
46368     'submit' : Roo.form.Action.Submit
46369 };/*
46370  * Based on:
46371  * Ext JS Library 1.1.1
46372  * Copyright(c) 2006-2007, Ext JS, LLC.
46373  *
46374  * Originally Released Under LGPL - original licence link has changed is not relivant.
46375  *
46376  * Fork - LGPL
46377  * <script type="text/javascript">
46378  */
46379  
46380 /**
46381  * @class Roo.form.Layout
46382  * @extends Roo.Component
46383  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
46384  * @constructor
46385  * @param {Object} config Configuration options
46386  */
46387 Roo.form.Layout = function(config){
46388     var xitems = [];
46389     if (config.items) {
46390         xitems = config.items;
46391         delete config.items;
46392     }
46393     Roo.form.Layout.superclass.constructor.call(this, config);
46394     this.stack = [];
46395     Roo.each(xitems, this.addxtype, this);
46396      
46397 };
46398
46399 Roo.extend(Roo.form.Layout, Roo.Component, {
46400     /**
46401      * @cfg {String/Object} autoCreate
46402      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46403      */
46404     /**
46405      * @cfg {String/Object/Function} style
46406      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46407      * a function which returns such a specification.
46408      */
46409     /**
46410      * @cfg {String} labelAlign
46411      * Valid values are "left," "top" and "right" (defaults to "left")
46412      */
46413     /**
46414      * @cfg {Number} labelWidth
46415      * Fixed width in pixels of all field labels (defaults to undefined)
46416      */
46417     /**
46418      * @cfg {Boolean} clear
46419      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46420      */
46421     clear : true,
46422     /**
46423      * @cfg {String} labelSeparator
46424      * The separator to use after field labels (defaults to ':')
46425      */
46426     labelSeparator : ':',
46427     /**
46428      * @cfg {Boolean} hideLabels
46429      * True to suppress the display of field labels in this layout (defaults to false)
46430      */
46431     hideLabels : false,
46432
46433     // private
46434     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46435     
46436     isLayout : true,
46437     
46438     // private
46439     onRender : function(ct, position){
46440         if(this.el){ // from markup
46441             this.el = Roo.get(this.el);
46442         }else {  // generate
46443             var cfg = this.getAutoCreate();
46444             this.el = ct.createChild(cfg, position);
46445         }
46446         if(this.style){
46447             this.el.applyStyles(this.style);
46448         }
46449         if(this.labelAlign){
46450             this.el.addClass('x-form-label-'+this.labelAlign);
46451         }
46452         if(this.hideLabels){
46453             this.labelStyle = "display:none";
46454             this.elementStyle = "padding-left:0;";
46455         }else{
46456             if(typeof this.labelWidth == 'number'){
46457                 this.labelStyle = "width:"+this.labelWidth+"px;";
46458                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46459             }
46460             if(this.labelAlign == 'top'){
46461                 this.labelStyle = "width:auto;";
46462                 this.elementStyle = "padding-left:0;";
46463             }
46464         }
46465         var stack = this.stack;
46466         var slen = stack.length;
46467         if(slen > 0){
46468             if(!this.fieldTpl){
46469                 var t = new Roo.Template(
46470                     '<div class="x-form-item {5}">',
46471                         '<label for="{0}" style="{2}">{1}{4}</label>',
46472                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46473                         '</div>',
46474                     '</div><div class="x-form-clear-left"></div>'
46475                 );
46476                 t.disableFormats = true;
46477                 t.compile();
46478                 Roo.form.Layout.prototype.fieldTpl = t;
46479             }
46480             for(var i = 0; i < slen; i++) {
46481                 if(stack[i].isFormField){
46482                     this.renderField(stack[i]);
46483                 }else{
46484                     this.renderComponent(stack[i]);
46485                 }
46486             }
46487         }
46488         if(this.clear){
46489             this.el.createChild({cls:'x-form-clear'});
46490         }
46491     },
46492
46493     // private
46494     renderField : function(f){
46495         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46496                f.id, //0
46497                f.fieldLabel, //1
46498                f.labelStyle||this.labelStyle||'', //2
46499                this.elementStyle||'', //3
46500                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46501                f.itemCls||this.itemCls||''  //5
46502        ], true).getPrevSibling());
46503     },
46504
46505     // private
46506     renderComponent : function(c){
46507         c.render(c.isLayout ? this.el : this.el.createChild());    
46508     },
46509     /**
46510      * Adds a object form elements (using the xtype property as the factory method.)
46511      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46512      * @param {Object} config 
46513      */
46514     addxtype : function(o)
46515     {
46516         // create the lement.
46517         o.form = this.form;
46518         var fe = Roo.factory(o, Roo.form);
46519         this.form.allItems.push(fe);
46520         this.stack.push(fe);
46521         
46522         if (fe.isFormField) {
46523             this.form.items.add(fe);
46524         }
46525          
46526         return fe;
46527     }
46528 });
46529
46530 /**
46531  * @class Roo.form.Column
46532  * @extends Roo.form.Layout
46533  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46534  * @constructor
46535  * @param {Object} config Configuration options
46536  */
46537 Roo.form.Column = function(config){
46538     Roo.form.Column.superclass.constructor.call(this, config);
46539 };
46540
46541 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46542     /**
46543      * @cfg {Number/String} width
46544      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46545      */
46546     /**
46547      * @cfg {String/Object} autoCreate
46548      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46549      */
46550
46551     // private
46552     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46553
46554     // private
46555     onRender : function(ct, position){
46556         Roo.form.Column.superclass.onRender.call(this, ct, position);
46557         if(this.width){
46558             this.el.setWidth(this.width);
46559         }
46560     }
46561 });
46562
46563
46564 /**
46565  * @class Roo.form.Row
46566  * @extends Roo.form.Layout
46567  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46568  * @constructor
46569  * @param {Object} config Configuration options
46570  */
46571
46572  
46573 Roo.form.Row = function(config){
46574     Roo.form.Row.superclass.constructor.call(this, config);
46575 };
46576  
46577 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46578       /**
46579      * @cfg {Number/String} width
46580      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46581      */
46582     /**
46583      * @cfg {Number/String} height
46584      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46585      */
46586     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46587     
46588     padWidth : 20,
46589     // private
46590     onRender : function(ct, position){
46591         //console.log('row render');
46592         if(!this.rowTpl){
46593             var t = new Roo.Template(
46594                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46595                     '<label for="{0}" style="{2}">{1}{4}</label>',
46596                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46597                     '</div>',
46598                 '</div>'
46599             );
46600             t.disableFormats = true;
46601             t.compile();
46602             Roo.form.Layout.prototype.rowTpl = t;
46603         }
46604         this.fieldTpl = this.rowTpl;
46605         
46606         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46607         var labelWidth = 100;
46608         
46609         if ((this.labelAlign != 'top')) {
46610             if (typeof this.labelWidth == 'number') {
46611                 labelWidth = this.labelWidth
46612             }
46613             this.padWidth =  20 + labelWidth;
46614             
46615         }
46616         
46617         Roo.form.Column.superclass.onRender.call(this, ct, position);
46618         if(this.width){
46619             this.el.setWidth(this.width);
46620         }
46621         if(this.height){
46622             this.el.setHeight(this.height);
46623         }
46624     },
46625     
46626     // private
46627     renderField : function(f){
46628         f.fieldEl = this.fieldTpl.append(this.el, [
46629                f.id, f.fieldLabel,
46630                f.labelStyle||this.labelStyle||'',
46631                this.elementStyle||'',
46632                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46633                f.itemCls||this.itemCls||'',
46634                f.width ? f.width + this.padWidth : 160 + this.padWidth
46635        ],true);
46636     }
46637 });
46638  
46639
46640 /**
46641  * @class Roo.form.FieldSet
46642  * @extends Roo.form.Layout
46643  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46644  * @constructor
46645  * @param {Object} config Configuration options
46646  */
46647 Roo.form.FieldSet = function(config){
46648     Roo.form.FieldSet.superclass.constructor.call(this, config);
46649 };
46650
46651 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46652     /**
46653      * @cfg {String} legend
46654      * The text to display as the legend for the FieldSet (defaults to '')
46655      */
46656     /**
46657      * @cfg {String/Object} autoCreate
46658      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46659      */
46660
46661     // private
46662     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46663
46664     // private
46665     onRender : function(ct, position){
46666         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46667         if(this.legend){
46668             this.setLegend(this.legend);
46669         }
46670     },
46671
46672     // private
46673     setLegend : function(text){
46674         if(this.rendered){
46675             this.el.child('legend').update(text);
46676         }
46677     }
46678 });/*
46679  * Based on:
46680  * Ext JS Library 1.1.1
46681  * Copyright(c) 2006-2007, Ext JS, LLC.
46682  *
46683  * Originally Released Under LGPL - original licence link has changed is not relivant.
46684  *
46685  * Fork - LGPL
46686  * <script type="text/javascript">
46687  */
46688 /**
46689  * @class Roo.form.VTypes
46690  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46691  * @singleton
46692  */
46693 Roo.form.VTypes = function(){
46694     // closure these in so they are only created once.
46695     var alpha = /^[a-zA-Z_]+$/;
46696     var alphanum = /^[a-zA-Z0-9_]+$/;
46697     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46698     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46699
46700     // All these messages and functions are configurable
46701     return {
46702         /**
46703          * The function used to validate email addresses
46704          * @param {String} value The email address
46705          */
46706         'email' : function(v){
46707             return email.test(v);
46708         },
46709         /**
46710          * The error text to display when the email validation function returns false
46711          * @type String
46712          */
46713         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46714         /**
46715          * The keystroke filter mask to be applied on email input
46716          * @type RegExp
46717          */
46718         'emailMask' : /[a-z0-9_\.\-@]/i,
46719
46720         /**
46721          * The function used to validate URLs
46722          * @param {String} value The URL
46723          */
46724         'url' : function(v){
46725             return url.test(v);
46726         },
46727         /**
46728          * The error text to display when the url validation function returns false
46729          * @type String
46730          */
46731         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46732         
46733         /**
46734          * The function used to validate alpha values
46735          * @param {String} value The value
46736          */
46737         'alpha' : function(v){
46738             return alpha.test(v);
46739         },
46740         /**
46741          * The error text to display when the alpha validation function returns false
46742          * @type String
46743          */
46744         'alphaText' : 'This field should only contain letters and _',
46745         /**
46746          * The keystroke filter mask to be applied on alpha input
46747          * @type RegExp
46748          */
46749         'alphaMask' : /[a-z_]/i,
46750
46751         /**
46752          * The function used to validate alphanumeric values
46753          * @param {String} value The value
46754          */
46755         'alphanum' : function(v){
46756             return alphanum.test(v);
46757         },
46758         /**
46759          * The error text to display when the alphanumeric validation function returns false
46760          * @type String
46761          */
46762         'alphanumText' : 'This field should only contain letters, numbers and _',
46763         /**
46764          * The keystroke filter mask to be applied on alphanumeric input
46765          * @type RegExp
46766          */
46767         'alphanumMask' : /[a-z0-9_]/i
46768     };
46769 }();//<script type="text/javascript">
46770
46771 /**
46772  * @class Roo.form.FCKeditor
46773  * @extends Roo.form.TextArea
46774  * Wrapper around the FCKEditor http://www.fckeditor.net
46775  * @constructor
46776  * Creates a new FCKeditor
46777  * @param {Object} config Configuration options
46778  */
46779 Roo.form.FCKeditor = function(config){
46780     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46781     this.addEvents({
46782          /**
46783          * @event editorinit
46784          * Fired when the editor is initialized - you can add extra handlers here..
46785          * @param {FCKeditor} this
46786          * @param {Object} the FCK object.
46787          */
46788         editorinit : true
46789     });
46790     
46791     
46792 };
46793 Roo.form.FCKeditor.editors = { };
46794 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46795 {
46796     //defaultAutoCreate : {
46797     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46798     //},
46799     // private
46800     /**
46801      * @cfg {Object} fck options - see fck manual for details.
46802      */
46803     fckconfig : false,
46804     
46805     /**
46806      * @cfg {Object} fck toolbar set (Basic or Default)
46807      */
46808     toolbarSet : 'Basic',
46809     /**
46810      * @cfg {Object} fck BasePath
46811      */ 
46812     basePath : '/fckeditor/',
46813     
46814     
46815     frame : false,
46816     
46817     value : '',
46818     
46819    
46820     onRender : function(ct, position)
46821     {
46822         if(!this.el){
46823             this.defaultAutoCreate = {
46824                 tag: "textarea",
46825                 style:"width:300px;height:60px;",
46826                 autocomplete: "new-password"
46827             };
46828         }
46829         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46830         /*
46831         if(this.grow){
46832             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46833             if(this.preventScrollbars){
46834                 this.el.setStyle("overflow", "hidden");
46835             }
46836             this.el.setHeight(this.growMin);
46837         }
46838         */
46839         //console.log('onrender' + this.getId() );
46840         Roo.form.FCKeditor.editors[this.getId()] = this;
46841          
46842
46843         this.replaceTextarea() ;
46844         
46845     },
46846     
46847     getEditor : function() {
46848         return this.fckEditor;
46849     },
46850     /**
46851      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46852      * @param {Mixed} value The value to set
46853      */
46854     
46855     
46856     setValue : function(value)
46857     {
46858         //console.log('setValue: ' + value);
46859         
46860         if(typeof(value) == 'undefined') { // not sure why this is happending...
46861             return;
46862         }
46863         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46864         
46865         //if(!this.el || !this.getEditor()) {
46866         //    this.value = value;
46867             //this.setValue.defer(100,this,[value]);    
46868         //    return;
46869         //} 
46870         
46871         if(!this.getEditor()) {
46872             return;
46873         }
46874         
46875         this.getEditor().SetData(value);
46876         
46877         //
46878
46879     },
46880
46881     /**
46882      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46883      * @return {Mixed} value The field value
46884      */
46885     getValue : function()
46886     {
46887         
46888         if (this.frame && this.frame.dom.style.display == 'none') {
46889             return Roo.form.FCKeditor.superclass.getValue.call(this);
46890         }
46891         
46892         if(!this.el || !this.getEditor()) {
46893            
46894            // this.getValue.defer(100,this); 
46895             return this.value;
46896         }
46897        
46898         
46899         var value=this.getEditor().GetData();
46900         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46901         return Roo.form.FCKeditor.superclass.getValue.call(this);
46902         
46903
46904     },
46905
46906     /**
46907      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46908      * @return {Mixed} value The field value
46909      */
46910     getRawValue : function()
46911     {
46912         if (this.frame && this.frame.dom.style.display == 'none') {
46913             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46914         }
46915         
46916         if(!this.el || !this.getEditor()) {
46917             //this.getRawValue.defer(100,this); 
46918             return this.value;
46919             return;
46920         }
46921         
46922         
46923         
46924         var value=this.getEditor().GetData();
46925         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46926         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46927          
46928     },
46929     
46930     setSize : function(w,h) {
46931         
46932         
46933         
46934         //if (this.frame && this.frame.dom.style.display == 'none') {
46935         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46936         //    return;
46937         //}
46938         //if(!this.el || !this.getEditor()) {
46939         //    this.setSize.defer(100,this, [w,h]); 
46940         //    return;
46941         //}
46942         
46943         
46944         
46945         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46946         
46947         this.frame.dom.setAttribute('width', w);
46948         this.frame.dom.setAttribute('height', h);
46949         this.frame.setSize(w,h);
46950         
46951     },
46952     
46953     toggleSourceEdit : function(value) {
46954         
46955       
46956          
46957         this.el.dom.style.display = value ? '' : 'none';
46958         this.frame.dom.style.display = value ?  'none' : '';
46959         
46960     },
46961     
46962     
46963     focus: function(tag)
46964     {
46965         if (this.frame.dom.style.display == 'none') {
46966             return Roo.form.FCKeditor.superclass.focus.call(this);
46967         }
46968         if(!this.el || !this.getEditor()) {
46969             this.focus.defer(100,this, [tag]); 
46970             return;
46971         }
46972         
46973         
46974         
46975         
46976         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46977         this.getEditor().Focus();
46978         if (tgs.length) {
46979             if (!this.getEditor().Selection.GetSelection()) {
46980                 this.focus.defer(100,this, [tag]); 
46981                 return;
46982             }
46983             
46984             
46985             var r = this.getEditor().EditorDocument.createRange();
46986             r.setStart(tgs[0],0);
46987             r.setEnd(tgs[0],0);
46988             this.getEditor().Selection.GetSelection().removeAllRanges();
46989             this.getEditor().Selection.GetSelection().addRange(r);
46990             this.getEditor().Focus();
46991         }
46992         
46993     },
46994     
46995     
46996     
46997     replaceTextarea : function()
46998     {
46999         if ( document.getElementById( this.getId() + '___Frame' ) )
47000             return ;
47001         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
47002         //{
47003             // We must check the elements firstly using the Id and then the name.
47004         var oTextarea = document.getElementById( this.getId() );
47005         
47006         var colElementsByName = document.getElementsByName( this.getId() ) ;
47007          
47008         oTextarea.style.display = 'none' ;
47009
47010         if ( oTextarea.tabIndex ) {            
47011             this.TabIndex = oTextarea.tabIndex ;
47012         }
47013         
47014         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
47015         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
47016         this.frame = Roo.get(this.getId() + '___Frame')
47017     },
47018     
47019     _getConfigHtml : function()
47020     {
47021         var sConfig = '' ;
47022
47023         for ( var o in this.fckconfig ) {
47024             sConfig += sConfig.length > 0  ? '&amp;' : '';
47025             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
47026         }
47027
47028         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
47029     },
47030     
47031     
47032     _getIFrameHtml : function()
47033     {
47034         var sFile = 'fckeditor.html' ;
47035         /* no idea what this is about..
47036         try
47037         {
47038             if ( (/fcksource=true/i).test( window.top.location.search ) )
47039                 sFile = 'fckeditor.original.html' ;
47040         }
47041         catch (e) { 
47042         */
47043
47044         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
47045         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
47046         
47047         
47048         var html = '<iframe id="' + this.getId() +
47049             '___Frame" src="' + sLink +
47050             '" width="' + this.width +
47051             '" height="' + this.height + '"' +
47052             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
47053             ' frameborder="0" scrolling="no"></iframe>' ;
47054
47055         return html ;
47056     },
47057     
47058     _insertHtmlBefore : function( html, element )
47059     {
47060         if ( element.insertAdjacentHTML )       {
47061             // IE
47062             element.insertAdjacentHTML( 'beforeBegin', html ) ;
47063         } else { // Gecko
47064             var oRange = document.createRange() ;
47065             oRange.setStartBefore( element ) ;
47066             var oFragment = oRange.createContextualFragment( html );
47067             element.parentNode.insertBefore( oFragment, element ) ;
47068         }
47069     }
47070     
47071     
47072   
47073     
47074     
47075     
47076     
47077
47078 });
47079
47080 //Roo.reg('fckeditor', Roo.form.FCKeditor);
47081
47082 function FCKeditor_OnComplete(editorInstance){
47083     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
47084     f.fckEditor = editorInstance;
47085     //console.log("loaded");
47086     f.fireEvent('editorinit', f, editorInstance);
47087
47088   
47089
47090  
47091
47092
47093
47094
47095
47096
47097
47098
47099
47100
47101
47102
47103
47104
47105
47106 //<script type="text/javascript">
47107 /**
47108  * @class Roo.form.GridField
47109  * @extends Roo.form.Field
47110  * Embed a grid (or editable grid into a form)
47111  * STATUS ALPHA
47112  * 
47113  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
47114  * it needs 
47115  * xgrid.store = Roo.data.Store
47116  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
47117  * xgrid.store.reader = Roo.data.JsonReader 
47118  * 
47119  * 
47120  * @constructor
47121  * Creates a new GridField
47122  * @param {Object} config Configuration options
47123  */
47124 Roo.form.GridField = function(config){
47125     Roo.form.GridField.superclass.constructor.call(this, config);
47126      
47127 };
47128
47129 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
47130     /**
47131      * @cfg {Number} width  - used to restrict width of grid..
47132      */
47133     width : 100,
47134     /**
47135      * @cfg {Number} height - used to restrict height of grid..
47136      */
47137     height : 50,
47138      /**
47139      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
47140          * 
47141          *}
47142      */
47143     xgrid : false, 
47144     /**
47145      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47146      * {tag: "input", type: "checkbox", autocomplete: "off"})
47147      */
47148    // defaultAutoCreate : { tag: 'div' },
47149     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
47150     /**
47151      * @cfg {String} addTitle Text to include for adding a title.
47152      */
47153     addTitle : false,
47154     //
47155     onResize : function(){
47156         Roo.form.Field.superclass.onResize.apply(this, arguments);
47157     },
47158
47159     initEvents : function(){
47160         // Roo.form.Checkbox.superclass.initEvents.call(this);
47161         // has no events...
47162        
47163     },
47164
47165
47166     getResizeEl : function(){
47167         return this.wrap;
47168     },
47169
47170     getPositionEl : function(){
47171         return this.wrap;
47172     },
47173
47174     // private
47175     onRender : function(ct, position){
47176         
47177         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
47178         var style = this.style;
47179         delete this.style;
47180         
47181         Roo.form.GridField.superclass.onRender.call(this, ct, position);
47182         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
47183         this.viewEl = this.wrap.createChild({ tag: 'div' });
47184         if (style) {
47185             this.viewEl.applyStyles(style);
47186         }
47187         if (this.width) {
47188             this.viewEl.setWidth(this.width);
47189         }
47190         if (this.height) {
47191             this.viewEl.setHeight(this.height);
47192         }
47193         //if(this.inputValue !== undefined){
47194         //this.setValue(this.value);
47195         
47196         
47197         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
47198         
47199         
47200         this.grid.render();
47201         this.grid.getDataSource().on('remove', this.refreshValue, this);
47202         this.grid.getDataSource().on('update', this.refreshValue, this);
47203         this.grid.on('afteredit', this.refreshValue, this);
47204  
47205     },
47206      
47207     
47208     /**
47209      * Sets the value of the item. 
47210      * @param {String} either an object  or a string..
47211      */
47212     setValue : function(v){
47213         //this.value = v;
47214         v = v || []; // empty set..
47215         // this does not seem smart - it really only affects memoryproxy grids..
47216         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
47217             var ds = this.grid.getDataSource();
47218             // assumes a json reader..
47219             var data = {}
47220             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
47221             ds.loadData( data);
47222         }
47223         // clear selection so it does not get stale.
47224         if (this.grid.sm) { 
47225             this.grid.sm.clearSelections();
47226         }
47227         
47228         Roo.form.GridField.superclass.setValue.call(this, v);
47229         this.refreshValue();
47230         // should load data in the grid really....
47231     },
47232     
47233     // private
47234     refreshValue: function() {
47235          var val = [];
47236         this.grid.getDataSource().each(function(r) {
47237             val.push(r.data);
47238         });
47239         this.el.dom.value = Roo.encode(val);
47240     }
47241     
47242      
47243     
47244     
47245 });/*
47246  * Based on:
47247  * Ext JS Library 1.1.1
47248  * Copyright(c) 2006-2007, Ext JS, LLC.
47249  *
47250  * Originally Released Under LGPL - original licence link has changed is not relivant.
47251  *
47252  * Fork - LGPL
47253  * <script type="text/javascript">
47254  */
47255 /**
47256  * @class Roo.form.DisplayField
47257  * @extends Roo.form.Field
47258  * A generic Field to display non-editable data.
47259  * @constructor
47260  * Creates a new Display Field item.
47261  * @param {Object} config Configuration options
47262  */
47263 Roo.form.DisplayField = function(config){
47264     Roo.form.DisplayField.superclass.constructor.call(this, config);
47265     
47266 };
47267
47268 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
47269     inputType:      'hidden',
47270     allowBlank:     true,
47271     readOnly:         true,
47272     
47273  
47274     /**
47275      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47276      */
47277     focusClass : undefined,
47278     /**
47279      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47280      */
47281     fieldClass: 'x-form-field',
47282     
47283      /**
47284      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
47285      */
47286     valueRenderer: undefined,
47287     
47288     width: 100,
47289     /**
47290      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47291      * {tag: "input", type: "checkbox", autocomplete: "off"})
47292      */
47293      
47294  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
47295
47296     onResize : function(){
47297         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
47298         
47299     },
47300
47301     initEvents : function(){
47302         // Roo.form.Checkbox.superclass.initEvents.call(this);
47303         // has no events...
47304        
47305     },
47306
47307
47308     getResizeEl : function(){
47309         return this.wrap;
47310     },
47311
47312     getPositionEl : function(){
47313         return this.wrap;
47314     },
47315
47316     // private
47317     onRender : function(ct, position){
47318         
47319         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
47320         //if(this.inputValue !== undefined){
47321         this.wrap = this.el.wrap();
47322         
47323         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
47324         
47325         if (this.bodyStyle) {
47326             this.viewEl.applyStyles(this.bodyStyle);
47327         }
47328         //this.viewEl.setStyle('padding', '2px');
47329         
47330         this.setValue(this.value);
47331         
47332     },
47333 /*
47334     // private
47335     initValue : Roo.emptyFn,
47336
47337   */
47338
47339         // private
47340     onClick : function(){
47341         
47342     },
47343
47344     /**
47345      * Sets the checked state of the checkbox.
47346      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
47347      */
47348     setValue : function(v){
47349         this.value = v;
47350         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
47351         // this might be called before we have a dom element..
47352         if (!this.viewEl) {
47353             return;
47354         }
47355         this.viewEl.dom.innerHTML = html;
47356         Roo.form.DisplayField.superclass.setValue.call(this, v);
47357
47358     }
47359 });/*
47360  * 
47361  * Licence- LGPL
47362  * 
47363  */
47364
47365 /**
47366  * @class Roo.form.DayPicker
47367  * @extends Roo.form.Field
47368  * A Day picker show [M] [T] [W] ....
47369  * @constructor
47370  * Creates a new Day Picker
47371  * @param {Object} config Configuration options
47372  */
47373 Roo.form.DayPicker= function(config){
47374     Roo.form.DayPicker.superclass.constructor.call(this, config);
47375      
47376 };
47377
47378 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
47379     /**
47380      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
47381      */
47382     focusClass : undefined,
47383     /**
47384      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
47385      */
47386     fieldClass: "x-form-field",
47387    
47388     /**
47389      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
47390      * {tag: "input", type: "checkbox", autocomplete: "off"})
47391      */
47392     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
47393     
47394    
47395     actionMode : 'viewEl', 
47396     //
47397     // private
47398  
47399     inputType : 'hidden',
47400     
47401      
47402     inputElement: false, // real input element?
47403     basedOn: false, // ????
47404     
47405     isFormField: true, // not sure where this is needed!!!!
47406
47407     onResize : function(){
47408         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47409         if(!this.boxLabel){
47410             this.el.alignTo(this.wrap, 'c-c');
47411         }
47412     },
47413
47414     initEvents : function(){
47415         Roo.form.Checkbox.superclass.initEvents.call(this);
47416         this.el.on("click", this.onClick,  this);
47417         this.el.on("change", this.onClick,  this);
47418     },
47419
47420
47421     getResizeEl : function(){
47422         return this.wrap;
47423     },
47424
47425     getPositionEl : function(){
47426         return this.wrap;
47427     },
47428
47429     
47430     // private
47431     onRender : function(ct, position){
47432         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47433        
47434         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47435         
47436         var r1 = '<table><tr>';
47437         var r2 = '<tr class="x-form-daypick-icons">';
47438         for (var i=0; i < 7; i++) {
47439             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47440             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47441         }
47442         
47443         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47444         viewEl.select('img').on('click', this.onClick, this);
47445         this.viewEl = viewEl;   
47446         
47447         
47448         // this will not work on Chrome!!!
47449         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47450         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47451         
47452         
47453           
47454
47455     },
47456
47457     // private
47458     initValue : Roo.emptyFn,
47459
47460     /**
47461      * Returns the checked state of the checkbox.
47462      * @return {Boolean} True if checked, else false
47463      */
47464     getValue : function(){
47465         return this.el.dom.value;
47466         
47467     },
47468
47469         // private
47470     onClick : function(e){ 
47471         //this.setChecked(!this.checked);
47472         Roo.get(e.target).toggleClass('x-menu-item-checked');
47473         this.refreshValue();
47474         //if(this.el.dom.checked != this.checked){
47475         //    this.setValue(this.el.dom.checked);
47476        // }
47477     },
47478     
47479     // private
47480     refreshValue : function()
47481     {
47482         var val = '';
47483         this.viewEl.select('img',true).each(function(e,i,n)  {
47484             val += e.is(".x-menu-item-checked") ? String(n) : '';
47485         });
47486         this.setValue(val, true);
47487     },
47488
47489     /**
47490      * Sets the checked state of the checkbox.
47491      * On is always based on a string comparison between inputValue and the param.
47492      * @param {Boolean/String} value - the value to set 
47493      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47494      */
47495     setValue : function(v,suppressEvent){
47496         if (!this.el.dom) {
47497             return;
47498         }
47499         var old = this.el.dom.value ;
47500         this.el.dom.value = v;
47501         if (suppressEvent) {
47502             return ;
47503         }
47504          
47505         // update display..
47506         this.viewEl.select('img',true).each(function(e,i,n)  {
47507             
47508             var on = e.is(".x-menu-item-checked");
47509             var newv = v.indexOf(String(n)) > -1;
47510             if (on != newv) {
47511                 e.toggleClass('x-menu-item-checked');
47512             }
47513             
47514         });
47515         
47516         
47517         this.fireEvent('change', this, v, old);
47518         
47519         
47520     },
47521    
47522     // handle setting of hidden value by some other method!!?!?
47523     setFromHidden: function()
47524     {
47525         if(!this.el){
47526             return;
47527         }
47528         //console.log("SET FROM HIDDEN");
47529         //alert('setFrom hidden');
47530         this.setValue(this.el.dom.value);
47531     },
47532     
47533     onDestroy : function()
47534     {
47535         if(this.viewEl){
47536             Roo.get(this.viewEl).remove();
47537         }
47538          
47539         Roo.form.DayPicker.superclass.onDestroy.call(this);
47540     }
47541
47542 });/*
47543  * RooJS Library 1.1.1
47544  * Copyright(c) 2008-2011  Alan Knowles
47545  *
47546  * License - LGPL
47547  */
47548  
47549
47550 /**
47551  * @class Roo.form.ComboCheck
47552  * @extends Roo.form.ComboBox
47553  * A combobox for multiple select items.
47554  *
47555  * FIXME - could do with a reset button..
47556  * 
47557  * @constructor
47558  * Create a new ComboCheck
47559  * @param {Object} config Configuration options
47560  */
47561 Roo.form.ComboCheck = function(config){
47562     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47563     // should verify some data...
47564     // like
47565     // hiddenName = required..
47566     // displayField = required
47567     // valudField == required
47568     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47569     var _t = this;
47570     Roo.each(req, function(e) {
47571         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47572             throw "Roo.form.ComboCheck : missing value for: " + e;
47573         }
47574     });
47575     
47576     
47577 };
47578
47579 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47580      
47581      
47582     editable : false,
47583      
47584     selectedClass: 'x-menu-item-checked', 
47585     
47586     // private
47587     onRender : function(ct, position){
47588         var _t = this;
47589         
47590         
47591         
47592         if(!this.tpl){
47593             var cls = 'x-combo-list';
47594
47595             
47596             this.tpl =  new Roo.Template({
47597                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47598                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47599                    '<span>{' + this.displayField + '}</span>' +
47600                     '</div>' 
47601                 
47602             });
47603         }
47604  
47605         
47606         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47607         this.view.singleSelect = false;
47608         this.view.multiSelect = true;
47609         this.view.toggleSelect = true;
47610         this.pageTb.add(new Roo.Toolbar.Fill(), {
47611             
47612             text: 'Done',
47613             handler: function()
47614             {
47615                 _t.collapse();
47616             }
47617         });
47618     },
47619     
47620     onViewOver : function(e, t){
47621         // do nothing...
47622         return;
47623         
47624     },
47625     
47626     onViewClick : function(doFocus,index){
47627         return;
47628         
47629     },
47630     select: function () {
47631         //Roo.log("SELECT CALLED");
47632     },
47633      
47634     selectByValue : function(xv, scrollIntoView){
47635         var ar = this.getValueArray();
47636         var sels = [];
47637         
47638         Roo.each(ar, function(v) {
47639             if(v === undefined || v === null){
47640                 return;
47641             }
47642             var r = this.findRecord(this.valueField, v);
47643             if(r){
47644                 sels.push(this.store.indexOf(r))
47645                 
47646             }
47647         },this);
47648         this.view.select(sels);
47649         return false;
47650     },
47651     
47652     
47653     
47654     onSelect : function(record, index){
47655        // Roo.log("onselect Called");
47656        // this is only called by the clear button now..
47657         this.view.clearSelections();
47658         this.setValue('[]');
47659         if (this.value != this.valueBefore) {
47660             this.fireEvent('change', this, this.value, this.valueBefore);
47661             this.valueBefore = this.value;
47662         }
47663     },
47664     getValueArray : function()
47665     {
47666         var ar = [] ;
47667         
47668         try {
47669             //Roo.log(this.value);
47670             if (typeof(this.value) == 'undefined') {
47671                 return [];
47672             }
47673             var ar = Roo.decode(this.value);
47674             return  ar instanceof Array ? ar : []; //?? valid?
47675             
47676         } catch(e) {
47677             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47678             return [];
47679         }
47680          
47681     },
47682     expand : function ()
47683     {
47684         
47685         Roo.form.ComboCheck.superclass.expand.call(this);
47686         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47687         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47688         
47689
47690     },
47691     
47692     collapse : function(){
47693         Roo.form.ComboCheck.superclass.collapse.call(this);
47694         var sl = this.view.getSelectedIndexes();
47695         var st = this.store;
47696         var nv = [];
47697         var tv = [];
47698         var r;
47699         Roo.each(sl, function(i) {
47700             r = st.getAt(i);
47701             nv.push(r.get(this.valueField));
47702         },this);
47703         this.setValue(Roo.encode(nv));
47704         if (this.value != this.valueBefore) {
47705
47706             this.fireEvent('change', this, this.value, this.valueBefore);
47707             this.valueBefore = this.value;
47708         }
47709         
47710     },
47711     
47712     setValue : function(v){
47713         // Roo.log(v);
47714         this.value = v;
47715         
47716         var vals = this.getValueArray();
47717         var tv = [];
47718         Roo.each(vals, function(k) {
47719             var r = this.findRecord(this.valueField, k);
47720             if(r){
47721                 tv.push(r.data[this.displayField]);
47722             }else if(this.valueNotFoundText !== undefined){
47723                 tv.push( this.valueNotFoundText );
47724             }
47725         },this);
47726        // Roo.log(tv);
47727         
47728         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47729         this.hiddenField.value = v;
47730         this.value = v;
47731     }
47732     
47733 });/*
47734  * Based on:
47735  * Ext JS Library 1.1.1
47736  * Copyright(c) 2006-2007, Ext JS, LLC.
47737  *
47738  * Originally Released Under LGPL - original licence link has changed is not relivant.
47739  *
47740  * Fork - LGPL
47741  * <script type="text/javascript">
47742  */
47743  
47744 /**
47745  * @class Roo.form.Signature
47746  * @extends Roo.form.Field
47747  * Signature field.  
47748  * @constructor
47749  * 
47750  * @param {Object} config Configuration options
47751  */
47752
47753 Roo.form.Signature = function(config){
47754     Roo.form.Signature.superclass.constructor.call(this, config);
47755     
47756     this.addEvents({// not in used??
47757          /**
47758          * @event confirm
47759          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47760              * @param {Roo.form.Signature} combo This combo box
47761              */
47762         'confirm' : true,
47763         /**
47764          * @event reset
47765          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47766              * @param {Roo.form.ComboBox} combo This combo box
47767              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47768              */
47769         'reset' : true
47770     });
47771 };
47772
47773 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47774     /**
47775      * @cfg {Object} labels Label to use when rendering a form.
47776      * defaults to 
47777      * labels : { 
47778      *      clear : "Clear",
47779      *      confirm : "Confirm"
47780      *  }
47781      */
47782     labels : { 
47783         clear : "Clear",
47784         confirm : "Confirm"
47785     },
47786     /**
47787      * @cfg {Number} width The signature panel width (defaults to 300)
47788      */
47789     width: 300,
47790     /**
47791      * @cfg {Number} height The signature panel height (defaults to 100)
47792      */
47793     height : 100,
47794     /**
47795      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47796      */
47797     allowBlank : false,
47798     
47799     //private
47800     // {Object} signPanel The signature SVG panel element (defaults to {})
47801     signPanel : {},
47802     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47803     isMouseDown : false,
47804     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47805     isConfirmed : false,
47806     // {String} signatureTmp SVG mapping string (defaults to empty string)
47807     signatureTmp : '',
47808     
47809     
47810     defaultAutoCreate : { // modified by initCompnoent..
47811         tag: "input",
47812         type:"hidden"
47813     },
47814
47815     // private
47816     onRender : function(ct, position){
47817         
47818         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47819         
47820         this.wrap = this.el.wrap({
47821             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47822         });
47823         
47824         this.createToolbar(this);
47825         this.signPanel = this.wrap.createChild({
47826                 tag: 'div',
47827                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47828             }, this.el
47829         );
47830             
47831         this.svgID = Roo.id();
47832         this.svgEl = this.signPanel.createChild({
47833               xmlns : 'http://www.w3.org/2000/svg',
47834               tag : 'svg',
47835               id : this.svgID + "-svg",
47836               width: this.width,
47837               height: this.height,
47838               viewBox: '0 0 '+this.width+' '+this.height,
47839               cn : [
47840                 {
47841                     tag: "rect",
47842                     id: this.svgID + "-svg-r",
47843                     width: this.width,
47844                     height: this.height,
47845                     fill: "#ffa"
47846                 },
47847                 {
47848                     tag: "line",
47849                     id: this.svgID + "-svg-l",
47850                     x1: "0", // start
47851                     y1: (this.height*0.8), // start set the line in 80% of height
47852                     x2: this.width, // end
47853                     y2: (this.height*0.8), // end set the line in 80% of height
47854                     'stroke': "#666",
47855                     'stroke-width': "1",
47856                     'stroke-dasharray': "3",
47857                     'shape-rendering': "crispEdges",
47858                     'pointer-events': "none"
47859                 },
47860                 {
47861                     tag: "path",
47862                     id: this.svgID + "-svg-p",
47863                     'stroke': "navy",
47864                     'stroke-width': "3",
47865                     'fill': "none",
47866                     'pointer-events': 'none'
47867                 }
47868               ]
47869         });
47870         this.createSVG();
47871         this.svgBox = this.svgEl.dom.getScreenCTM();
47872     },
47873     createSVG : function(){ 
47874         var svg = this.signPanel;
47875         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47876         var t = this;
47877
47878         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47879         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47880         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47881         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47882         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47883         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47884         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47885         
47886     },
47887     isTouchEvent : function(e){
47888         return e.type.match(/^touch/);
47889     },
47890     getCoords : function (e) {
47891         var pt    = this.svgEl.dom.createSVGPoint();
47892         pt.x = e.clientX; 
47893         pt.y = e.clientY;
47894         if (this.isTouchEvent(e)) {
47895             pt.x =  e.targetTouches[0].clientX 
47896             pt.y = e.targetTouches[0].clientY;
47897         }
47898         var a = this.svgEl.dom.getScreenCTM();
47899         var b = a.inverse();
47900         var mx = pt.matrixTransform(b);
47901         return mx.x + ',' + mx.y;
47902     },
47903     //mouse event headler 
47904     down : function (e) {
47905         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47906         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47907         
47908         this.isMouseDown = true;
47909         
47910         e.preventDefault();
47911     },
47912     move : function (e) {
47913         if (this.isMouseDown) {
47914             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47915             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47916         }
47917         
47918         e.preventDefault();
47919     },
47920     up : function (e) {
47921         this.isMouseDown = false;
47922         var sp = this.signatureTmp.split(' ');
47923         
47924         if(sp.length > 1){
47925             if(!sp[sp.length-2].match(/^L/)){
47926                 sp.pop();
47927                 sp.pop();
47928                 sp.push("");
47929                 this.signatureTmp = sp.join(" ");
47930             }
47931         }
47932         if(this.getValue() != this.signatureTmp){
47933             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47934             this.isConfirmed = false;
47935         }
47936         e.preventDefault();
47937     },
47938     
47939     /**
47940      * Protected method that will not generally be called directly. It
47941      * is called when the editor creates its toolbar. Override this method if you need to
47942      * add custom toolbar buttons.
47943      * @param {HtmlEditor} editor
47944      */
47945     createToolbar : function(editor){
47946          function btn(id, toggle, handler){
47947             var xid = fid + '-'+ id ;
47948             return {
47949                 id : xid,
47950                 cmd : id,
47951                 cls : 'x-btn-icon x-edit-'+id,
47952                 enableToggle:toggle !== false,
47953                 scope: editor, // was editor...
47954                 handler:handler||editor.relayBtnCmd,
47955                 clickEvent:'mousedown',
47956                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47957                 tabIndex:-1
47958             };
47959         }
47960         
47961         
47962         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47963         this.tb = tb;
47964         this.tb.add(
47965            {
47966                 cls : ' x-signature-btn x-signature-'+id,
47967                 scope: editor, // was editor...
47968                 handler: this.reset,
47969                 clickEvent:'mousedown',
47970                 text: this.labels.clear
47971             },
47972             {
47973                  xtype : 'Fill',
47974                  xns: Roo.Toolbar
47975             }, 
47976             {
47977                 cls : '  x-signature-btn x-signature-'+id,
47978                 scope: editor, // was editor...
47979                 handler: this.confirmHandler,
47980                 clickEvent:'mousedown',
47981                 text: this.labels.confirm
47982             }
47983         );
47984     
47985     },
47986     //public
47987     /**
47988      * when user is clicked confirm then show this image.....
47989      * 
47990      * @return {String} Image Data URI
47991      */
47992     getImageDataURI : function(){
47993         var svg = this.svgEl.dom.parentNode.innerHTML;
47994         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47995         return src; 
47996     },
47997     /**
47998      * 
47999      * @return {Boolean} this.isConfirmed
48000      */
48001     getConfirmed : function(){
48002         return this.isConfirmed;
48003     },
48004     /**
48005      * 
48006      * @return {Number} this.width
48007      */
48008     getWidth : function(){
48009         return this.width;
48010     },
48011     /**
48012      * 
48013      * @return {Number} this.height
48014      */
48015     getHeight : function(){
48016         return this.height;
48017     },
48018     // private
48019     getSignature : function(){
48020         return this.signatureTmp;
48021     },
48022     // private
48023     reset : function(){
48024         this.signatureTmp = '';
48025         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48026         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
48027         this.isConfirmed = false;
48028         Roo.form.Signature.superclass.reset.call(this);
48029     },
48030     setSignature : function(s){
48031         this.signatureTmp = s;
48032         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
48033         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
48034         this.setValue(s);
48035         this.isConfirmed = false;
48036         Roo.form.Signature.superclass.reset.call(this);
48037     }, 
48038     test : function(){
48039 //        Roo.log(this.signPanel.dom.contentWindow.up())
48040     },
48041     //private
48042     setConfirmed : function(){
48043         
48044         
48045         
48046 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
48047     },
48048     // private
48049     confirmHandler : function(){
48050         if(!this.getSignature()){
48051             return;
48052         }
48053         
48054         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
48055         this.setValue(this.getSignature());
48056         this.isConfirmed = true;
48057         
48058         this.fireEvent('confirm', this);
48059     },
48060     // private
48061     // Subclasses should provide the validation implementation by overriding this
48062     validateValue : function(value){
48063         if(this.allowBlank){
48064             return true;
48065         }
48066         
48067         if(this.isConfirmed){
48068             return true;
48069         }
48070         return false;
48071     }
48072 });/*
48073  * Based on:
48074  * Ext JS Library 1.1.1
48075  * Copyright(c) 2006-2007, Ext JS, LLC.
48076  *
48077  * Originally Released Under LGPL - original licence link has changed is not relivant.
48078  *
48079  * Fork - LGPL
48080  * <script type="text/javascript">
48081  */
48082  
48083
48084 /**
48085  * @class Roo.form.ComboBox
48086  * @extends Roo.form.TriggerField
48087  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
48088  * @constructor
48089  * Create a new ComboBox.
48090  * @param {Object} config Configuration options
48091  */
48092 Roo.form.Select = function(config){
48093     Roo.form.Select.superclass.constructor.call(this, config);
48094      
48095 };
48096
48097 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
48098     /**
48099      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
48100      */
48101     /**
48102      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
48103      * rendering into an Roo.Editor, defaults to false)
48104      */
48105     /**
48106      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
48107      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
48108      */
48109     /**
48110      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
48111      */
48112     /**
48113      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
48114      * the dropdown list (defaults to undefined, with no header element)
48115      */
48116
48117      /**
48118      * @cfg {String/Roo.Template} tpl The template to use to render the output
48119      */
48120      
48121     // private
48122     defaultAutoCreate : {tag: "select"  },
48123     /**
48124      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
48125      */
48126     listWidth: undefined,
48127     /**
48128      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
48129      * mode = 'remote' or 'text' if mode = 'local')
48130      */
48131     displayField: undefined,
48132     /**
48133      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
48134      * mode = 'remote' or 'value' if mode = 'local'). 
48135      * Note: use of a valueField requires the user make a selection
48136      * in order for a value to be mapped.
48137      */
48138     valueField: undefined,
48139     
48140     
48141     /**
48142      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
48143      * field's data value (defaults to the underlying DOM element's name)
48144      */
48145     hiddenName: undefined,
48146     /**
48147      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
48148      */
48149     listClass: '',
48150     /**
48151      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
48152      */
48153     selectedClass: 'x-combo-selected',
48154     /**
48155      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
48156      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
48157      * which displays a downward arrow icon).
48158      */
48159     triggerClass : 'x-form-arrow-trigger',
48160     /**
48161      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
48162      */
48163     shadow:'sides',
48164     /**
48165      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
48166      * anchor positions (defaults to 'tl-bl')
48167      */
48168     listAlign: 'tl-bl?',
48169     /**
48170      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
48171      */
48172     maxHeight: 300,
48173     /**
48174      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
48175      * query specified by the allQuery config option (defaults to 'query')
48176      */
48177     triggerAction: 'query',
48178     /**
48179      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
48180      * (defaults to 4, does not apply if editable = false)
48181      */
48182     minChars : 4,
48183     /**
48184      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
48185      * delay (typeAheadDelay) if it matches a known value (defaults to false)
48186      */
48187     typeAhead: false,
48188     /**
48189      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
48190      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
48191      */
48192     queryDelay: 500,
48193     /**
48194      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
48195      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
48196      */
48197     pageSize: 0,
48198     /**
48199      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
48200      * when editable = true (defaults to false)
48201      */
48202     selectOnFocus:false,
48203     /**
48204      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
48205      */
48206     queryParam: 'query',
48207     /**
48208      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
48209      * when mode = 'remote' (defaults to 'Loading...')
48210      */
48211     loadingText: 'Loading...',
48212     /**
48213      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
48214      */
48215     resizable: false,
48216     /**
48217      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
48218      */
48219     handleHeight : 8,
48220     /**
48221      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
48222      * traditional select (defaults to true)
48223      */
48224     editable: true,
48225     /**
48226      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
48227      */
48228     allQuery: '',
48229     /**
48230      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
48231      */
48232     mode: 'remote',
48233     /**
48234      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
48235      * listWidth has a higher value)
48236      */
48237     minListWidth : 70,
48238     /**
48239      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
48240      * allow the user to set arbitrary text into the field (defaults to false)
48241      */
48242     forceSelection:false,
48243     /**
48244      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
48245      * if typeAhead = true (defaults to 250)
48246      */
48247     typeAheadDelay : 250,
48248     /**
48249      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
48250      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
48251      */
48252     valueNotFoundText : undefined,
48253     
48254     /**
48255      * @cfg {String} defaultValue The value displayed after loading the store.
48256      */
48257     defaultValue: '',
48258     
48259     /**
48260      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
48261      */
48262     blockFocus : false,
48263     
48264     /**
48265      * @cfg {Boolean} disableClear Disable showing of clear button.
48266      */
48267     disableClear : false,
48268     /**
48269      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
48270      */
48271     alwaysQuery : false,
48272     
48273     //private
48274     addicon : false,
48275     editicon: false,
48276     
48277     // element that contains real text value.. (when hidden is used..)
48278      
48279     // private
48280     onRender : function(ct, position){
48281         Roo.form.Field.prototype.onRender.call(this, ct, position);
48282         
48283         if(this.store){
48284             this.store.on('beforeload', this.onBeforeLoad, this);
48285             this.store.on('load', this.onLoad, this);
48286             this.store.on('loadexception', this.onLoadException, this);
48287             this.store.load({});
48288         }
48289         
48290         
48291         
48292     },
48293
48294     // private
48295     initEvents : function(){
48296         //Roo.form.ComboBox.superclass.initEvents.call(this);
48297  
48298     },
48299
48300     onDestroy : function(){
48301        
48302         if(this.store){
48303             this.store.un('beforeload', this.onBeforeLoad, this);
48304             this.store.un('load', this.onLoad, this);
48305             this.store.un('loadexception', this.onLoadException, this);
48306         }
48307         //Roo.form.ComboBox.superclass.onDestroy.call(this);
48308     },
48309
48310     // private
48311     fireKey : function(e){
48312         if(e.isNavKeyPress() && !this.list.isVisible()){
48313             this.fireEvent("specialkey", this, e);
48314         }
48315     },
48316
48317     // private
48318     onResize: function(w, h){
48319         
48320         return; 
48321     
48322         
48323     },
48324
48325     /**
48326      * Allow or prevent the user from directly editing the field text.  If false is passed,
48327      * the user will only be able to select from the items defined in the dropdown list.  This method
48328      * is the runtime equivalent of setting the 'editable' config option at config time.
48329      * @param {Boolean} value True to allow the user to directly edit the field text
48330      */
48331     setEditable : function(value){
48332          
48333     },
48334
48335     // private
48336     onBeforeLoad : function(){
48337         
48338         Roo.log("Select before load");
48339         return;
48340     
48341         this.innerList.update(this.loadingText ?
48342                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
48343         //this.restrictHeight();
48344         this.selectedIndex = -1;
48345     },
48346
48347     // private
48348     onLoad : function(){
48349
48350     
48351         var dom = this.el.dom;
48352         dom.innerHTML = '';
48353          var od = dom.ownerDocument;
48354          
48355         if (this.emptyText) {
48356             var op = od.createElement('option');
48357             op.setAttribute('value', '');
48358             op.innerHTML = String.format('{0}', this.emptyText);
48359             dom.appendChild(op);
48360         }
48361         if(this.store.getCount() > 0){
48362            
48363             var vf = this.valueField;
48364             var df = this.displayField;
48365             this.store.data.each(function(r) {
48366                 // which colmsn to use... testing - cdoe / title..
48367                 var op = od.createElement('option');
48368                 op.setAttribute('value', r.data[vf]);
48369                 op.innerHTML = String.format('{0}', r.data[df]);
48370                 dom.appendChild(op);
48371             });
48372             if (typeof(this.defaultValue != 'undefined')) {
48373                 this.setValue(this.defaultValue);
48374             }
48375             
48376              
48377         }else{
48378             //this.onEmptyResults();
48379         }
48380         //this.el.focus();
48381     },
48382     // private
48383     onLoadException : function()
48384     {
48385         dom.innerHTML = '';
48386             
48387         Roo.log("Select on load exception");
48388         return;
48389     
48390         this.collapse();
48391         Roo.log(this.store.reader.jsonData);
48392         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48393             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48394         }
48395         
48396         
48397     },
48398     // private
48399     onTypeAhead : function(){
48400          
48401     },
48402
48403     // private
48404     onSelect : function(record, index){
48405         Roo.log('on select?');
48406         return;
48407         if(this.fireEvent('beforeselect', this, record, index) !== false){
48408             this.setFromData(index > -1 ? record.data : false);
48409             this.collapse();
48410             this.fireEvent('select', this, record, index);
48411         }
48412     },
48413
48414     /**
48415      * Returns the currently selected field value or empty string if no value is set.
48416      * @return {String} value The selected value
48417      */
48418     getValue : function(){
48419         var dom = this.el.dom;
48420         this.value = dom.options[dom.selectedIndex].value;
48421         return this.value;
48422         
48423     },
48424
48425     /**
48426      * Clears any text/value currently set in the field
48427      */
48428     clearValue : function(){
48429         this.value = '';
48430         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48431         
48432     },
48433
48434     /**
48435      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48436      * will be displayed in the field.  If the value does not match the data value of an existing item,
48437      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48438      * Otherwise the field will be blank (although the value will still be set).
48439      * @param {String} value The value to match
48440      */
48441     setValue : function(v){
48442         var d = this.el.dom;
48443         for (var i =0; i < d.options.length;i++) {
48444             if (v == d.options[i].value) {
48445                 d.selectedIndex = i;
48446                 this.value = v;
48447                 return;
48448             }
48449         }
48450         this.clearValue();
48451     },
48452     /**
48453      * @property {Object} the last set data for the element
48454      */
48455     
48456     lastData : false,
48457     /**
48458      * Sets the value of the field based on a object which is related to the record format for the store.
48459      * @param {Object} value the value to set as. or false on reset?
48460      */
48461     setFromData : function(o){
48462         Roo.log('setfrom data?');
48463          
48464         
48465         
48466     },
48467     // private
48468     reset : function(){
48469         this.clearValue();
48470     },
48471     // private
48472     findRecord : function(prop, value){
48473         
48474         return false;
48475     
48476         var record;
48477         if(this.store.getCount() > 0){
48478             this.store.each(function(r){
48479                 if(r.data[prop] == value){
48480                     record = r;
48481                     return false;
48482                 }
48483                 return true;
48484             });
48485         }
48486         return record;
48487     },
48488     
48489     getName: function()
48490     {
48491         // returns hidden if it's set..
48492         if (!this.rendered) {return ''};
48493         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48494         
48495     },
48496      
48497
48498     
48499
48500     // private
48501     onEmptyResults : function(){
48502         Roo.log('empty results');
48503         //this.collapse();
48504     },
48505
48506     /**
48507      * Returns true if the dropdown list is expanded, else false.
48508      */
48509     isExpanded : function(){
48510         return false;
48511     },
48512
48513     /**
48514      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48515      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48516      * @param {String} value The data value of the item to select
48517      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48518      * selected item if it is not currently in view (defaults to true)
48519      * @return {Boolean} True if the value matched an item in the list, else false
48520      */
48521     selectByValue : function(v, scrollIntoView){
48522         Roo.log('select By Value');
48523         return false;
48524     
48525         if(v !== undefined && v !== null){
48526             var r = this.findRecord(this.valueField || this.displayField, v);
48527             if(r){
48528                 this.select(this.store.indexOf(r), scrollIntoView);
48529                 return true;
48530             }
48531         }
48532         return false;
48533     },
48534
48535     /**
48536      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48537      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48538      * @param {Number} index The zero-based index of the list item to select
48539      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48540      * selected item if it is not currently in view (defaults to true)
48541      */
48542     select : function(index, scrollIntoView){
48543         Roo.log('select ');
48544         return  ;
48545         
48546         this.selectedIndex = index;
48547         this.view.select(index);
48548         if(scrollIntoView !== false){
48549             var el = this.view.getNode(index);
48550             if(el){
48551                 this.innerList.scrollChildIntoView(el, false);
48552             }
48553         }
48554     },
48555
48556       
48557
48558     // private
48559     validateBlur : function(){
48560         
48561         return;
48562         
48563     },
48564
48565     // private
48566     initQuery : function(){
48567         this.doQuery(this.getRawValue());
48568     },
48569
48570     // private
48571     doForce : function(){
48572         if(this.el.dom.value.length > 0){
48573             this.el.dom.value =
48574                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48575              
48576         }
48577     },
48578
48579     /**
48580      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48581      * query allowing the query action to be canceled if needed.
48582      * @param {String} query The SQL query to execute
48583      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48584      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48585      * saved in the current store (defaults to false)
48586      */
48587     doQuery : function(q, forceAll){
48588         
48589         Roo.log('doQuery?');
48590         if(q === undefined || q === null){
48591             q = '';
48592         }
48593         var qe = {
48594             query: q,
48595             forceAll: forceAll,
48596             combo: this,
48597             cancel:false
48598         };
48599         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48600             return false;
48601         }
48602         q = qe.query;
48603         forceAll = qe.forceAll;
48604         if(forceAll === true || (q.length >= this.minChars)){
48605             if(this.lastQuery != q || this.alwaysQuery){
48606                 this.lastQuery = q;
48607                 if(this.mode == 'local'){
48608                     this.selectedIndex = -1;
48609                     if(forceAll){
48610                         this.store.clearFilter();
48611                     }else{
48612                         this.store.filter(this.displayField, q);
48613                     }
48614                     this.onLoad();
48615                 }else{
48616                     this.store.baseParams[this.queryParam] = q;
48617                     this.store.load({
48618                         params: this.getParams(q)
48619                     });
48620                     this.expand();
48621                 }
48622             }else{
48623                 this.selectedIndex = -1;
48624                 this.onLoad();   
48625             }
48626         }
48627     },
48628
48629     // private
48630     getParams : function(q){
48631         var p = {};
48632         //p[this.queryParam] = q;
48633         if(this.pageSize){
48634             p.start = 0;
48635             p.limit = this.pageSize;
48636         }
48637         return p;
48638     },
48639
48640     /**
48641      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48642      */
48643     collapse : function(){
48644         
48645     },
48646
48647     // private
48648     collapseIf : function(e){
48649         
48650     },
48651
48652     /**
48653      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48654      */
48655     expand : function(){
48656         
48657     } ,
48658
48659     // private
48660      
48661
48662     /** 
48663     * @cfg {Boolean} grow 
48664     * @hide 
48665     */
48666     /** 
48667     * @cfg {Number} growMin 
48668     * @hide 
48669     */
48670     /** 
48671     * @cfg {Number} growMax 
48672     * @hide 
48673     */
48674     /**
48675      * @hide
48676      * @method autoSize
48677      */
48678     
48679     setWidth : function()
48680     {
48681         
48682     },
48683     getResizeEl : function(){
48684         return this.el;
48685     }
48686 });//<script type="text/javasscript">
48687  
48688
48689 /**
48690  * @class Roo.DDView
48691  * A DnD enabled version of Roo.View.
48692  * @param {Element/String} container The Element in which to create the View.
48693  * @param {String} tpl The template string used to create the markup for each element of the View
48694  * @param {Object} config The configuration properties. These include all the config options of
48695  * {@link Roo.View} plus some specific to this class.<br>
48696  * <p>
48697  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48698  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48699  * <p>
48700  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48701 .x-view-drag-insert-above {
48702         border-top:1px dotted #3366cc;
48703 }
48704 .x-view-drag-insert-below {
48705         border-bottom:1px dotted #3366cc;
48706 }
48707 </code></pre>
48708  * 
48709  */
48710  
48711 Roo.DDView = function(container, tpl, config) {
48712     Roo.DDView.superclass.constructor.apply(this, arguments);
48713     this.getEl().setStyle("outline", "0px none");
48714     this.getEl().unselectable();
48715     if (this.dragGroup) {
48716                 this.setDraggable(this.dragGroup.split(","));
48717     }
48718     if (this.dropGroup) {
48719                 this.setDroppable(this.dropGroup.split(","));
48720     }
48721     if (this.deletable) {
48722         this.setDeletable();
48723     }
48724     this.isDirtyFlag = false;
48725         this.addEvents({
48726                 "drop" : true
48727         });
48728 };
48729
48730 Roo.extend(Roo.DDView, Roo.View, {
48731 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48732 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48733 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48734 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48735
48736         isFormField: true,
48737
48738         reset: Roo.emptyFn,
48739         
48740         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48741
48742         validate: function() {
48743                 return true;
48744         },
48745         
48746         destroy: function() {
48747                 this.purgeListeners();
48748                 this.getEl.removeAllListeners();
48749                 this.getEl().remove();
48750                 if (this.dragZone) {
48751                         if (this.dragZone.destroy) {
48752                                 this.dragZone.destroy();
48753                         }
48754                 }
48755                 if (this.dropZone) {
48756                         if (this.dropZone.destroy) {
48757                                 this.dropZone.destroy();
48758                         }
48759                 }
48760         },
48761
48762 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48763         getName: function() {
48764                 return this.name;
48765         },
48766
48767 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48768         setValue: function(v) {
48769                 if (!this.store) {
48770                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48771                 }
48772                 var data = {};
48773                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48774                 this.store.proxy = new Roo.data.MemoryProxy(data);
48775                 this.store.load();
48776         },
48777
48778 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48779         getValue: function() {
48780                 var result = '(';
48781                 this.store.each(function(rec) {
48782                         result += rec.id + ',';
48783                 });
48784                 return result.substr(0, result.length - 1) + ')';
48785         },
48786         
48787         getIds: function() {
48788                 var i = 0, result = new Array(this.store.getCount());
48789                 this.store.each(function(rec) {
48790                         result[i++] = rec.id;
48791                 });
48792                 return result;
48793         },
48794         
48795         isDirty: function() {
48796                 return this.isDirtyFlag;
48797         },
48798
48799 /**
48800  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48801  *      whole Element becomes the target, and this causes the drop gesture to append.
48802  */
48803     getTargetFromEvent : function(e) {
48804                 var target = e.getTarget();
48805                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48806                 target = target.parentNode;
48807                 }
48808                 if (!target) {
48809                         target = this.el.dom.lastChild || this.el.dom;
48810                 }
48811                 return target;
48812     },
48813
48814 /**
48815  *      Create the drag data which consists of an object which has the property "ddel" as
48816  *      the drag proxy element. 
48817  */
48818     getDragData : function(e) {
48819         var target = this.findItemFromChild(e.getTarget());
48820                 if(target) {
48821                         this.handleSelection(e);
48822                         var selNodes = this.getSelectedNodes();
48823             var dragData = {
48824                 source: this,
48825                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48826                 nodes: selNodes,
48827                 records: []
48828                         };
48829                         var selectedIndices = this.getSelectedIndexes();
48830                         for (var i = 0; i < selectedIndices.length; i++) {
48831                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48832                         }
48833                         if (selNodes.length == 1) {
48834                                 dragData.ddel = target.cloneNode(true); // the div element
48835                         } else {
48836                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48837                                 div.className = 'multi-proxy';
48838                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48839                                         div.appendChild(selNodes[i].cloneNode(true));
48840                                 }
48841                                 dragData.ddel = div;
48842                         }
48843             //console.log(dragData)
48844             //console.log(dragData.ddel.innerHTML)
48845                         return dragData;
48846                 }
48847         //console.log('nodragData')
48848                 return false;
48849     },
48850     
48851 /**     Specify to which ddGroup items in this DDView may be dragged. */
48852     setDraggable: function(ddGroup) {
48853         if (ddGroup instanceof Array) {
48854                 Roo.each(ddGroup, this.setDraggable, this);
48855                 return;
48856         }
48857         if (this.dragZone) {
48858                 this.dragZone.addToGroup(ddGroup);
48859         } else {
48860                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48861                                 containerScroll: true,
48862                                 ddGroup: ddGroup 
48863
48864                         });
48865 //                      Draggability implies selection. DragZone's mousedown selects the element.
48866                         if (!this.multiSelect) { this.singleSelect = true; }
48867
48868 //                      Wire the DragZone's handlers up to methods in *this*
48869                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48870                 }
48871     },
48872
48873 /**     Specify from which ddGroup this DDView accepts drops. */
48874     setDroppable: function(ddGroup) {
48875         if (ddGroup instanceof Array) {
48876                 Roo.each(ddGroup, this.setDroppable, this);
48877                 return;
48878         }
48879         if (this.dropZone) {
48880                 this.dropZone.addToGroup(ddGroup);
48881         } else {
48882                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48883                                 containerScroll: true,
48884                                 ddGroup: ddGroup
48885                         });
48886
48887 //                      Wire the DropZone's handlers up to methods in *this*
48888                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48889                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48890                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48891                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48892                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48893                 }
48894     },
48895
48896 /**     Decide whether to drop above or below a View node. */
48897     getDropPoint : function(e, n, dd){
48898         if (n == this.el.dom) { return "above"; }
48899                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48900                 var c = t + (b - t) / 2;
48901                 var y = Roo.lib.Event.getPageY(e);
48902                 if(y <= c) {
48903                         return "above";
48904                 }else{
48905                         return "below";
48906                 }
48907     },
48908
48909     onNodeEnter : function(n, dd, e, data){
48910                 return false;
48911     },
48912     
48913     onNodeOver : function(n, dd, e, data){
48914                 var pt = this.getDropPoint(e, n, dd);
48915                 // set the insert point style on the target node
48916                 var dragElClass = this.dropNotAllowed;
48917                 if (pt) {
48918                         var targetElClass;
48919                         if (pt == "above"){
48920                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48921                                 targetElClass = "x-view-drag-insert-above";
48922                         } else {
48923                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48924                                 targetElClass = "x-view-drag-insert-below";
48925                         }
48926                         if (this.lastInsertClass != targetElClass){
48927                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48928                                 this.lastInsertClass = targetElClass;
48929                         }
48930                 }
48931                 return dragElClass;
48932         },
48933
48934     onNodeOut : function(n, dd, e, data){
48935                 this.removeDropIndicators(n);
48936     },
48937
48938     onNodeDrop : function(n, dd, e, data){
48939         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48940                 return false;
48941         }
48942         var pt = this.getDropPoint(e, n, dd);
48943                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48944                 if (pt == "below") { insertAt++; }
48945                 for (var i = 0; i < data.records.length; i++) {
48946                         var r = data.records[i];
48947                         var dup = this.store.getById(r.id);
48948                         if (dup && (dd != this.dragZone)) {
48949                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48950                         } else {
48951                                 if (data.copy) {
48952                                         this.store.insert(insertAt++, r.copy());
48953                                 } else {
48954                                         data.source.isDirtyFlag = true;
48955                                         r.store.remove(r);
48956                                         this.store.insert(insertAt++, r);
48957                                 }
48958                                 this.isDirtyFlag = true;
48959                         }
48960                 }
48961                 this.dragZone.cachedTarget = null;
48962                 return true;
48963     },
48964
48965     removeDropIndicators : function(n){
48966                 if(n){
48967                         Roo.fly(n).removeClass([
48968                                 "x-view-drag-insert-above",
48969                                 "x-view-drag-insert-below"]);
48970                         this.lastInsertClass = "_noclass";
48971                 }
48972     },
48973
48974 /**
48975  *      Utility method. Add a delete option to the DDView's context menu.
48976  *      @param {String} imageUrl The URL of the "delete" icon image.
48977  */
48978         setDeletable: function(imageUrl) {
48979                 if (!this.singleSelect && !this.multiSelect) {
48980                         this.singleSelect = true;
48981                 }
48982                 var c = this.getContextMenu();
48983                 this.contextMenu.on("itemclick", function(item) {
48984                         switch (item.id) {
48985                                 case "delete":
48986                                         this.remove(this.getSelectedIndexes());
48987                                         break;
48988                         }
48989                 }, this);
48990                 this.contextMenu.add({
48991                         icon: imageUrl,
48992                         id: "delete",
48993                         text: 'Delete'
48994                 });
48995         },
48996         
48997 /**     Return the context menu for this DDView. */
48998         getContextMenu: function() {
48999                 if (!this.contextMenu) {
49000 //                      Create the View's context menu
49001                         this.contextMenu = new Roo.menu.Menu({
49002                                 id: this.id + "-contextmenu"
49003                         });
49004                         this.el.on("contextmenu", this.showContextMenu, this);
49005                 }
49006                 return this.contextMenu;
49007         },
49008         
49009         disableContextMenu: function() {
49010                 if (this.contextMenu) {
49011                         this.el.un("contextmenu", this.showContextMenu, this);
49012                 }
49013         },
49014
49015         showContextMenu: function(e, item) {
49016         item = this.findItemFromChild(e.getTarget());
49017                 if (item) {
49018                         e.stopEvent();
49019                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
49020                         this.contextMenu.showAt(e.getXY());
49021             }
49022     },
49023
49024 /**
49025  *      Remove {@link Roo.data.Record}s at the specified indices.
49026  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
49027  */
49028     remove: function(selectedIndices) {
49029                 selectedIndices = [].concat(selectedIndices);
49030                 for (var i = 0; i < selectedIndices.length; i++) {
49031                         var rec = this.store.getAt(selectedIndices[i]);
49032                         this.store.remove(rec);
49033                 }
49034     },
49035
49036 /**
49037  *      Double click fires the event, but also, if this is draggable, and there is only one other
49038  *      related DropZone, it transfers the selected node.
49039  */
49040     onDblClick : function(e){
49041         var item = this.findItemFromChild(e.getTarget());
49042         if(item){
49043             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
49044                 return false;
49045             }
49046             if (this.dragGroup) {
49047                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
49048                     while (targets.indexOf(this.dropZone) > -1) {
49049                             targets.remove(this.dropZone);
49050                                 }
49051                     if (targets.length == 1) {
49052                                         this.dragZone.cachedTarget = null;
49053                         var el = Roo.get(targets[0].getEl());
49054                         var box = el.getBox(true);
49055                         targets[0].onNodeDrop(el.dom, {
49056                                 target: el.dom,
49057                                 xy: [box.x, box.y + box.height - 1]
49058                         }, null, this.getDragData(e));
49059                     }
49060                 }
49061         }
49062     },
49063     
49064     handleSelection: function(e) {
49065                 this.dragZone.cachedTarget = null;
49066         var item = this.findItemFromChild(e.getTarget());
49067         if (!item) {
49068                 this.clearSelections(true);
49069                 return;
49070         }
49071                 if (item && (this.multiSelect || this.singleSelect)){
49072                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
49073                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
49074                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
49075                                 this.unselect(item);
49076                         } else {
49077                                 this.select(item, this.multiSelect && e.ctrlKey);
49078                                 this.lastSelection = item;
49079                         }
49080                 }
49081     },
49082
49083     onItemClick : function(item, index, e){
49084                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
49085                         return false;
49086                 }
49087                 return true;
49088     },
49089
49090     unselect : function(nodeInfo, suppressEvent){
49091                 var node = this.getNode(nodeInfo);
49092                 if(node && this.isSelected(node)){
49093                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
49094                                 Roo.fly(node).removeClass(this.selectedClass);
49095                                 this.selections.remove(node);
49096                                 if(!suppressEvent){
49097                                         this.fireEvent("selectionchange", this, this.selections);
49098                                 }
49099                         }
49100                 }
49101     }
49102 });
49103 /*
49104  * Based on:
49105  * Ext JS Library 1.1.1
49106  * Copyright(c) 2006-2007, Ext JS, LLC.
49107  *
49108  * Originally Released Under LGPL - original licence link has changed is not relivant.
49109  *
49110  * Fork - LGPL
49111  * <script type="text/javascript">
49112  */
49113  
49114 /**
49115  * @class Roo.LayoutManager
49116  * @extends Roo.util.Observable
49117  * Base class for layout managers.
49118  */
49119 Roo.LayoutManager = function(container, config){
49120     Roo.LayoutManager.superclass.constructor.call(this);
49121     this.el = Roo.get(container);
49122     // ie scrollbar fix
49123     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
49124         document.body.scroll = "no";
49125     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
49126         this.el.position('relative');
49127     }
49128     this.id = this.el.id;
49129     this.el.addClass("x-layout-container");
49130     /** false to disable window resize monitoring @type Boolean */
49131     this.monitorWindowResize = true;
49132     this.regions = {};
49133     this.addEvents({
49134         /**
49135          * @event layout
49136          * Fires when a layout is performed. 
49137          * @param {Roo.LayoutManager} this
49138          */
49139         "layout" : true,
49140         /**
49141          * @event regionresized
49142          * Fires when the user resizes a region. 
49143          * @param {Roo.LayoutRegion} region The resized region
49144          * @param {Number} newSize The new size (width for east/west, height for north/south)
49145          */
49146         "regionresized" : true,
49147         /**
49148          * @event regioncollapsed
49149          * Fires when a region is collapsed. 
49150          * @param {Roo.LayoutRegion} region The collapsed region
49151          */
49152         "regioncollapsed" : true,
49153         /**
49154          * @event regionexpanded
49155          * Fires when a region is expanded.  
49156          * @param {Roo.LayoutRegion} region The expanded region
49157          */
49158         "regionexpanded" : true
49159     });
49160     this.updating = false;
49161     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49162 };
49163
49164 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
49165     /**
49166      * Returns true if this layout is currently being updated
49167      * @return {Boolean}
49168      */
49169     isUpdating : function(){
49170         return this.updating; 
49171     },
49172     
49173     /**
49174      * Suspend the LayoutManager from doing auto-layouts while
49175      * making multiple add or remove calls
49176      */
49177     beginUpdate : function(){
49178         this.updating = true;    
49179     },
49180     
49181     /**
49182      * Restore auto-layouts and optionally disable the manager from performing a layout
49183      * @param {Boolean} noLayout true to disable a layout update 
49184      */
49185     endUpdate : function(noLayout){
49186         this.updating = false;
49187         if(!noLayout){
49188             this.layout();
49189         }    
49190     },
49191     
49192     layout: function(){
49193         
49194     },
49195     
49196     onRegionResized : function(region, newSize){
49197         this.fireEvent("regionresized", region, newSize);
49198         this.layout();
49199     },
49200     
49201     onRegionCollapsed : function(region){
49202         this.fireEvent("regioncollapsed", region);
49203     },
49204     
49205     onRegionExpanded : function(region){
49206         this.fireEvent("regionexpanded", region);
49207     },
49208         
49209     /**
49210      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
49211      * performs box-model adjustments.
49212      * @return {Object} The size as an object {width: (the width), height: (the height)}
49213      */
49214     getViewSize : function(){
49215         var size;
49216         if(this.el.dom != document.body){
49217             size = this.el.getSize();
49218         }else{
49219             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
49220         }
49221         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
49222         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
49223         return size;
49224     },
49225     
49226     /**
49227      * Returns the Element this layout is bound to.
49228      * @return {Roo.Element}
49229      */
49230     getEl : function(){
49231         return this.el;
49232     },
49233     
49234     /**
49235      * Returns the specified region.
49236      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
49237      * @return {Roo.LayoutRegion}
49238      */
49239     getRegion : function(target){
49240         return this.regions[target.toLowerCase()];
49241     },
49242     
49243     onWindowResize : function(){
49244         if(this.monitorWindowResize){
49245             this.layout();
49246         }
49247     }
49248 });/*
49249  * Based on:
49250  * Ext JS Library 1.1.1
49251  * Copyright(c) 2006-2007, Ext JS, LLC.
49252  *
49253  * Originally Released Under LGPL - original licence link has changed is not relivant.
49254  *
49255  * Fork - LGPL
49256  * <script type="text/javascript">
49257  */
49258 /**
49259  * @class Roo.BorderLayout
49260  * @extends Roo.LayoutManager
49261  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
49262  * please see: <br><br>
49263  * <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>
49264  * <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>
49265  * Example:
49266  <pre><code>
49267  var layout = new Roo.BorderLayout(document.body, {
49268     north: {
49269         initialSize: 25,
49270         titlebar: false
49271     },
49272     west: {
49273         split:true,
49274         initialSize: 200,
49275         minSize: 175,
49276         maxSize: 400,
49277         titlebar: true,
49278         collapsible: true
49279     },
49280     east: {
49281         split:true,
49282         initialSize: 202,
49283         minSize: 175,
49284         maxSize: 400,
49285         titlebar: true,
49286         collapsible: true
49287     },
49288     south: {
49289         split:true,
49290         initialSize: 100,
49291         minSize: 100,
49292         maxSize: 200,
49293         titlebar: true,
49294         collapsible: true
49295     },
49296     center: {
49297         titlebar: true,
49298         autoScroll:true,
49299         resizeTabs: true,
49300         minTabWidth: 50,
49301         preferredTabWidth: 150
49302     }
49303 });
49304
49305 // shorthand
49306 var CP = Roo.ContentPanel;
49307
49308 layout.beginUpdate();
49309 layout.add("north", new CP("north", "North"));
49310 layout.add("south", new CP("south", {title: "South", closable: true}));
49311 layout.add("west", new CP("west", {title: "West"}));
49312 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
49313 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
49314 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
49315 layout.getRegion("center").showPanel("center1");
49316 layout.endUpdate();
49317 </code></pre>
49318
49319 <b>The container the layout is rendered into can be either the body element or any other element.
49320 If it is not the body element, the container needs to either be an absolute positioned element,
49321 or you will need to add "position:relative" to the css of the container.  You will also need to specify
49322 the container size if it is not the body element.</b>
49323
49324 * @constructor
49325 * Create a new BorderLayout
49326 * @param {String/HTMLElement/Element} container The container this layout is bound to
49327 * @param {Object} config Configuration options
49328  */
49329 Roo.BorderLayout = function(container, config){
49330     config = config || {};
49331     Roo.BorderLayout.superclass.constructor.call(this, container, config);
49332     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
49333     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
49334         var target = this.factory.validRegions[i];
49335         if(config[target]){
49336             this.addRegion(target, config[target]);
49337         }
49338     }
49339 };
49340
49341 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
49342     /**
49343      * Creates and adds a new region if it doesn't already exist.
49344      * @param {String} target The target region key (north, south, east, west or center).
49345      * @param {Object} config The regions config object
49346      * @return {BorderLayoutRegion} The new region
49347      */
49348     addRegion : function(target, config){
49349         if(!this.regions[target]){
49350             var r = this.factory.create(target, this, config);
49351             this.bindRegion(target, r);
49352         }
49353         return this.regions[target];
49354     },
49355
49356     // private (kinda)
49357     bindRegion : function(name, r){
49358         this.regions[name] = r;
49359         r.on("visibilitychange", this.layout, this);
49360         r.on("paneladded", this.layout, this);
49361         r.on("panelremoved", this.layout, this);
49362         r.on("invalidated", this.layout, this);
49363         r.on("resized", this.onRegionResized, this);
49364         r.on("collapsed", this.onRegionCollapsed, this);
49365         r.on("expanded", this.onRegionExpanded, this);
49366     },
49367
49368     /**
49369      * Performs a layout update.
49370      */
49371     layout : function(){
49372         if(this.updating) return;
49373         var size = this.getViewSize();
49374         var w = size.width;
49375         var h = size.height;
49376         var centerW = w;
49377         var centerH = h;
49378         var centerY = 0;
49379         var centerX = 0;
49380         //var x = 0, y = 0;
49381
49382         var rs = this.regions;
49383         var north = rs["north"];
49384         var south = rs["south"]; 
49385         var west = rs["west"];
49386         var east = rs["east"];
49387         var center = rs["center"];
49388         //if(this.hideOnLayout){ // not supported anymore
49389             //c.el.setStyle("display", "none");
49390         //}
49391         if(north && north.isVisible()){
49392             var b = north.getBox();
49393             var m = north.getMargins();
49394             b.width = w - (m.left+m.right);
49395             b.x = m.left;
49396             b.y = m.top;
49397             centerY = b.height + b.y + m.bottom;
49398             centerH -= centerY;
49399             north.updateBox(this.safeBox(b));
49400         }
49401         if(south && south.isVisible()){
49402             var b = south.getBox();
49403             var m = south.getMargins();
49404             b.width = w - (m.left+m.right);
49405             b.x = m.left;
49406             var totalHeight = (b.height + m.top + m.bottom);
49407             b.y = h - totalHeight + m.top;
49408             centerH -= totalHeight;
49409             south.updateBox(this.safeBox(b));
49410         }
49411         if(west && west.isVisible()){
49412             var b = west.getBox();
49413             var m = west.getMargins();
49414             b.height = centerH - (m.top+m.bottom);
49415             b.x = m.left;
49416             b.y = centerY + m.top;
49417             var totalWidth = (b.width + m.left + m.right);
49418             centerX += totalWidth;
49419             centerW -= totalWidth;
49420             west.updateBox(this.safeBox(b));
49421         }
49422         if(east && east.isVisible()){
49423             var b = east.getBox();
49424             var m = east.getMargins();
49425             b.height = centerH - (m.top+m.bottom);
49426             var totalWidth = (b.width + m.left + m.right);
49427             b.x = w - totalWidth + m.left;
49428             b.y = centerY + m.top;
49429             centerW -= totalWidth;
49430             east.updateBox(this.safeBox(b));
49431         }
49432         if(center){
49433             var m = center.getMargins();
49434             var centerBox = {
49435                 x: centerX + m.left,
49436                 y: centerY + m.top,
49437                 width: centerW - (m.left+m.right),
49438                 height: centerH - (m.top+m.bottom)
49439             };
49440             //if(this.hideOnLayout){
49441                 //center.el.setStyle("display", "block");
49442             //}
49443             center.updateBox(this.safeBox(centerBox));
49444         }
49445         this.el.repaint();
49446         this.fireEvent("layout", this);
49447     },
49448
49449     // private
49450     safeBox : function(box){
49451         box.width = Math.max(0, box.width);
49452         box.height = Math.max(0, box.height);
49453         return box;
49454     },
49455
49456     /**
49457      * Adds a ContentPanel (or subclass) to this layout.
49458      * @param {String} target The target region key (north, south, east, west or center).
49459      * @param {Roo.ContentPanel} panel The panel to add
49460      * @return {Roo.ContentPanel} The added panel
49461      */
49462     add : function(target, panel){
49463          
49464         target = target.toLowerCase();
49465         return this.regions[target].add(panel);
49466     },
49467
49468     /**
49469      * Remove a ContentPanel (or subclass) to this layout.
49470      * @param {String} target The target region key (north, south, east, west or center).
49471      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49472      * @return {Roo.ContentPanel} The removed panel
49473      */
49474     remove : function(target, panel){
49475         target = target.toLowerCase();
49476         return this.regions[target].remove(panel);
49477     },
49478
49479     /**
49480      * Searches all regions for a panel with the specified id
49481      * @param {String} panelId
49482      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49483      */
49484     findPanel : function(panelId){
49485         var rs = this.regions;
49486         for(var target in rs){
49487             if(typeof rs[target] != "function"){
49488                 var p = rs[target].getPanel(panelId);
49489                 if(p){
49490                     return p;
49491                 }
49492             }
49493         }
49494         return null;
49495     },
49496
49497     /**
49498      * Searches all regions for a panel with the specified id and activates (shows) it.
49499      * @param {String/ContentPanel} panelId The panels id or the panel itself
49500      * @return {Roo.ContentPanel} The shown panel or null
49501      */
49502     showPanel : function(panelId) {
49503       var rs = this.regions;
49504       for(var target in rs){
49505          var r = rs[target];
49506          if(typeof r != "function"){
49507             if(r.hasPanel(panelId)){
49508                return r.showPanel(panelId);
49509             }
49510          }
49511       }
49512       return null;
49513    },
49514
49515    /**
49516      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49517      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49518      */
49519     restoreState : function(provider){
49520         if(!provider){
49521             provider = Roo.state.Manager;
49522         }
49523         var sm = new Roo.LayoutStateManager();
49524         sm.init(this, provider);
49525     },
49526
49527     /**
49528      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49529      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49530      * a valid ContentPanel config object.  Example:
49531      * <pre><code>
49532 // Create the main layout
49533 var layout = new Roo.BorderLayout('main-ct', {
49534     west: {
49535         split:true,
49536         minSize: 175,
49537         titlebar: true
49538     },
49539     center: {
49540         title:'Components'
49541     }
49542 }, 'main-ct');
49543
49544 // Create and add multiple ContentPanels at once via configs
49545 layout.batchAdd({
49546    west: {
49547        id: 'source-files',
49548        autoCreate:true,
49549        title:'Ext Source Files',
49550        autoScroll:true,
49551        fitToFrame:true
49552    },
49553    center : {
49554        el: cview,
49555        autoScroll:true,
49556        fitToFrame:true,
49557        toolbar: tb,
49558        resizeEl:'cbody'
49559    }
49560 });
49561 </code></pre>
49562      * @param {Object} regions An object containing ContentPanel configs by region name
49563      */
49564     batchAdd : function(regions){
49565         this.beginUpdate();
49566         for(var rname in regions){
49567             var lr = this.regions[rname];
49568             if(lr){
49569                 this.addTypedPanels(lr, regions[rname]);
49570             }
49571         }
49572         this.endUpdate();
49573     },
49574
49575     // private
49576     addTypedPanels : function(lr, ps){
49577         if(typeof ps == 'string'){
49578             lr.add(new Roo.ContentPanel(ps));
49579         }
49580         else if(ps instanceof Array){
49581             for(var i =0, len = ps.length; i < len; i++){
49582                 this.addTypedPanels(lr, ps[i]);
49583             }
49584         }
49585         else if(!ps.events){ // raw config?
49586             var el = ps.el;
49587             delete ps.el; // prevent conflict
49588             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49589         }
49590         else {  // panel object assumed!
49591             lr.add(ps);
49592         }
49593     },
49594     /**
49595      * Adds a xtype elements to the layout.
49596      * <pre><code>
49597
49598 layout.addxtype({
49599        xtype : 'ContentPanel',
49600        region: 'west',
49601        items: [ .... ]
49602    }
49603 );
49604
49605 layout.addxtype({
49606         xtype : 'NestedLayoutPanel',
49607         region: 'west',
49608         layout: {
49609            center: { },
49610            west: { }   
49611         },
49612         items : [ ... list of content panels or nested layout panels.. ]
49613    }
49614 );
49615 </code></pre>
49616      * @param {Object} cfg Xtype definition of item to add.
49617      */
49618     addxtype : function(cfg)
49619     {
49620         // basically accepts a pannel...
49621         // can accept a layout region..!?!?
49622         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49623         
49624         if (!cfg.xtype.match(/Panel$/)) {
49625             return false;
49626         }
49627         var ret = false;
49628         
49629         if (typeof(cfg.region) == 'undefined') {
49630             Roo.log("Failed to add Panel, region was not set");
49631             Roo.log(cfg);
49632             return false;
49633         }
49634         var region = cfg.region;
49635         delete cfg.region;
49636         
49637           
49638         var xitems = [];
49639         if (cfg.items) {
49640             xitems = cfg.items;
49641             delete cfg.items;
49642         }
49643         var nb = false;
49644         
49645         switch(cfg.xtype) 
49646         {
49647             case 'ContentPanel':  // ContentPanel (el, cfg)
49648             case 'ScrollPanel':  // ContentPanel (el, cfg)
49649             case 'ViewPanel': 
49650                 if(cfg.autoCreate) {
49651                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49652                 } else {
49653                     var el = this.el.createChild();
49654                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49655                 }
49656                 
49657                 this.add(region, ret);
49658                 break;
49659             
49660             
49661             case 'TreePanel': // our new panel!
49662                 cfg.el = this.el.createChild();
49663                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49664                 this.add(region, ret);
49665                 break;
49666             
49667             case 'NestedLayoutPanel': 
49668                 // create a new Layout (which is  a Border Layout...
49669                 var el = this.el.createChild();
49670                 var clayout = cfg.layout;
49671                 delete cfg.layout;
49672                 clayout.items   = clayout.items  || [];
49673                 // replace this exitems with the clayout ones..
49674                 xitems = clayout.items;
49675                  
49676                 
49677                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49678                     cfg.background = false;
49679                 }
49680                 var layout = new Roo.BorderLayout(el, clayout);
49681                 
49682                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49683                 //console.log('adding nested layout panel '  + cfg.toSource());
49684                 this.add(region, ret);
49685                 nb = {}; /// find first...
49686                 break;
49687                 
49688             case 'GridPanel': 
49689             
49690                 // needs grid and region
49691                 
49692                 //var el = this.getRegion(region).el.createChild();
49693                 var el = this.el.createChild();
49694                 // create the grid first...
49695                 
49696                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49697                 delete cfg.grid;
49698                 if (region == 'center' && this.active ) {
49699                     cfg.background = false;
49700                 }
49701                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49702                 
49703                 this.add(region, ret);
49704                 if (cfg.background) {
49705                     ret.on('activate', function(gp) {
49706                         if (!gp.grid.rendered) {
49707                             gp.grid.render();
49708                         }
49709                     });
49710                 } else {
49711                     grid.render();
49712                 }
49713                 break;
49714            
49715            
49716            
49717                 
49718                 
49719                 
49720             default:
49721                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49722                     
49723                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49724                     this.add(region, ret);
49725                 } else {
49726                 
49727                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49728                     return null;
49729                 }
49730                 
49731              // GridPanel (grid, cfg)
49732             
49733         }
49734         this.beginUpdate();
49735         // add children..
49736         var region = '';
49737         var abn = {};
49738         Roo.each(xitems, function(i)  {
49739             region = nb && i.region ? i.region : false;
49740             
49741             var add = ret.addxtype(i);
49742            
49743             if (region) {
49744                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49745                 if (!i.background) {
49746                     abn[region] = nb[region] ;
49747                 }
49748             }
49749             
49750         });
49751         this.endUpdate();
49752
49753         // make the last non-background panel active..
49754         //if (nb) { Roo.log(abn); }
49755         if (nb) {
49756             
49757             for(var r in abn) {
49758                 region = this.getRegion(r);
49759                 if (region) {
49760                     // tried using nb[r], but it does not work..
49761                      
49762                     region.showPanel(abn[r]);
49763                    
49764                 }
49765             }
49766         }
49767         return ret;
49768         
49769     }
49770 });
49771
49772 /**
49773  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49774  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49775  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49776  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49777  * <pre><code>
49778 // shorthand
49779 var CP = Roo.ContentPanel;
49780
49781 var layout = Roo.BorderLayout.create({
49782     north: {
49783         initialSize: 25,
49784         titlebar: false,
49785         panels: [new CP("north", "North")]
49786     },
49787     west: {
49788         split:true,
49789         initialSize: 200,
49790         minSize: 175,
49791         maxSize: 400,
49792         titlebar: true,
49793         collapsible: true,
49794         panels: [new CP("west", {title: "West"})]
49795     },
49796     east: {
49797         split:true,
49798         initialSize: 202,
49799         minSize: 175,
49800         maxSize: 400,
49801         titlebar: true,
49802         collapsible: true,
49803         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49804     },
49805     south: {
49806         split:true,
49807         initialSize: 100,
49808         minSize: 100,
49809         maxSize: 200,
49810         titlebar: true,
49811         collapsible: true,
49812         panels: [new CP("south", {title: "South", closable: true})]
49813     },
49814     center: {
49815         titlebar: true,
49816         autoScroll:true,
49817         resizeTabs: true,
49818         minTabWidth: 50,
49819         preferredTabWidth: 150,
49820         panels: [
49821             new CP("center1", {title: "Close Me", closable: true}),
49822             new CP("center2", {title: "Center Panel", closable: false})
49823         ]
49824     }
49825 }, document.body);
49826
49827 layout.getRegion("center").showPanel("center1");
49828 </code></pre>
49829  * @param config
49830  * @param targetEl
49831  */
49832 Roo.BorderLayout.create = function(config, targetEl){
49833     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49834     layout.beginUpdate();
49835     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49836     for(var j = 0, jlen = regions.length; j < jlen; j++){
49837         var lr = regions[j];
49838         if(layout.regions[lr] && config[lr].panels){
49839             var r = layout.regions[lr];
49840             var ps = config[lr].panels;
49841             layout.addTypedPanels(r, ps);
49842         }
49843     }
49844     layout.endUpdate();
49845     return layout;
49846 };
49847
49848 // private
49849 Roo.BorderLayout.RegionFactory = {
49850     // private
49851     validRegions : ["north","south","east","west","center"],
49852
49853     // private
49854     create : function(target, mgr, config){
49855         target = target.toLowerCase();
49856         if(config.lightweight || config.basic){
49857             return new Roo.BasicLayoutRegion(mgr, config, target);
49858         }
49859         switch(target){
49860             case "north":
49861                 return new Roo.NorthLayoutRegion(mgr, config);
49862             case "south":
49863                 return new Roo.SouthLayoutRegion(mgr, config);
49864             case "east":
49865                 return new Roo.EastLayoutRegion(mgr, config);
49866             case "west":
49867                 return new Roo.WestLayoutRegion(mgr, config);
49868             case "center":
49869                 return new Roo.CenterLayoutRegion(mgr, config);
49870         }
49871         throw 'Layout region "'+target+'" not supported.';
49872     }
49873 };/*
49874  * Based on:
49875  * Ext JS Library 1.1.1
49876  * Copyright(c) 2006-2007, Ext JS, LLC.
49877  *
49878  * Originally Released Under LGPL - original licence link has changed is not relivant.
49879  *
49880  * Fork - LGPL
49881  * <script type="text/javascript">
49882  */
49883  
49884 /**
49885  * @class Roo.BasicLayoutRegion
49886  * @extends Roo.util.Observable
49887  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49888  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49889  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49890  */
49891 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49892     this.mgr = mgr;
49893     this.position  = pos;
49894     this.events = {
49895         /**
49896          * @scope Roo.BasicLayoutRegion
49897          */
49898         
49899         /**
49900          * @event beforeremove
49901          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49902          * @param {Roo.LayoutRegion} this
49903          * @param {Roo.ContentPanel} panel The panel
49904          * @param {Object} e The cancel event object
49905          */
49906         "beforeremove" : true,
49907         /**
49908          * @event invalidated
49909          * Fires when the layout for this region is changed.
49910          * @param {Roo.LayoutRegion} this
49911          */
49912         "invalidated" : true,
49913         /**
49914          * @event visibilitychange
49915          * Fires when this region is shown or hidden 
49916          * @param {Roo.LayoutRegion} this
49917          * @param {Boolean} visibility true or false
49918          */
49919         "visibilitychange" : true,
49920         /**
49921          * @event paneladded
49922          * Fires when a panel is added. 
49923          * @param {Roo.LayoutRegion} this
49924          * @param {Roo.ContentPanel} panel The panel
49925          */
49926         "paneladded" : true,
49927         /**
49928          * @event panelremoved
49929          * Fires when a panel is removed. 
49930          * @param {Roo.LayoutRegion} this
49931          * @param {Roo.ContentPanel} panel The panel
49932          */
49933         "panelremoved" : true,
49934         /**
49935          * @event collapsed
49936          * Fires when this region is collapsed.
49937          * @param {Roo.LayoutRegion} this
49938          */
49939         "collapsed" : true,
49940         /**
49941          * @event expanded
49942          * Fires when this region is expanded.
49943          * @param {Roo.LayoutRegion} this
49944          */
49945         "expanded" : true,
49946         /**
49947          * @event slideshow
49948          * Fires when this region is slid into view.
49949          * @param {Roo.LayoutRegion} this
49950          */
49951         "slideshow" : true,
49952         /**
49953          * @event slidehide
49954          * Fires when this region slides out of view. 
49955          * @param {Roo.LayoutRegion} this
49956          */
49957         "slidehide" : true,
49958         /**
49959          * @event panelactivated
49960          * Fires when a panel is activated. 
49961          * @param {Roo.LayoutRegion} this
49962          * @param {Roo.ContentPanel} panel The activated panel
49963          */
49964         "panelactivated" : true,
49965         /**
49966          * @event resized
49967          * Fires when the user resizes this region. 
49968          * @param {Roo.LayoutRegion} this
49969          * @param {Number} newSize The new size (width for east/west, height for north/south)
49970          */
49971         "resized" : true
49972     };
49973     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49974     this.panels = new Roo.util.MixedCollection();
49975     this.panels.getKey = this.getPanelId.createDelegate(this);
49976     this.box = null;
49977     this.activePanel = null;
49978     // ensure listeners are added...
49979     
49980     if (config.listeners || config.events) {
49981         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49982             listeners : config.listeners || {},
49983             events : config.events || {}
49984         });
49985     }
49986     
49987     if(skipConfig !== true){
49988         this.applyConfig(config);
49989     }
49990 };
49991
49992 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49993     getPanelId : function(p){
49994         return p.getId();
49995     },
49996     
49997     applyConfig : function(config){
49998         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49999         this.config = config;
50000         
50001     },
50002     
50003     /**
50004      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
50005      * the width, for horizontal (north, south) the height.
50006      * @param {Number} newSize The new width or height
50007      */
50008     resizeTo : function(newSize){
50009         var el = this.el ? this.el :
50010                  (this.activePanel ? this.activePanel.getEl() : null);
50011         if(el){
50012             switch(this.position){
50013                 case "east":
50014                 case "west":
50015                     el.setWidth(newSize);
50016                     this.fireEvent("resized", this, newSize);
50017                 break;
50018                 case "north":
50019                 case "south":
50020                     el.setHeight(newSize);
50021                     this.fireEvent("resized", this, newSize);
50022                 break;                
50023             }
50024         }
50025     },
50026     
50027     getBox : function(){
50028         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
50029     },
50030     
50031     getMargins : function(){
50032         return this.margins;
50033     },
50034     
50035     updateBox : function(box){
50036         this.box = box;
50037         var el = this.activePanel.getEl();
50038         el.dom.style.left = box.x + "px";
50039         el.dom.style.top = box.y + "px";
50040         this.activePanel.setSize(box.width, box.height);
50041     },
50042     
50043     /**
50044      * Returns the container element for this region.
50045      * @return {Roo.Element}
50046      */
50047     getEl : function(){
50048         return this.activePanel;
50049     },
50050     
50051     /**
50052      * Returns true if this region is currently visible.
50053      * @return {Boolean}
50054      */
50055     isVisible : function(){
50056         return this.activePanel ? true : false;
50057     },
50058     
50059     setActivePanel : function(panel){
50060         panel = this.getPanel(panel);
50061         if(this.activePanel && this.activePanel != panel){
50062             this.activePanel.setActiveState(false);
50063             this.activePanel.getEl().setLeftTop(-10000,-10000);
50064         }
50065         this.activePanel = panel;
50066         panel.setActiveState(true);
50067         if(this.box){
50068             panel.setSize(this.box.width, this.box.height);
50069         }
50070         this.fireEvent("panelactivated", this, panel);
50071         this.fireEvent("invalidated");
50072     },
50073     
50074     /**
50075      * Show the specified panel.
50076      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
50077      * @return {Roo.ContentPanel} The shown panel or null
50078      */
50079     showPanel : function(panel){
50080         if(panel = this.getPanel(panel)){
50081             this.setActivePanel(panel);
50082         }
50083         return panel;
50084     },
50085     
50086     /**
50087      * Get the active panel for this region.
50088      * @return {Roo.ContentPanel} The active panel or null
50089      */
50090     getActivePanel : function(){
50091         return this.activePanel;
50092     },
50093     
50094     /**
50095      * Add the passed ContentPanel(s)
50096      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50097      * @return {Roo.ContentPanel} The panel added (if only one was added)
50098      */
50099     add : function(panel){
50100         if(arguments.length > 1){
50101             for(var i = 0, len = arguments.length; i < len; i++) {
50102                 this.add(arguments[i]);
50103             }
50104             return null;
50105         }
50106         if(this.hasPanel(panel)){
50107             this.showPanel(panel);
50108             return panel;
50109         }
50110         var el = panel.getEl();
50111         if(el.dom.parentNode != this.mgr.el.dom){
50112             this.mgr.el.dom.appendChild(el.dom);
50113         }
50114         if(panel.setRegion){
50115             panel.setRegion(this);
50116         }
50117         this.panels.add(panel);
50118         el.setStyle("position", "absolute");
50119         if(!panel.background){
50120             this.setActivePanel(panel);
50121             if(this.config.initialSize && this.panels.getCount()==1){
50122                 this.resizeTo(this.config.initialSize);
50123             }
50124         }
50125         this.fireEvent("paneladded", this, panel);
50126         return panel;
50127     },
50128     
50129     /**
50130      * Returns true if the panel is in this region.
50131      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50132      * @return {Boolean}
50133      */
50134     hasPanel : function(panel){
50135         if(typeof panel == "object"){ // must be panel obj
50136             panel = panel.getId();
50137         }
50138         return this.getPanel(panel) ? true : false;
50139     },
50140     
50141     /**
50142      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50143      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50144      * @param {Boolean} preservePanel Overrides the config preservePanel option
50145      * @return {Roo.ContentPanel} The panel that was removed
50146      */
50147     remove : function(panel, preservePanel){
50148         panel = this.getPanel(panel);
50149         if(!panel){
50150             return null;
50151         }
50152         var e = {};
50153         this.fireEvent("beforeremove", this, panel, e);
50154         if(e.cancel === true){
50155             return null;
50156         }
50157         var panelId = panel.getId();
50158         this.panels.removeKey(panelId);
50159         return panel;
50160     },
50161     
50162     /**
50163      * Returns the panel specified or null if it's not in this region.
50164      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
50165      * @return {Roo.ContentPanel}
50166      */
50167     getPanel : function(id){
50168         if(typeof id == "object"){ // must be panel obj
50169             return id;
50170         }
50171         return this.panels.get(id);
50172     },
50173     
50174     /**
50175      * Returns this regions position (north/south/east/west/center).
50176      * @return {String} 
50177      */
50178     getPosition: function(){
50179         return this.position;    
50180     }
50181 });/*
50182  * Based on:
50183  * Ext JS Library 1.1.1
50184  * Copyright(c) 2006-2007, Ext JS, LLC.
50185  *
50186  * Originally Released Under LGPL - original licence link has changed is not relivant.
50187  *
50188  * Fork - LGPL
50189  * <script type="text/javascript">
50190  */
50191  
50192 /**
50193  * @class Roo.LayoutRegion
50194  * @extends Roo.BasicLayoutRegion
50195  * This class represents a region in a layout manager.
50196  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
50197  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
50198  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
50199  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
50200  * @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})
50201  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
50202  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
50203  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
50204  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
50205  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
50206  * @cfg {String}    title           The title for the region (overrides panel titles)
50207  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
50208  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
50209  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
50210  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
50211  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
50212  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
50213  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
50214  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
50215  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
50216  * @cfg {Boolean}   showPin         True to show a pin button
50217  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
50218  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
50219  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
50220  * @cfg {Number}    width           For East/West panels
50221  * @cfg {Number}    height          For North/South panels
50222  * @cfg {Boolean}   split           To show the splitter
50223  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
50224  */
50225 Roo.LayoutRegion = function(mgr, config, pos){
50226     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
50227     var dh = Roo.DomHelper;
50228     /** This region's container element 
50229     * @type Roo.Element */
50230     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
50231     /** This region's title element 
50232     * @type Roo.Element */
50233
50234     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
50235         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
50236         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
50237     ]}, true);
50238     this.titleEl.enableDisplayMode();
50239     /** This region's title text element 
50240     * @type HTMLElement */
50241     this.titleTextEl = this.titleEl.dom.firstChild;
50242     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
50243     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
50244     this.closeBtn.enableDisplayMode();
50245     this.closeBtn.on("click", this.closeClicked, this);
50246     this.closeBtn.hide();
50247
50248     this.createBody(config);
50249     this.visible = true;
50250     this.collapsed = false;
50251
50252     if(config.hideWhenEmpty){
50253         this.hide();
50254         this.on("paneladded", this.validateVisibility, this);
50255         this.on("panelremoved", this.validateVisibility, this);
50256     }
50257     this.applyConfig(config);
50258 };
50259
50260 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
50261
50262     createBody : function(){
50263         /** This region's body element 
50264         * @type Roo.Element */
50265         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
50266     },
50267
50268     applyConfig : function(c){
50269         if(c.collapsible && this.position != "center" && !this.collapsedEl){
50270             var dh = Roo.DomHelper;
50271             if(c.titlebar !== false){
50272                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
50273                 this.collapseBtn.on("click", this.collapse, this);
50274                 this.collapseBtn.enableDisplayMode();
50275
50276                 if(c.showPin === true || this.showPin){
50277                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
50278                     this.stickBtn.enableDisplayMode();
50279                     this.stickBtn.on("click", this.expand, this);
50280                     this.stickBtn.hide();
50281                 }
50282             }
50283             /** This region's collapsed element
50284             * @type Roo.Element */
50285             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
50286                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
50287             ]}, true);
50288             if(c.floatable !== false){
50289                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
50290                this.collapsedEl.on("click", this.collapseClick, this);
50291             }
50292
50293             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
50294                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
50295                    id: "message", unselectable: "on", style:{"float":"left"}});
50296                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
50297              }
50298             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
50299             this.expandBtn.on("click", this.expand, this);
50300         }
50301         if(this.collapseBtn){
50302             this.collapseBtn.setVisible(c.collapsible == true);
50303         }
50304         this.cmargins = c.cmargins || this.cmargins ||
50305                          (this.position == "west" || this.position == "east" ?
50306                              {top: 0, left: 2, right:2, bottom: 0} :
50307                              {top: 2, left: 0, right:0, bottom: 2});
50308         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
50309         this.bottomTabs = c.tabPosition != "top";
50310         this.autoScroll = c.autoScroll || false;
50311         if(this.autoScroll){
50312             this.bodyEl.setStyle("overflow", "auto");
50313         }else{
50314             this.bodyEl.setStyle("overflow", "hidden");
50315         }
50316         //if(c.titlebar !== false){
50317             if((!c.titlebar && !c.title) || c.titlebar === false){
50318                 this.titleEl.hide();
50319             }else{
50320                 this.titleEl.show();
50321                 if(c.title){
50322                     this.titleTextEl.innerHTML = c.title;
50323                 }
50324             }
50325         //}
50326         this.duration = c.duration || .30;
50327         this.slideDuration = c.slideDuration || .45;
50328         this.config = c;
50329         if(c.collapsed){
50330             this.collapse(true);
50331         }
50332         if(c.hidden){
50333             this.hide();
50334         }
50335     },
50336     /**
50337      * Returns true if this region is currently visible.
50338      * @return {Boolean}
50339      */
50340     isVisible : function(){
50341         return this.visible;
50342     },
50343
50344     /**
50345      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
50346      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
50347      */
50348     setCollapsedTitle : function(title){
50349         title = title || "&#160;";
50350         if(this.collapsedTitleTextEl){
50351             this.collapsedTitleTextEl.innerHTML = title;
50352         }
50353     },
50354
50355     getBox : function(){
50356         var b;
50357         if(!this.collapsed){
50358             b = this.el.getBox(false, true);
50359         }else{
50360             b = this.collapsedEl.getBox(false, true);
50361         }
50362         return b;
50363     },
50364
50365     getMargins : function(){
50366         return this.collapsed ? this.cmargins : this.margins;
50367     },
50368
50369     highlight : function(){
50370         this.el.addClass("x-layout-panel-dragover");
50371     },
50372
50373     unhighlight : function(){
50374         this.el.removeClass("x-layout-panel-dragover");
50375     },
50376
50377     updateBox : function(box){
50378         this.box = box;
50379         if(!this.collapsed){
50380             this.el.dom.style.left = box.x + "px";
50381             this.el.dom.style.top = box.y + "px";
50382             this.updateBody(box.width, box.height);
50383         }else{
50384             this.collapsedEl.dom.style.left = box.x + "px";
50385             this.collapsedEl.dom.style.top = box.y + "px";
50386             this.collapsedEl.setSize(box.width, box.height);
50387         }
50388         if(this.tabs){
50389             this.tabs.autoSizeTabs();
50390         }
50391     },
50392
50393     updateBody : function(w, h){
50394         if(w !== null){
50395             this.el.setWidth(w);
50396             w -= this.el.getBorderWidth("rl");
50397             if(this.config.adjustments){
50398                 w += this.config.adjustments[0];
50399             }
50400         }
50401         if(h !== null){
50402             this.el.setHeight(h);
50403             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50404             h -= this.el.getBorderWidth("tb");
50405             if(this.config.adjustments){
50406                 h += this.config.adjustments[1];
50407             }
50408             this.bodyEl.setHeight(h);
50409             if(this.tabs){
50410                 h = this.tabs.syncHeight(h);
50411             }
50412         }
50413         if(this.panelSize){
50414             w = w !== null ? w : this.panelSize.width;
50415             h = h !== null ? h : this.panelSize.height;
50416         }
50417         if(this.activePanel){
50418             var el = this.activePanel.getEl();
50419             w = w !== null ? w : el.getWidth();
50420             h = h !== null ? h : el.getHeight();
50421             this.panelSize = {width: w, height: h};
50422             this.activePanel.setSize(w, h);
50423         }
50424         if(Roo.isIE && this.tabs){
50425             this.tabs.el.repaint();
50426         }
50427     },
50428
50429     /**
50430      * Returns the container element for this region.
50431      * @return {Roo.Element}
50432      */
50433     getEl : function(){
50434         return this.el;
50435     },
50436
50437     /**
50438      * Hides this region.
50439      */
50440     hide : function(){
50441         if(!this.collapsed){
50442             this.el.dom.style.left = "-2000px";
50443             this.el.hide();
50444         }else{
50445             this.collapsedEl.dom.style.left = "-2000px";
50446             this.collapsedEl.hide();
50447         }
50448         this.visible = false;
50449         this.fireEvent("visibilitychange", this, false);
50450     },
50451
50452     /**
50453      * Shows this region if it was previously hidden.
50454      */
50455     show : function(){
50456         if(!this.collapsed){
50457             this.el.show();
50458         }else{
50459             this.collapsedEl.show();
50460         }
50461         this.visible = true;
50462         this.fireEvent("visibilitychange", this, true);
50463     },
50464
50465     closeClicked : function(){
50466         if(this.activePanel){
50467             this.remove(this.activePanel);
50468         }
50469     },
50470
50471     collapseClick : function(e){
50472         if(this.isSlid){
50473            e.stopPropagation();
50474            this.slideIn();
50475         }else{
50476            e.stopPropagation();
50477            this.slideOut();
50478         }
50479     },
50480
50481     /**
50482      * Collapses this region.
50483      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50484      */
50485     collapse : function(skipAnim){
50486         if(this.collapsed) return;
50487         this.collapsed = true;
50488         if(this.split){
50489             this.split.el.hide();
50490         }
50491         if(this.config.animate && skipAnim !== true){
50492             this.fireEvent("invalidated", this);
50493             this.animateCollapse();
50494         }else{
50495             this.el.setLocation(-20000,-20000);
50496             this.el.hide();
50497             this.collapsedEl.show();
50498             this.fireEvent("collapsed", this);
50499             this.fireEvent("invalidated", this);
50500         }
50501     },
50502
50503     animateCollapse : function(){
50504         // overridden
50505     },
50506
50507     /**
50508      * Expands this region if it was previously collapsed.
50509      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50510      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50511      */
50512     expand : function(e, skipAnim){
50513         if(e) e.stopPropagation();
50514         if(!this.collapsed || this.el.hasActiveFx()) return;
50515         if(this.isSlid){
50516             this.afterSlideIn();
50517             skipAnim = true;
50518         }
50519         this.collapsed = false;
50520         if(this.config.animate && skipAnim !== true){
50521             this.animateExpand();
50522         }else{
50523             this.el.show();
50524             if(this.split){
50525                 this.split.el.show();
50526             }
50527             this.collapsedEl.setLocation(-2000,-2000);
50528             this.collapsedEl.hide();
50529             this.fireEvent("invalidated", this);
50530             this.fireEvent("expanded", this);
50531         }
50532     },
50533
50534     animateExpand : function(){
50535         // overridden
50536     },
50537
50538     initTabs : function()
50539     {
50540         this.bodyEl.setStyle("overflow", "hidden");
50541         var ts = new Roo.TabPanel(
50542                 this.bodyEl.dom,
50543                 {
50544                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50545                     disableTooltips: this.config.disableTabTips,
50546                     toolbar : this.config.toolbar
50547                 }
50548         );
50549         if(this.config.hideTabs){
50550             ts.stripWrap.setDisplayed(false);
50551         }
50552         this.tabs = ts;
50553         ts.resizeTabs = this.config.resizeTabs === true;
50554         ts.minTabWidth = this.config.minTabWidth || 40;
50555         ts.maxTabWidth = this.config.maxTabWidth || 250;
50556         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50557         ts.monitorResize = false;
50558         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50559         ts.bodyEl.addClass('x-layout-tabs-body');
50560         this.panels.each(this.initPanelAsTab, this);
50561     },
50562
50563     initPanelAsTab : function(panel){
50564         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50565                     this.config.closeOnTab && panel.isClosable());
50566         if(panel.tabTip !== undefined){
50567             ti.setTooltip(panel.tabTip);
50568         }
50569         ti.on("activate", function(){
50570               this.setActivePanel(panel);
50571         }, this);
50572         if(this.config.closeOnTab){
50573             ti.on("beforeclose", function(t, e){
50574                 e.cancel = true;
50575                 this.remove(panel);
50576             }, this);
50577         }
50578         return ti;
50579     },
50580
50581     updatePanelTitle : function(panel, title){
50582         if(this.activePanel == panel){
50583             this.updateTitle(title);
50584         }
50585         if(this.tabs){
50586             var ti = this.tabs.getTab(panel.getEl().id);
50587             ti.setText(title);
50588             if(panel.tabTip !== undefined){
50589                 ti.setTooltip(panel.tabTip);
50590             }
50591         }
50592     },
50593
50594     updateTitle : function(title){
50595         if(this.titleTextEl && !this.config.title){
50596             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50597         }
50598     },
50599
50600     setActivePanel : function(panel){
50601         panel = this.getPanel(panel);
50602         if(this.activePanel && this.activePanel != panel){
50603             this.activePanel.setActiveState(false);
50604         }
50605         this.activePanel = panel;
50606         panel.setActiveState(true);
50607         if(this.panelSize){
50608             panel.setSize(this.panelSize.width, this.panelSize.height);
50609         }
50610         if(this.closeBtn){
50611             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50612         }
50613         this.updateTitle(panel.getTitle());
50614         if(this.tabs){
50615             this.fireEvent("invalidated", this);
50616         }
50617         this.fireEvent("panelactivated", this, panel);
50618     },
50619
50620     /**
50621      * Shows the specified panel.
50622      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50623      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50624      */
50625     showPanel : function(panel)
50626     {
50627         panel = this.getPanel(panel);
50628         if(panel){
50629             if(this.tabs){
50630                 var tab = this.tabs.getTab(panel.getEl().id);
50631                 if(tab.isHidden()){
50632                     this.tabs.unhideTab(tab.id);
50633                 }
50634                 tab.activate();
50635             }else{
50636                 this.setActivePanel(panel);
50637             }
50638         }
50639         return panel;
50640     },
50641
50642     /**
50643      * Get the active panel for this region.
50644      * @return {Roo.ContentPanel} The active panel or null
50645      */
50646     getActivePanel : function(){
50647         return this.activePanel;
50648     },
50649
50650     validateVisibility : function(){
50651         if(this.panels.getCount() < 1){
50652             this.updateTitle("&#160;");
50653             this.closeBtn.hide();
50654             this.hide();
50655         }else{
50656             if(!this.isVisible()){
50657                 this.show();
50658             }
50659         }
50660     },
50661
50662     /**
50663      * Adds the passed ContentPanel(s) to this region.
50664      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50665      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50666      */
50667     add : function(panel){
50668         if(arguments.length > 1){
50669             for(var i = 0, len = arguments.length; i < len; i++) {
50670                 this.add(arguments[i]);
50671             }
50672             return null;
50673         }
50674         if(this.hasPanel(panel)){
50675             this.showPanel(panel);
50676             return panel;
50677         }
50678         panel.setRegion(this);
50679         this.panels.add(panel);
50680         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50681             this.bodyEl.dom.appendChild(panel.getEl().dom);
50682             if(panel.background !== true){
50683                 this.setActivePanel(panel);
50684             }
50685             this.fireEvent("paneladded", this, panel);
50686             return panel;
50687         }
50688         if(!this.tabs){
50689             this.initTabs();
50690         }else{
50691             this.initPanelAsTab(panel);
50692         }
50693         if(panel.background !== true){
50694             this.tabs.activate(panel.getEl().id);
50695         }
50696         this.fireEvent("paneladded", this, panel);
50697         return panel;
50698     },
50699
50700     /**
50701      * Hides the tab for the specified panel.
50702      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50703      */
50704     hidePanel : function(panel){
50705         if(this.tabs && (panel = this.getPanel(panel))){
50706             this.tabs.hideTab(panel.getEl().id);
50707         }
50708     },
50709
50710     /**
50711      * Unhides the tab for a previously hidden panel.
50712      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50713      */
50714     unhidePanel : function(panel){
50715         if(this.tabs && (panel = this.getPanel(panel))){
50716             this.tabs.unhideTab(panel.getEl().id);
50717         }
50718     },
50719
50720     clearPanels : function(){
50721         while(this.panels.getCount() > 0){
50722              this.remove(this.panels.first());
50723         }
50724     },
50725
50726     /**
50727      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50728      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50729      * @param {Boolean} preservePanel Overrides the config preservePanel option
50730      * @return {Roo.ContentPanel} The panel that was removed
50731      */
50732     remove : function(panel, preservePanel){
50733         panel = this.getPanel(panel);
50734         if(!panel){
50735             return null;
50736         }
50737         var e = {};
50738         this.fireEvent("beforeremove", this, panel, e);
50739         if(e.cancel === true){
50740             return null;
50741         }
50742         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50743         var panelId = panel.getId();
50744         this.panels.removeKey(panelId);
50745         if(preservePanel){
50746             document.body.appendChild(panel.getEl().dom);
50747         }
50748         if(this.tabs){
50749             this.tabs.removeTab(panel.getEl().id);
50750         }else if (!preservePanel){
50751             this.bodyEl.dom.removeChild(panel.getEl().dom);
50752         }
50753         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50754             var p = this.panels.first();
50755             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50756             tempEl.appendChild(p.getEl().dom);
50757             this.bodyEl.update("");
50758             this.bodyEl.dom.appendChild(p.getEl().dom);
50759             tempEl = null;
50760             this.updateTitle(p.getTitle());
50761             this.tabs = null;
50762             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50763             this.setActivePanel(p);
50764         }
50765         panel.setRegion(null);
50766         if(this.activePanel == panel){
50767             this.activePanel = null;
50768         }
50769         if(this.config.autoDestroy !== false && preservePanel !== true){
50770             try{panel.destroy();}catch(e){}
50771         }
50772         this.fireEvent("panelremoved", this, panel);
50773         return panel;
50774     },
50775
50776     /**
50777      * Returns the TabPanel component used by this region
50778      * @return {Roo.TabPanel}
50779      */
50780     getTabs : function(){
50781         return this.tabs;
50782     },
50783
50784     createTool : function(parentEl, className){
50785         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50786             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50787         btn.addClassOnOver("x-layout-tools-button-over");
50788         return btn;
50789     }
50790 });/*
50791  * Based on:
50792  * Ext JS Library 1.1.1
50793  * Copyright(c) 2006-2007, Ext JS, LLC.
50794  *
50795  * Originally Released Under LGPL - original licence link has changed is not relivant.
50796  *
50797  * Fork - LGPL
50798  * <script type="text/javascript">
50799  */
50800  
50801
50802
50803 /**
50804  * @class Roo.SplitLayoutRegion
50805  * @extends Roo.LayoutRegion
50806  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50807  */
50808 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50809     this.cursor = cursor;
50810     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50811 };
50812
50813 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50814     splitTip : "Drag to resize.",
50815     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50816     useSplitTips : false,
50817
50818     applyConfig : function(config){
50819         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50820         if(config.split){
50821             if(!this.split){
50822                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50823                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50824                 /** The SplitBar for this region 
50825                 * @type Roo.SplitBar */
50826                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50827                 this.split.on("moved", this.onSplitMove, this);
50828                 this.split.useShim = config.useShim === true;
50829                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50830                 if(this.useSplitTips){
50831                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50832                 }
50833                 if(config.collapsible){
50834                     this.split.el.on("dblclick", this.collapse,  this);
50835                 }
50836             }
50837             if(typeof config.minSize != "undefined"){
50838                 this.split.minSize = config.minSize;
50839             }
50840             if(typeof config.maxSize != "undefined"){
50841                 this.split.maxSize = config.maxSize;
50842             }
50843             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50844                 this.hideSplitter();
50845             }
50846         }
50847     },
50848
50849     getHMaxSize : function(){
50850          var cmax = this.config.maxSize || 10000;
50851          var center = this.mgr.getRegion("center");
50852          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50853     },
50854
50855     getVMaxSize : function(){
50856          var cmax = this.config.maxSize || 10000;
50857          var center = this.mgr.getRegion("center");
50858          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50859     },
50860
50861     onSplitMove : function(split, newSize){
50862         this.fireEvent("resized", this, newSize);
50863     },
50864     
50865     /** 
50866      * Returns the {@link Roo.SplitBar} for this region.
50867      * @return {Roo.SplitBar}
50868      */
50869     getSplitBar : function(){
50870         return this.split;
50871     },
50872     
50873     hide : function(){
50874         this.hideSplitter();
50875         Roo.SplitLayoutRegion.superclass.hide.call(this);
50876     },
50877
50878     hideSplitter : function(){
50879         if(this.split){
50880             this.split.el.setLocation(-2000,-2000);
50881             this.split.el.hide();
50882         }
50883     },
50884
50885     show : function(){
50886         if(this.split){
50887             this.split.el.show();
50888         }
50889         Roo.SplitLayoutRegion.superclass.show.call(this);
50890     },
50891     
50892     beforeSlide: function(){
50893         if(Roo.isGecko){// firefox overflow auto bug workaround
50894             this.bodyEl.clip();
50895             if(this.tabs) this.tabs.bodyEl.clip();
50896             if(this.activePanel){
50897                 this.activePanel.getEl().clip();
50898                 
50899                 if(this.activePanel.beforeSlide){
50900                     this.activePanel.beforeSlide();
50901                 }
50902             }
50903         }
50904     },
50905     
50906     afterSlide : function(){
50907         if(Roo.isGecko){// firefox overflow auto bug workaround
50908             this.bodyEl.unclip();
50909             if(this.tabs) this.tabs.bodyEl.unclip();
50910             if(this.activePanel){
50911                 this.activePanel.getEl().unclip();
50912                 if(this.activePanel.afterSlide){
50913                     this.activePanel.afterSlide();
50914                 }
50915             }
50916         }
50917     },
50918
50919     initAutoHide : function(){
50920         if(this.autoHide !== false){
50921             if(!this.autoHideHd){
50922                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50923                 this.autoHideHd = {
50924                     "mouseout": function(e){
50925                         if(!e.within(this.el, true)){
50926                             st.delay(500);
50927                         }
50928                     },
50929                     "mouseover" : function(e){
50930                         st.cancel();
50931                     },
50932                     scope : this
50933                 };
50934             }
50935             this.el.on(this.autoHideHd);
50936         }
50937     },
50938
50939     clearAutoHide : function(){
50940         if(this.autoHide !== false){
50941             this.el.un("mouseout", this.autoHideHd.mouseout);
50942             this.el.un("mouseover", this.autoHideHd.mouseover);
50943         }
50944     },
50945
50946     clearMonitor : function(){
50947         Roo.get(document).un("click", this.slideInIf, this);
50948     },
50949
50950     // these names are backwards but not changed for compat
50951     slideOut : function(){
50952         if(this.isSlid || this.el.hasActiveFx()){
50953             return;
50954         }
50955         this.isSlid = true;
50956         if(this.collapseBtn){
50957             this.collapseBtn.hide();
50958         }
50959         this.closeBtnState = this.closeBtn.getStyle('display');
50960         this.closeBtn.hide();
50961         if(this.stickBtn){
50962             this.stickBtn.show();
50963         }
50964         this.el.show();
50965         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50966         this.beforeSlide();
50967         this.el.setStyle("z-index", 10001);
50968         this.el.slideIn(this.getSlideAnchor(), {
50969             callback: function(){
50970                 this.afterSlide();
50971                 this.initAutoHide();
50972                 Roo.get(document).on("click", this.slideInIf, this);
50973                 this.fireEvent("slideshow", this);
50974             },
50975             scope: this,
50976             block: true
50977         });
50978     },
50979
50980     afterSlideIn : function(){
50981         this.clearAutoHide();
50982         this.isSlid = false;
50983         this.clearMonitor();
50984         this.el.setStyle("z-index", "");
50985         if(this.collapseBtn){
50986             this.collapseBtn.show();
50987         }
50988         this.closeBtn.setStyle('display', this.closeBtnState);
50989         if(this.stickBtn){
50990             this.stickBtn.hide();
50991         }
50992         this.fireEvent("slidehide", this);
50993     },
50994
50995     slideIn : function(cb){
50996         if(!this.isSlid || this.el.hasActiveFx()){
50997             Roo.callback(cb);
50998             return;
50999         }
51000         this.isSlid = false;
51001         this.beforeSlide();
51002         this.el.slideOut(this.getSlideAnchor(), {
51003             callback: function(){
51004                 this.el.setLeftTop(-10000, -10000);
51005                 this.afterSlide();
51006                 this.afterSlideIn();
51007                 Roo.callback(cb);
51008             },
51009             scope: this,
51010             block: true
51011         });
51012     },
51013     
51014     slideInIf : function(e){
51015         if(!e.within(this.el)){
51016             this.slideIn();
51017         }
51018     },
51019
51020     animateCollapse : function(){
51021         this.beforeSlide();
51022         this.el.setStyle("z-index", 20000);
51023         var anchor = this.getSlideAnchor();
51024         this.el.slideOut(anchor, {
51025             callback : function(){
51026                 this.el.setStyle("z-index", "");
51027                 this.collapsedEl.slideIn(anchor, {duration:.3});
51028                 this.afterSlide();
51029                 this.el.setLocation(-10000,-10000);
51030                 this.el.hide();
51031                 this.fireEvent("collapsed", this);
51032             },
51033             scope: this,
51034             block: true
51035         });
51036     },
51037
51038     animateExpand : function(){
51039         this.beforeSlide();
51040         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
51041         this.el.setStyle("z-index", 20000);
51042         this.collapsedEl.hide({
51043             duration:.1
51044         });
51045         this.el.slideIn(this.getSlideAnchor(), {
51046             callback : function(){
51047                 this.el.setStyle("z-index", "");
51048                 this.afterSlide();
51049                 if(this.split){
51050                     this.split.el.show();
51051                 }
51052                 this.fireEvent("invalidated", this);
51053                 this.fireEvent("expanded", this);
51054             },
51055             scope: this,
51056             block: true
51057         });
51058     },
51059
51060     anchors : {
51061         "west" : "left",
51062         "east" : "right",
51063         "north" : "top",
51064         "south" : "bottom"
51065     },
51066
51067     sanchors : {
51068         "west" : "l",
51069         "east" : "r",
51070         "north" : "t",
51071         "south" : "b"
51072     },
51073
51074     canchors : {
51075         "west" : "tl-tr",
51076         "east" : "tr-tl",
51077         "north" : "tl-bl",
51078         "south" : "bl-tl"
51079     },
51080
51081     getAnchor : function(){
51082         return this.anchors[this.position];
51083     },
51084
51085     getCollapseAnchor : function(){
51086         return this.canchors[this.position];
51087     },
51088
51089     getSlideAnchor : function(){
51090         return this.sanchors[this.position];
51091     },
51092
51093     getAlignAdj : function(){
51094         var cm = this.cmargins;
51095         switch(this.position){
51096             case "west":
51097                 return [0, 0];
51098             break;
51099             case "east":
51100                 return [0, 0];
51101             break;
51102             case "north":
51103                 return [0, 0];
51104             break;
51105             case "south":
51106                 return [0, 0];
51107             break;
51108         }
51109     },
51110
51111     getExpandAdj : function(){
51112         var c = this.collapsedEl, cm = this.cmargins;
51113         switch(this.position){
51114             case "west":
51115                 return [-(cm.right+c.getWidth()+cm.left), 0];
51116             break;
51117             case "east":
51118                 return [cm.right+c.getWidth()+cm.left, 0];
51119             break;
51120             case "north":
51121                 return [0, -(cm.top+cm.bottom+c.getHeight())];
51122             break;
51123             case "south":
51124                 return [0, cm.top+cm.bottom+c.getHeight()];
51125             break;
51126         }
51127     }
51128 });/*
51129  * Based on:
51130  * Ext JS Library 1.1.1
51131  * Copyright(c) 2006-2007, Ext JS, LLC.
51132  *
51133  * Originally Released Under LGPL - original licence link has changed is not relivant.
51134  *
51135  * Fork - LGPL
51136  * <script type="text/javascript">
51137  */
51138 /*
51139  * These classes are private internal classes
51140  */
51141 Roo.CenterLayoutRegion = function(mgr, config){
51142     Roo.LayoutRegion.call(this, mgr, config, "center");
51143     this.visible = true;
51144     this.minWidth = config.minWidth || 20;
51145     this.minHeight = config.minHeight || 20;
51146 };
51147
51148 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
51149     hide : function(){
51150         // center panel can't be hidden
51151     },
51152     
51153     show : function(){
51154         // center panel can't be hidden
51155     },
51156     
51157     getMinWidth: function(){
51158         return this.minWidth;
51159     },
51160     
51161     getMinHeight: function(){
51162         return this.minHeight;
51163     }
51164 });
51165
51166
51167 Roo.NorthLayoutRegion = function(mgr, config){
51168     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
51169     if(this.split){
51170         this.split.placement = Roo.SplitBar.TOP;
51171         this.split.orientation = Roo.SplitBar.VERTICAL;
51172         this.split.el.addClass("x-layout-split-v");
51173     }
51174     var size = config.initialSize || config.height;
51175     if(typeof size != "undefined"){
51176         this.el.setHeight(size);
51177     }
51178 };
51179 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
51180     orientation: Roo.SplitBar.VERTICAL,
51181     getBox : function(){
51182         if(this.collapsed){
51183             return this.collapsedEl.getBox();
51184         }
51185         var box = this.el.getBox();
51186         if(this.split){
51187             box.height += this.split.el.getHeight();
51188         }
51189         return box;
51190     },
51191     
51192     updateBox : function(box){
51193         if(this.split && !this.collapsed){
51194             box.height -= this.split.el.getHeight();
51195             this.split.el.setLeft(box.x);
51196             this.split.el.setTop(box.y+box.height);
51197             this.split.el.setWidth(box.width);
51198         }
51199         if(this.collapsed){
51200             this.updateBody(box.width, null);
51201         }
51202         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51203     }
51204 });
51205
51206 Roo.SouthLayoutRegion = function(mgr, config){
51207     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
51208     if(this.split){
51209         this.split.placement = Roo.SplitBar.BOTTOM;
51210         this.split.orientation = Roo.SplitBar.VERTICAL;
51211         this.split.el.addClass("x-layout-split-v");
51212     }
51213     var size = config.initialSize || config.height;
51214     if(typeof size != "undefined"){
51215         this.el.setHeight(size);
51216     }
51217 };
51218 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
51219     orientation: Roo.SplitBar.VERTICAL,
51220     getBox : function(){
51221         if(this.collapsed){
51222             return this.collapsedEl.getBox();
51223         }
51224         var box = this.el.getBox();
51225         if(this.split){
51226             var sh = this.split.el.getHeight();
51227             box.height += sh;
51228             box.y -= sh;
51229         }
51230         return box;
51231     },
51232     
51233     updateBox : function(box){
51234         if(this.split && !this.collapsed){
51235             var sh = this.split.el.getHeight();
51236             box.height -= sh;
51237             box.y += sh;
51238             this.split.el.setLeft(box.x);
51239             this.split.el.setTop(box.y-sh);
51240             this.split.el.setWidth(box.width);
51241         }
51242         if(this.collapsed){
51243             this.updateBody(box.width, null);
51244         }
51245         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51246     }
51247 });
51248
51249 Roo.EastLayoutRegion = function(mgr, config){
51250     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
51251     if(this.split){
51252         this.split.placement = Roo.SplitBar.RIGHT;
51253         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51254         this.split.el.addClass("x-layout-split-h");
51255     }
51256     var size = config.initialSize || config.width;
51257     if(typeof size != "undefined"){
51258         this.el.setWidth(size);
51259     }
51260 };
51261 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
51262     orientation: Roo.SplitBar.HORIZONTAL,
51263     getBox : function(){
51264         if(this.collapsed){
51265             return this.collapsedEl.getBox();
51266         }
51267         var box = this.el.getBox();
51268         if(this.split){
51269             var sw = this.split.el.getWidth();
51270             box.width += sw;
51271             box.x -= sw;
51272         }
51273         return box;
51274     },
51275
51276     updateBox : function(box){
51277         if(this.split && !this.collapsed){
51278             var sw = this.split.el.getWidth();
51279             box.width -= sw;
51280             this.split.el.setLeft(box.x);
51281             this.split.el.setTop(box.y);
51282             this.split.el.setHeight(box.height);
51283             box.x += sw;
51284         }
51285         if(this.collapsed){
51286             this.updateBody(null, box.height);
51287         }
51288         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51289     }
51290 });
51291
51292 Roo.WestLayoutRegion = function(mgr, config){
51293     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
51294     if(this.split){
51295         this.split.placement = Roo.SplitBar.LEFT;
51296         this.split.orientation = Roo.SplitBar.HORIZONTAL;
51297         this.split.el.addClass("x-layout-split-h");
51298     }
51299     var size = config.initialSize || config.width;
51300     if(typeof size != "undefined"){
51301         this.el.setWidth(size);
51302     }
51303 };
51304 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
51305     orientation: Roo.SplitBar.HORIZONTAL,
51306     getBox : function(){
51307         if(this.collapsed){
51308             return this.collapsedEl.getBox();
51309         }
51310         var box = this.el.getBox();
51311         if(this.split){
51312             box.width += this.split.el.getWidth();
51313         }
51314         return box;
51315     },
51316     
51317     updateBox : function(box){
51318         if(this.split && !this.collapsed){
51319             var sw = this.split.el.getWidth();
51320             box.width -= sw;
51321             this.split.el.setLeft(box.x+box.width);
51322             this.split.el.setTop(box.y);
51323             this.split.el.setHeight(box.height);
51324         }
51325         if(this.collapsed){
51326             this.updateBody(null, box.height);
51327         }
51328         Roo.LayoutRegion.prototype.updateBox.call(this, box);
51329     }
51330 });
51331 /*
51332  * Based on:
51333  * Ext JS Library 1.1.1
51334  * Copyright(c) 2006-2007, Ext JS, LLC.
51335  *
51336  * Originally Released Under LGPL - original licence link has changed is not relivant.
51337  *
51338  * Fork - LGPL
51339  * <script type="text/javascript">
51340  */
51341  
51342  
51343 /*
51344  * Private internal class for reading and applying state
51345  */
51346 Roo.LayoutStateManager = function(layout){
51347      // default empty state
51348      this.state = {
51349         north: {},
51350         south: {},
51351         east: {},
51352         west: {}       
51353     };
51354 };
51355
51356 Roo.LayoutStateManager.prototype = {
51357     init : function(layout, provider){
51358         this.provider = provider;
51359         var state = provider.get(layout.id+"-layout-state");
51360         if(state){
51361             var wasUpdating = layout.isUpdating();
51362             if(!wasUpdating){
51363                 layout.beginUpdate();
51364             }
51365             for(var key in state){
51366                 if(typeof state[key] != "function"){
51367                     var rstate = state[key];
51368                     var r = layout.getRegion(key);
51369                     if(r && rstate){
51370                         if(rstate.size){
51371                             r.resizeTo(rstate.size);
51372                         }
51373                         if(rstate.collapsed == true){
51374                             r.collapse(true);
51375                         }else{
51376                             r.expand(null, true);
51377                         }
51378                     }
51379                 }
51380             }
51381             if(!wasUpdating){
51382                 layout.endUpdate();
51383             }
51384             this.state = state; 
51385         }
51386         this.layout = layout;
51387         layout.on("regionresized", this.onRegionResized, this);
51388         layout.on("regioncollapsed", this.onRegionCollapsed, this);
51389         layout.on("regionexpanded", this.onRegionExpanded, this);
51390     },
51391     
51392     storeState : function(){
51393         this.provider.set(this.layout.id+"-layout-state", this.state);
51394     },
51395     
51396     onRegionResized : function(region, newSize){
51397         this.state[region.getPosition()].size = newSize;
51398         this.storeState();
51399     },
51400     
51401     onRegionCollapsed : function(region){
51402         this.state[region.getPosition()].collapsed = true;
51403         this.storeState();
51404     },
51405     
51406     onRegionExpanded : function(region){
51407         this.state[region.getPosition()].collapsed = false;
51408         this.storeState();
51409     }
51410 };/*
51411  * Based on:
51412  * Ext JS Library 1.1.1
51413  * Copyright(c) 2006-2007, Ext JS, LLC.
51414  *
51415  * Originally Released Under LGPL - original licence link has changed is not relivant.
51416  *
51417  * Fork - LGPL
51418  * <script type="text/javascript">
51419  */
51420 /**
51421  * @class Roo.ContentPanel
51422  * @extends Roo.util.Observable
51423  * A basic ContentPanel element.
51424  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51425  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51426  * @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
51427  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51428  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51429  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51430  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51431  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51432  * @cfg {String} title          The title for this panel
51433  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51434  * @cfg {String} url            Calls {@link #setUrl} with this value
51435  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51436  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51437  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51438  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51439
51440  * @constructor
51441  * Create a new ContentPanel.
51442  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51443  * @param {String/Object} config A string to set only the title or a config object
51444  * @param {String} content (optional) Set the HTML content for this panel
51445  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51446  */
51447 Roo.ContentPanel = function(el, config, content){
51448     
51449      
51450     /*
51451     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51452         config = el;
51453         el = Roo.id();
51454     }
51455     if (config && config.parentLayout) { 
51456         el = config.parentLayout.el.createChild(); 
51457     }
51458     */
51459     if(el.autoCreate){ // xtype is available if this is called from factory
51460         config = el;
51461         el = Roo.id();
51462     }
51463     this.el = Roo.get(el);
51464     if(!this.el && config && config.autoCreate){
51465         if(typeof config.autoCreate == "object"){
51466             if(!config.autoCreate.id){
51467                 config.autoCreate.id = config.id||el;
51468             }
51469             this.el = Roo.DomHelper.append(document.body,
51470                         config.autoCreate, true);
51471         }else{
51472             this.el = Roo.DomHelper.append(document.body,
51473                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51474         }
51475     }
51476     this.closable = false;
51477     this.loaded = false;
51478     this.active = false;
51479     if(typeof config == "string"){
51480         this.title = config;
51481     }else{
51482         Roo.apply(this, config);
51483     }
51484     
51485     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51486         this.wrapEl = this.el.wrap();
51487         this.toolbar.container = this.el.insertSibling(false, 'before');
51488         this.toolbar = new Roo.Toolbar(this.toolbar);
51489     }
51490     
51491     // xtype created footer. - not sure if will work as we normally have to render first..
51492     if (this.footer && !this.footer.el && this.footer.xtype) {
51493         if (!this.wrapEl) {
51494             this.wrapEl = this.el.wrap();
51495         }
51496     
51497         this.footer.container = this.wrapEl.createChild();
51498          
51499         this.footer = Roo.factory(this.footer, Roo);
51500         
51501     }
51502     
51503     if(this.resizeEl){
51504         this.resizeEl = Roo.get(this.resizeEl, true);
51505     }else{
51506         this.resizeEl = this.el;
51507     }
51508     // handle view.xtype
51509     
51510  
51511     
51512     
51513     this.addEvents({
51514         /**
51515          * @event activate
51516          * Fires when this panel is activated. 
51517          * @param {Roo.ContentPanel} this
51518          */
51519         "activate" : true,
51520         /**
51521          * @event deactivate
51522          * Fires when this panel is activated. 
51523          * @param {Roo.ContentPanel} this
51524          */
51525         "deactivate" : true,
51526
51527         /**
51528          * @event resize
51529          * Fires when this panel is resized if fitToFrame is true.
51530          * @param {Roo.ContentPanel} this
51531          * @param {Number} width The width after any component adjustments
51532          * @param {Number} height The height after any component adjustments
51533          */
51534         "resize" : true,
51535         
51536          /**
51537          * @event render
51538          * Fires when this tab is created
51539          * @param {Roo.ContentPanel} this
51540          */
51541         "render" : true
51542         
51543         
51544         
51545     });
51546     
51547
51548     
51549     
51550     if(this.autoScroll){
51551         this.resizeEl.setStyle("overflow", "auto");
51552     } else {
51553         // fix randome scrolling
51554         this.el.on('scroll', function() {
51555             Roo.log('fix random scolling');
51556             this.scrollTo('top',0); 
51557         });
51558     }
51559     content = content || this.content;
51560     if(content){
51561         this.setContent(content);
51562     }
51563     if(config && config.url){
51564         this.setUrl(this.url, this.params, this.loadOnce);
51565     }
51566     
51567     
51568     
51569     Roo.ContentPanel.superclass.constructor.call(this);
51570     
51571     if (this.view && typeof(this.view.xtype) != 'undefined') {
51572         this.view.el = this.el.appendChild(document.createElement("div"));
51573         this.view = Roo.factory(this.view); 
51574         this.view.render  &&  this.view.render(false, '');  
51575     }
51576     
51577     
51578     this.fireEvent('render', this);
51579 };
51580
51581 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51582     tabTip:'',
51583     setRegion : function(region){
51584         this.region = region;
51585         if(region){
51586            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51587         }else{
51588            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51589         } 
51590     },
51591     
51592     /**
51593      * Returns the toolbar for this Panel if one was configured. 
51594      * @return {Roo.Toolbar} 
51595      */
51596     getToolbar : function(){
51597         return this.toolbar;
51598     },
51599     
51600     setActiveState : function(active){
51601         this.active = active;
51602         if(!active){
51603             this.fireEvent("deactivate", this);
51604         }else{
51605             this.fireEvent("activate", this);
51606         }
51607     },
51608     /**
51609      * Updates this panel's element
51610      * @param {String} content The new content
51611      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51612     */
51613     setContent : function(content, loadScripts){
51614         this.el.update(content, loadScripts);
51615     },
51616
51617     ignoreResize : function(w, h){
51618         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51619             return true;
51620         }else{
51621             this.lastSize = {width: w, height: h};
51622             return false;
51623         }
51624     },
51625     /**
51626      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51627      * @return {Roo.UpdateManager} The UpdateManager
51628      */
51629     getUpdateManager : function(){
51630         return this.el.getUpdateManager();
51631     },
51632      /**
51633      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51634      * @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:
51635 <pre><code>
51636 panel.load({
51637     url: "your-url.php",
51638     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51639     callback: yourFunction,
51640     scope: yourObject, //(optional scope)
51641     discardUrl: false,
51642     nocache: false,
51643     text: "Loading...",
51644     timeout: 30,
51645     scripts: false
51646 });
51647 </code></pre>
51648      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51649      * 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.
51650      * @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}
51651      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51652      * @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.
51653      * @return {Roo.ContentPanel} this
51654      */
51655     load : function(){
51656         var um = this.el.getUpdateManager();
51657         um.update.apply(um, arguments);
51658         return this;
51659     },
51660
51661
51662     /**
51663      * 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.
51664      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51665      * @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)
51666      * @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)
51667      * @return {Roo.UpdateManager} The UpdateManager
51668      */
51669     setUrl : function(url, params, loadOnce){
51670         if(this.refreshDelegate){
51671             this.removeListener("activate", this.refreshDelegate);
51672         }
51673         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51674         this.on("activate", this.refreshDelegate);
51675         return this.el.getUpdateManager();
51676     },
51677     
51678     _handleRefresh : function(url, params, loadOnce){
51679         if(!loadOnce || !this.loaded){
51680             var updater = this.el.getUpdateManager();
51681             updater.update(url, params, this._setLoaded.createDelegate(this));
51682         }
51683     },
51684     
51685     _setLoaded : function(){
51686         this.loaded = true;
51687     }, 
51688     
51689     /**
51690      * Returns this panel's id
51691      * @return {String} 
51692      */
51693     getId : function(){
51694         return this.el.id;
51695     },
51696     
51697     /** 
51698      * Returns this panel's element - used by regiosn to add.
51699      * @return {Roo.Element} 
51700      */
51701     getEl : function(){
51702         return this.wrapEl || this.el;
51703     },
51704     
51705     adjustForComponents : function(width, height)
51706     {
51707         //Roo.log('adjustForComponents ');
51708         if(this.resizeEl != this.el){
51709             width -= this.el.getFrameWidth('lr');
51710             height -= this.el.getFrameWidth('tb');
51711         }
51712         if(this.toolbar){
51713             var te = this.toolbar.getEl();
51714             height -= te.getHeight();
51715             te.setWidth(width);
51716         }
51717         if(this.footer){
51718             var te = this.footer.getEl();
51719             Roo.log("footer:" + te.getHeight());
51720             
51721             height -= te.getHeight();
51722             te.setWidth(width);
51723         }
51724         
51725         
51726         if(this.adjustments){
51727             width += this.adjustments[0];
51728             height += this.adjustments[1];
51729         }
51730         return {"width": width, "height": height};
51731     },
51732     
51733     setSize : function(width, height){
51734         if(this.fitToFrame && !this.ignoreResize(width, height)){
51735             if(this.fitContainer && this.resizeEl != this.el){
51736                 this.el.setSize(width, height);
51737             }
51738             var size = this.adjustForComponents(width, height);
51739             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51740             this.fireEvent('resize', this, size.width, size.height);
51741         }
51742     },
51743     
51744     /**
51745      * Returns this panel's title
51746      * @return {String} 
51747      */
51748     getTitle : function(){
51749         return this.title;
51750     },
51751     
51752     /**
51753      * Set this panel's title
51754      * @param {String} title
51755      */
51756     setTitle : function(title){
51757         this.title = title;
51758         if(this.region){
51759             this.region.updatePanelTitle(this, title);
51760         }
51761     },
51762     
51763     /**
51764      * Returns true is this panel was configured to be closable
51765      * @return {Boolean} 
51766      */
51767     isClosable : function(){
51768         return this.closable;
51769     },
51770     
51771     beforeSlide : function(){
51772         this.el.clip();
51773         this.resizeEl.clip();
51774     },
51775     
51776     afterSlide : function(){
51777         this.el.unclip();
51778         this.resizeEl.unclip();
51779     },
51780     
51781     /**
51782      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51783      *   Will fail silently if the {@link #setUrl} method has not been called.
51784      *   This does not activate the panel, just updates its content.
51785      */
51786     refresh : function(){
51787         if(this.refreshDelegate){
51788            this.loaded = false;
51789            this.refreshDelegate();
51790         }
51791     },
51792     
51793     /**
51794      * Destroys this panel
51795      */
51796     destroy : function(){
51797         this.el.removeAllListeners();
51798         var tempEl = document.createElement("span");
51799         tempEl.appendChild(this.el.dom);
51800         tempEl.innerHTML = "";
51801         this.el.remove();
51802         this.el = null;
51803     },
51804     
51805     /**
51806      * form - if the content panel contains a form - this is a reference to it.
51807      * @type {Roo.form.Form}
51808      */
51809     form : false,
51810     /**
51811      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51812      *    This contains a reference to it.
51813      * @type {Roo.View}
51814      */
51815     view : false,
51816     
51817       /**
51818      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51819      * <pre><code>
51820
51821 layout.addxtype({
51822        xtype : 'Form',
51823        items: [ .... ]
51824    }
51825 );
51826
51827 </code></pre>
51828      * @param {Object} cfg Xtype definition of item to add.
51829      */
51830     
51831     addxtype : function(cfg) {
51832         // add form..
51833         if (cfg.xtype.match(/^Form$/)) {
51834             
51835             var el;
51836             //if (this.footer) {
51837             //    el = this.footer.container.insertSibling(false, 'before');
51838             //} else {
51839                 el = this.el.createChild();
51840             //}
51841
51842             this.form = new  Roo.form.Form(cfg);
51843             
51844             
51845             if ( this.form.allItems.length) this.form.render(el.dom);
51846             return this.form;
51847         }
51848         // should only have one of theses..
51849         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51850             // views.. should not be just added - used named prop 'view''
51851             
51852             cfg.el = this.el.appendChild(document.createElement("div"));
51853             // factory?
51854             
51855             var ret = new Roo.factory(cfg);
51856              
51857              ret.render && ret.render(false, ''); // render blank..
51858             this.view = ret;
51859             return ret;
51860         }
51861         return false;
51862     }
51863 });
51864
51865 /**
51866  * @class Roo.GridPanel
51867  * @extends Roo.ContentPanel
51868  * @constructor
51869  * Create a new GridPanel.
51870  * @param {Roo.grid.Grid} grid The grid for this panel
51871  * @param {String/Object} config A string to set only the panel's title, or a config object
51872  */
51873 Roo.GridPanel = function(grid, config){
51874     
51875   
51876     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51877         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51878         
51879     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51880     
51881     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51882     
51883     if(this.toolbar){
51884         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51885     }
51886     // xtype created footer. - not sure if will work as we normally have to render first..
51887     if (this.footer && !this.footer.el && this.footer.xtype) {
51888         
51889         this.footer.container = this.grid.getView().getFooterPanel(true);
51890         this.footer.dataSource = this.grid.dataSource;
51891         this.footer = Roo.factory(this.footer, Roo);
51892         
51893     }
51894     
51895     grid.monitorWindowResize = false; // turn off autosizing
51896     grid.autoHeight = false;
51897     grid.autoWidth = false;
51898     this.grid = grid;
51899     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51900 };
51901
51902 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51903     getId : function(){
51904         return this.grid.id;
51905     },
51906     
51907     /**
51908      * Returns the grid for this panel
51909      * @return {Roo.grid.Grid} 
51910      */
51911     getGrid : function(){
51912         return this.grid;    
51913     },
51914     
51915     setSize : function(width, height){
51916         if(!this.ignoreResize(width, height)){
51917             var grid = this.grid;
51918             var size = this.adjustForComponents(width, height);
51919             grid.getGridEl().setSize(size.width, size.height);
51920             grid.autoSize();
51921         }
51922     },
51923     
51924     beforeSlide : function(){
51925         this.grid.getView().scroller.clip();
51926     },
51927     
51928     afterSlide : function(){
51929         this.grid.getView().scroller.unclip();
51930     },
51931     
51932     destroy : function(){
51933         this.grid.destroy();
51934         delete this.grid;
51935         Roo.GridPanel.superclass.destroy.call(this); 
51936     }
51937 });
51938
51939
51940 /**
51941  * @class Roo.NestedLayoutPanel
51942  * @extends Roo.ContentPanel
51943  * @constructor
51944  * Create a new NestedLayoutPanel.
51945  * 
51946  * 
51947  * @param {Roo.BorderLayout} layout The layout for this panel
51948  * @param {String/Object} config A string to set only the title or a config object
51949  */
51950 Roo.NestedLayoutPanel = function(layout, config)
51951 {
51952     // construct with only one argument..
51953     /* FIXME - implement nicer consturctors
51954     if (layout.layout) {
51955         config = layout;
51956         layout = config.layout;
51957         delete config.layout;
51958     }
51959     if (layout.xtype && !layout.getEl) {
51960         // then layout needs constructing..
51961         layout = Roo.factory(layout, Roo);
51962     }
51963     */
51964     
51965     
51966     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51967     
51968     layout.monitorWindowResize = false; // turn off autosizing
51969     this.layout = layout;
51970     this.layout.getEl().addClass("x-layout-nested-layout");
51971     
51972     
51973     
51974     
51975 };
51976
51977 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51978
51979     setSize : function(width, height){
51980         if(!this.ignoreResize(width, height)){
51981             var size = this.adjustForComponents(width, height);
51982             var el = this.layout.getEl();
51983             el.setSize(size.width, size.height);
51984             var touch = el.dom.offsetWidth;
51985             this.layout.layout();
51986             // ie requires a double layout on the first pass
51987             if(Roo.isIE && !this.initialized){
51988                 this.initialized = true;
51989                 this.layout.layout();
51990             }
51991         }
51992     },
51993     
51994     // activate all subpanels if not currently active..
51995     
51996     setActiveState : function(active){
51997         this.active = active;
51998         if(!active){
51999             this.fireEvent("deactivate", this);
52000             return;
52001         }
52002         
52003         this.fireEvent("activate", this);
52004         // not sure if this should happen before or after..
52005         if (!this.layout) {
52006             return; // should not happen..
52007         }
52008         var reg = false;
52009         for (var r in this.layout.regions) {
52010             reg = this.layout.getRegion(r);
52011             if (reg.getActivePanel()) {
52012                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
52013                 reg.setActivePanel(reg.getActivePanel());
52014                 continue;
52015             }
52016             if (!reg.panels.length) {
52017                 continue;
52018             }
52019             reg.showPanel(reg.getPanel(0));
52020         }
52021         
52022         
52023         
52024         
52025     },
52026     
52027     /**
52028      * Returns the nested BorderLayout for this panel
52029      * @return {Roo.BorderLayout} 
52030      */
52031     getLayout : function(){
52032         return this.layout;
52033     },
52034     
52035      /**
52036      * Adds a xtype elements to the layout of the nested panel
52037      * <pre><code>
52038
52039 panel.addxtype({
52040        xtype : 'ContentPanel',
52041        region: 'west',
52042        items: [ .... ]
52043    }
52044 );
52045
52046 panel.addxtype({
52047         xtype : 'NestedLayoutPanel',
52048         region: 'west',
52049         layout: {
52050            center: { },
52051            west: { }   
52052         },
52053         items : [ ... list of content panels or nested layout panels.. ]
52054    }
52055 );
52056 </code></pre>
52057      * @param {Object} cfg Xtype definition of item to add.
52058      */
52059     addxtype : function(cfg) {
52060         return this.layout.addxtype(cfg);
52061     
52062     }
52063 });
52064
52065 Roo.ScrollPanel = function(el, config, content){
52066     config = config || {};
52067     config.fitToFrame = true;
52068     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
52069     
52070     this.el.dom.style.overflow = "hidden";
52071     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
52072     this.el.removeClass("x-layout-inactive-content");
52073     this.el.on("mousewheel", this.onWheel, this);
52074
52075     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
52076     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
52077     up.unselectable(); down.unselectable();
52078     up.on("click", this.scrollUp, this);
52079     down.on("click", this.scrollDown, this);
52080     up.addClassOnOver("x-scroller-btn-over");
52081     down.addClassOnOver("x-scroller-btn-over");
52082     up.addClassOnClick("x-scroller-btn-click");
52083     down.addClassOnClick("x-scroller-btn-click");
52084     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
52085
52086     this.resizeEl = this.el;
52087     this.el = wrap; this.up = up; this.down = down;
52088 };
52089
52090 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
52091     increment : 100,
52092     wheelIncrement : 5,
52093     scrollUp : function(){
52094         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
52095     },
52096
52097     scrollDown : function(){
52098         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
52099     },
52100
52101     afterScroll : function(){
52102         var el = this.resizeEl;
52103         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
52104         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52105         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
52106     },
52107
52108     setSize : function(){
52109         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
52110         this.afterScroll();
52111     },
52112
52113     onWheel : function(e){
52114         var d = e.getWheelDelta();
52115         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
52116         this.afterScroll();
52117         e.stopEvent();
52118     },
52119
52120     setContent : function(content, loadScripts){
52121         this.resizeEl.update(content, loadScripts);
52122     }
52123
52124 });
52125
52126
52127
52128
52129
52130
52131
52132
52133
52134 /**
52135  * @class Roo.TreePanel
52136  * @extends Roo.ContentPanel
52137  * @constructor
52138  * Create a new TreePanel. - defaults to fit/scoll contents.
52139  * @param {String/Object} config A string to set only the panel's title, or a config object
52140  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
52141  */
52142 Roo.TreePanel = function(config){
52143     var el = config.el;
52144     var tree = config.tree;
52145     delete config.tree; 
52146     delete config.el; // hopefull!
52147     
52148     // wrapper for IE7 strict & safari scroll issue
52149     
52150     var treeEl = el.createChild();
52151     config.resizeEl = treeEl;
52152     
52153     
52154     
52155     Roo.TreePanel.superclass.constructor.call(this, el, config);
52156  
52157  
52158     this.tree = new Roo.tree.TreePanel(treeEl , tree);
52159     //console.log(tree);
52160     this.on('activate', function()
52161     {
52162         if (this.tree.rendered) {
52163             return;
52164         }
52165         //console.log('render tree');
52166         this.tree.render();
52167     });
52168     // this should not be needed.. - it's actually the 'el' that resizes?
52169     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
52170     
52171     //this.on('resize',  function (cp, w, h) {
52172     //        this.tree.innerCt.setWidth(w);
52173     //        this.tree.innerCt.setHeight(h);
52174     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
52175     //});
52176
52177         
52178     
52179 };
52180
52181 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
52182     fitToFrame : true,
52183     autoScroll : true
52184 });
52185
52186
52187
52188
52189
52190
52191
52192
52193
52194
52195
52196 /*
52197  * Based on:
52198  * Ext JS Library 1.1.1
52199  * Copyright(c) 2006-2007, Ext JS, LLC.
52200  *
52201  * Originally Released Under LGPL - original licence link has changed is not relivant.
52202  *
52203  * Fork - LGPL
52204  * <script type="text/javascript">
52205  */
52206  
52207
52208 /**
52209  * @class Roo.ReaderLayout
52210  * @extends Roo.BorderLayout
52211  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
52212  * center region containing two nested regions (a top one for a list view and one for item preview below),
52213  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
52214  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
52215  * expedites the setup of the overall layout and regions for this common application style.
52216  * Example:
52217  <pre><code>
52218 var reader = new Roo.ReaderLayout();
52219 var CP = Roo.ContentPanel;  // shortcut for adding
52220
52221 reader.beginUpdate();
52222 reader.add("north", new CP("north", "North"));
52223 reader.add("west", new CP("west", {title: "West"}));
52224 reader.add("east", new CP("east", {title: "East"}));
52225
52226 reader.regions.listView.add(new CP("listView", "List"));
52227 reader.regions.preview.add(new CP("preview", "Preview"));
52228 reader.endUpdate();
52229 </code></pre>
52230 * @constructor
52231 * Create a new ReaderLayout
52232 * @param {Object} config Configuration options
52233 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
52234 * document.body if omitted)
52235 */
52236 Roo.ReaderLayout = function(config, renderTo){
52237     var c = config || {size:{}};
52238     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
52239         north: c.north !== false ? Roo.apply({
52240             split:false,
52241             initialSize: 32,
52242             titlebar: false
52243         }, c.north) : false,
52244         west: c.west !== false ? Roo.apply({
52245             split:true,
52246             initialSize: 200,
52247             minSize: 175,
52248             maxSize: 400,
52249             titlebar: true,
52250             collapsible: true,
52251             animate: true,
52252             margins:{left:5,right:0,bottom:5,top:5},
52253             cmargins:{left:5,right:5,bottom:5,top:5}
52254         }, c.west) : false,
52255         east: c.east !== false ? Roo.apply({
52256             split:true,
52257             initialSize: 200,
52258             minSize: 175,
52259             maxSize: 400,
52260             titlebar: true,
52261             collapsible: true,
52262             animate: true,
52263             margins:{left:0,right:5,bottom:5,top:5},
52264             cmargins:{left:5,right:5,bottom:5,top:5}
52265         }, c.east) : false,
52266         center: Roo.apply({
52267             tabPosition: 'top',
52268             autoScroll:false,
52269             closeOnTab: true,
52270             titlebar:false,
52271             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
52272         }, c.center)
52273     });
52274
52275     this.el.addClass('x-reader');
52276
52277     this.beginUpdate();
52278
52279     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
52280         south: c.preview !== false ? Roo.apply({
52281             split:true,
52282             initialSize: 200,
52283             minSize: 100,
52284             autoScroll:true,
52285             collapsible:true,
52286             titlebar: true,
52287             cmargins:{top:5,left:0, right:0, bottom:0}
52288         }, c.preview) : false,
52289         center: Roo.apply({
52290             autoScroll:false,
52291             titlebar:false,
52292             minHeight:200
52293         }, c.listView)
52294     });
52295     this.add('center', new Roo.NestedLayoutPanel(inner,
52296             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
52297
52298     this.endUpdate();
52299
52300     this.regions.preview = inner.getRegion('south');
52301     this.regions.listView = inner.getRegion('center');
52302 };
52303
52304 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
52305  * Based on:
52306  * Ext JS Library 1.1.1
52307  * Copyright(c) 2006-2007, Ext JS, LLC.
52308  *
52309  * Originally Released Under LGPL - original licence link has changed is not relivant.
52310  *
52311  * Fork - LGPL
52312  * <script type="text/javascript">
52313  */
52314  
52315 /**
52316  * @class Roo.grid.Grid
52317  * @extends Roo.util.Observable
52318  * This class represents the primary interface of a component based grid control.
52319  * <br><br>Usage:<pre><code>
52320  var grid = new Roo.grid.Grid("my-container-id", {
52321      ds: myDataStore,
52322      cm: myColModel,
52323      selModel: mySelectionModel,
52324      autoSizeColumns: true,
52325      monitorWindowResize: false,
52326      trackMouseOver: true
52327  });
52328  // set any options
52329  grid.render();
52330  * </code></pre>
52331  * <b>Common Problems:</b><br/>
52332  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
52333  * element will correct this<br/>
52334  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
52335  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
52336  * are unpredictable.<br/>
52337  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
52338  * grid to calculate dimensions/offsets.<br/>
52339   * @constructor
52340  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
52341  * The container MUST have some type of size defined for the grid to fill. The container will be
52342  * automatically set to position relative if it isn't already.
52343  * @param {Object} config A config object that sets properties on this grid.
52344  */
52345 Roo.grid.Grid = function(container, config){
52346         // initialize the container
52347         this.container = Roo.get(container);
52348         this.container.update("");
52349         this.container.setStyle("overflow", "hidden");
52350     this.container.addClass('x-grid-container');
52351
52352     this.id = this.container.id;
52353
52354     Roo.apply(this, config);
52355     // check and correct shorthanded configs
52356     if(this.ds){
52357         this.dataSource = this.ds;
52358         delete this.ds;
52359     }
52360     if(this.cm){
52361         this.colModel = this.cm;
52362         delete this.cm;
52363     }
52364     if(this.sm){
52365         this.selModel = this.sm;
52366         delete this.sm;
52367     }
52368
52369     if (this.selModel) {
52370         this.selModel = Roo.factory(this.selModel, Roo.grid);
52371         this.sm = this.selModel;
52372         this.sm.xmodule = this.xmodule || false;
52373     }
52374     if (typeof(this.colModel.config) == 'undefined') {
52375         this.colModel = new Roo.grid.ColumnModel(this.colModel);
52376         this.cm = this.colModel;
52377         this.cm.xmodule = this.xmodule || false;
52378     }
52379     if (this.dataSource) {
52380         this.dataSource= Roo.factory(this.dataSource, Roo.data);
52381         this.ds = this.dataSource;
52382         this.ds.xmodule = this.xmodule || false;
52383          
52384     }
52385     
52386     
52387     
52388     if(this.width){
52389         this.container.setWidth(this.width);
52390     }
52391
52392     if(this.height){
52393         this.container.setHeight(this.height);
52394     }
52395     /** @private */
52396         this.addEvents({
52397         // raw events
52398         /**
52399          * @event click
52400          * The raw click event for the entire grid.
52401          * @param {Roo.EventObject} e
52402          */
52403         "click" : true,
52404         /**
52405          * @event dblclick
52406          * The raw dblclick event for the entire grid.
52407          * @param {Roo.EventObject} e
52408          */
52409         "dblclick" : true,
52410         /**
52411          * @event contextmenu
52412          * The raw contextmenu event for the entire grid.
52413          * @param {Roo.EventObject} e
52414          */
52415         "contextmenu" : true,
52416         /**
52417          * @event mousedown
52418          * The raw mousedown event for the entire grid.
52419          * @param {Roo.EventObject} e
52420          */
52421         "mousedown" : true,
52422         /**
52423          * @event mouseup
52424          * The raw mouseup event for the entire grid.
52425          * @param {Roo.EventObject} e
52426          */
52427         "mouseup" : true,
52428         /**
52429          * @event mouseover
52430          * The raw mouseover event for the entire grid.
52431          * @param {Roo.EventObject} e
52432          */
52433         "mouseover" : true,
52434         /**
52435          * @event mouseout
52436          * The raw mouseout event for the entire grid.
52437          * @param {Roo.EventObject} e
52438          */
52439         "mouseout" : true,
52440         /**
52441          * @event keypress
52442          * The raw keypress event for the entire grid.
52443          * @param {Roo.EventObject} e
52444          */
52445         "keypress" : true,
52446         /**
52447          * @event keydown
52448          * The raw keydown event for the entire grid.
52449          * @param {Roo.EventObject} e
52450          */
52451         "keydown" : true,
52452
52453         // custom events
52454
52455         /**
52456          * @event cellclick
52457          * Fires when a cell is clicked
52458          * @param {Grid} this
52459          * @param {Number} rowIndex
52460          * @param {Number} columnIndex
52461          * @param {Roo.EventObject} e
52462          */
52463         "cellclick" : true,
52464         /**
52465          * @event celldblclick
52466          * Fires when a cell is double clicked
52467          * @param {Grid} this
52468          * @param {Number} rowIndex
52469          * @param {Number} columnIndex
52470          * @param {Roo.EventObject} e
52471          */
52472         "celldblclick" : true,
52473         /**
52474          * @event rowclick
52475          * Fires when a row is clicked
52476          * @param {Grid} this
52477          * @param {Number} rowIndex
52478          * @param {Roo.EventObject} e
52479          */
52480         "rowclick" : true,
52481         /**
52482          * @event rowdblclick
52483          * Fires when a row is double clicked
52484          * @param {Grid} this
52485          * @param {Number} rowIndex
52486          * @param {Roo.EventObject} e
52487          */
52488         "rowdblclick" : true,
52489         /**
52490          * @event headerclick
52491          * Fires when a header is clicked
52492          * @param {Grid} this
52493          * @param {Number} columnIndex
52494          * @param {Roo.EventObject} e
52495          */
52496         "headerclick" : true,
52497         /**
52498          * @event headerdblclick
52499          * Fires when a header cell is double clicked
52500          * @param {Grid} this
52501          * @param {Number} columnIndex
52502          * @param {Roo.EventObject} e
52503          */
52504         "headerdblclick" : true,
52505         /**
52506          * @event rowcontextmenu
52507          * Fires when a row is right clicked
52508          * @param {Grid} this
52509          * @param {Number} rowIndex
52510          * @param {Roo.EventObject} e
52511          */
52512         "rowcontextmenu" : true,
52513         /**
52514          * @event cellcontextmenu
52515          * Fires when a cell is right clicked
52516          * @param {Grid} this
52517          * @param {Number} rowIndex
52518          * @param {Number} cellIndex
52519          * @param {Roo.EventObject} e
52520          */
52521          "cellcontextmenu" : true,
52522         /**
52523          * @event headercontextmenu
52524          * Fires when a header is right clicked
52525          * @param {Grid} this
52526          * @param {Number} columnIndex
52527          * @param {Roo.EventObject} e
52528          */
52529         "headercontextmenu" : true,
52530         /**
52531          * @event bodyscroll
52532          * Fires when the body element is scrolled
52533          * @param {Number} scrollLeft
52534          * @param {Number} scrollTop
52535          */
52536         "bodyscroll" : true,
52537         /**
52538          * @event columnresize
52539          * Fires when the user resizes a column
52540          * @param {Number} columnIndex
52541          * @param {Number} newSize
52542          */
52543         "columnresize" : true,
52544         /**
52545          * @event columnmove
52546          * Fires when the user moves a column
52547          * @param {Number} oldIndex
52548          * @param {Number} newIndex
52549          */
52550         "columnmove" : true,
52551         /**
52552          * @event startdrag
52553          * Fires when row(s) start being dragged
52554          * @param {Grid} this
52555          * @param {Roo.GridDD} dd The drag drop object
52556          * @param {event} e The raw browser event
52557          */
52558         "startdrag" : true,
52559         /**
52560          * @event enddrag
52561          * Fires when a drag operation is complete
52562          * @param {Grid} this
52563          * @param {Roo.GridDD} dd The drag drop object
52564          * @param {event} e The raw browser event
52565          */
52566         "enddrag" : true,
52567         /**
52568          * @event dragdrop
52569          * Fires when dragged row(s) are dropped on a valid DD target
52570          * @param {Grid} this
52571          * @param {Roo.GridDD} dd The drag drop object
52572          * @param {String} targetId The target drag drop object
52573          * @param {event} e The raw browser event
52574          */
52575         "dragdrop" : true,
52576         /**
52577          * @event dragover
52578          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52579          * @param {Grid} this
52580          * @param {Roo.GridDD} dd The drag drop object
52581          * @param {String} targetId The target drag drop object
52582          * @param {event} e The raw browser event
52583          */
52584         "dragover" : true,
52585         /**
52586          * @event dragenter
52587          *  Fires when the dragged row(s) first cross another DD target while being dragged
52588          * @param {Grid} this
52589          * @param {Roo.GridDD} dd The drag drop object
52590          * @param {String} targetId The target drag drop object
52591          * @param {event} e The raw browser event
52592          */
52593         "dragenter" : true,
52594         /**
52595          * @event dragout
52596          * Fires when the dragged row(s) leave another DD target while being dragged
52597          * @param {Grid} this
52598          * @param {Roo.GridDD} dd The drag drop object
52599          * @param {String} targetId The target drag drop object
52600          * @param {event} e The raw browser event
52601          */
52602         "dragout" : true,
52603         /**
52604          * @event rowclass
52605          * Fires when a row is rendered, so you can change add a style to it.
52606          * @param {GridView} gridview   The grid view
52607          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52608          */
52609         'rowclass' : true,
52610
52611         /**
52612          * @event render
52613          * Fires when the grid is rendered
52614          * @param {Grid} grid
52615          */
52616         'render' : true
52617     });
52618
52619     Roo.grid.Grid.superclass.constructor.call(this);
52620 };
52621 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52622     
52623     /**
52624      * @cfg {String} ddGroup - drag drop group.
52625      */
52626
52627     /**
52628      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52629      */
52630     minColumnWidth : 25,
52631
52632     /**
52633      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52634      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52635      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52636      */
52637     autoSizeColumns : false,
52638
52639     /**
52640      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52641      */
52642     autoSizeHeaders : true,
52643
52644     /**
52645      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52646      */
52647     monitorWindowResize : true,
52648
52649     /**
52650      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52651      * rows measured to get a columns size. Default is 0 (all rows).
52652      */
52653     maxRowsToMeasure : 0,
52654
52655     /**
52656      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52657      */
52658     trackMouseOver : true,
52659
52660     /**
52661     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52662     */
52663     
52664     /**
52665     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52666     */
52667     enableDragDrop : false,
52668     
52669     /**
52670     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52671     */
52672     enableColumnMove : true,
52673     
52674     /**
52675     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52676     */
52677     enableColumnHide : true,
52678     
52679     /**
52680     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52681     */
52682     enableRowHeightSync : false,
52683     
52684     /**
52685     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52686     */
52687     stripeRows : true,
52688     
52689     /**
52690     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52691     */
52692     autoHeight : false,
52693
52694     /**
52695      * @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.
52696      */
52697     autoExpandColumn : false,
52698
52699     /**
52700     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52701     * Default is 50.
52702     */
52703     autoExpandMin : 50,
52704
52705     /**
52706     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52707     */
52708     autoExpandMax : 1000,
52709
52710     /**
52711     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52712     */
52713     view : null,
52714
52715     /**
52716     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52717     */
52718     loadMask : false,
52719     /**
52720     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52721     */
52722     dropTarget: false,
52723     
52724    
52725     
52726     // private
52727     rendered : false,
52728
52729     /**
52730     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52731     * of a fixed width. Default is false.
52732     */
52733     /**
52734     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52735     */
52736     /**
52737      * Called once after all setup has been completed and the grid is ready to be rendered.
52738      * @return {Roo.grid.Grid} this
52739      */
52740     render : function()
52741     {
52742         var c = this.container;
52743         // try to detect autoHeight/width mode
52744         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52745             this.autoHeight = true;
52746         }
52747         var view = this.getView();
52748         view.init(this);
52749
52750         c.on("click", this.onClick, this);
52751         c.on("dblclick", this.onDblClick, this);
52752         c.on("contextmenu", this.onContextMenu, this);
52753         c.on("keydown", this.onKeyDown, this);
52754         if (Roo.isTouch) {
52755             c.on("touchstart", this.onTouchStart, this);
52756         }
52757
52758         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52759
52760         this.getSelectionModel().init(this);
52761
52762         view.render();
52763
52764         if(this.loadMask){
52765             this.loadMask = new Roo.LoadMask(this.container,
52766                     Roo.apply({store:this.dataSource}, this.loadMask));
52767         }
52768         
52769         
52770         if (this.toolbar && this.toolbar.xtype) {
52771             this.toolbar.container = this.getView().getHeaderPanel(true);
52772             this.toolbar = new Roo.Toolbar(this.toolbar);
52773         }
52774         if (this.footer && this.footer.xtype) {
52775             this.footer.dataSource = this.getDataSource();
52776             this.footer.container = this.getView().getFooterPanel(true);
52777             this.footer = Roo.factory(this.footer, Roo);
52778         }
52779         if (this.dropTarget && this.dropTarget.xtype) {
52780             delete this.dropTarget.xtype;
52781             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52782         }
52783         
52784         
52785         this.rendered = true;
52786         this.fireEvent('render', this);
52787         return this;
52788     },
52789
52790         /**
52791          * Reconfigures the grid to use a different Store and Column Model.
52792          * The View will be bound to the new objects and refreshed.
52793          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52794          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52795          */
52796     reconfigure : function(dataSource, colModel){
52797         if(this.loadMask){
52798             this.loadMask.destroy();
52799             this.loadMask = new Roo.LoadMask(this.container,
52800                     Roo.apply({store:dataSource}, this.loadMask));
52801         }
52802         this.view.bind(dataSource, colModel);
52803         this.dataSource = dataSource;
52804         this.colModel = colModel;
52805         this.view.refresh(true);
52806     },
52807
52808     // private
52809     onKeyDown : function(e){
52810         this.fireEvent("keydown", e);
52811     },
52812
52813     /**
52814      * Destroy this grid.
52815      * @param {Boolean} removeEl True to remove the element
52816      */
52817     destroy : function(removeEl, keepListeners){
52818         if(this.loadMask){
52819             this.loadMask.destroy();
52820         }
52821         var c = this.container;
52822         c.removeAllListeners();
52823         this.view.destroy();
52824         this.colModel.purgeListeners();
52825         if(!keepListeners){
52826             this.purgeListeners();
52827         }
52828         c.update("");
52829         if(removeEl === true){
52830             c.remove();
52831         }
52832     },
52833
52834     // private
52835     processEvent : function(name, e){
52836         // does this fire select???
52837         //Roo.log('grid:processEvent '  + name);
52838         
52839         if (name != 'touchstart' ) {
52840             this.fireEvent(name, e);    
52841         }
52842         
52843         var t = e.getTarget();
52844         var v = this.view;
52845         var header = v.findHeaderIndex(t);
52846         if(header !== false){
52847             var ename = name == 'touchstart' ? 'click' : name;
52848              
52849             this.fireEvent("header" + ename, this, header, e);
52850         }else{
52851             var row = v.findRowIndex(t);
52852             var cell = v.findCellIndex(t);
52853             if (name == 'touchstart') {
52854                 // first touch is always a click.
52855                 // hopefull this happens after selection is updated.?
52856                 name = false;
52857                 
52858                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52859                     var cs = this.selModel.getSelectedCell();
52860                     if (row == cs[0] && cell == cs[1]){
52861                         name = 'dblclick';
52862                     }
52863                 }
52864                 if (typeof(this.selModel.getSelections) != 'undefined') {
52865                     var cs = this.selModel.getSelections();
52866                     var ds = this.dataSource;
52867                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52868                         name = 'dblclick';
52869                     }
52870                 }
52871                 if (!name) {
52872                     return;
52873                 }
52874             }
52875             
52876             
52877             if(row !== false){
52878                 this.fireEvent("row" + name, this, row, e);
52879                 if(cell !== false){
52880                     this.fireEvent("cell" + name, this, row, cell, e);
52881                 }
52882             }
52883         }
52884     },
52885
52886     // private
52887     onClick : function(e){
52888         this.processEvent("click", e);
52889     },
52890    // private
52891     onTouchStart : function(e){
52892         this.processEvent("touchstart", e);
52893     },
52894
52895     // private
52896     onContextMenu : function(e, t){
52897         this.processEvent("contextmenu", e);
52898     },
52899
52900     // private
52901     onDblClick : function(e){
52902         this.processEvent("dblclick", e);
52903     },
52904
52905     // private
52906     walkCells : function(row, col, step, fn, scope){
52907         var cm = this.colModel, clen = cm.getColumnCount();
52908         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52909         if(step < 0){
52910             if(col < 0){
52911                 row--;
52912                 first = false;
52913             }
52914             while(row >= 0){
52915                 if(!first){
52916                     col = clen-1;
52917                 }
52918                 first = false;
52919                 while(col >= 0){
52920                     if(fn.call(scope || this, row, col, cm) === true){
52921                         return [row, col];
52922                     }
52923                     col--;
52924                 }
52925                 row--;
52926             }
52927         } else {
52928             if(col >= clen){
52929                 row++;
52930                 first = false;
52931             }
52932             while(row < rlen){
52933                 if(!first){
52934                     col = 0;
52935                 }
52936                 first = false;
52937                 while(col < clen){
52938                     if(fn.call(scope || this, row, col, cm) === true){
52939                         return [row, col];
52940                     }
52941                     col++;
52942                 }
52943                 row++;
52944             }
52945         }
52946         return null;
52947     },
52948
52949     // private
52950     getSelections : function(){
52951         return this.selModel.getSelections();
52952     },
52953
52954     /**
52955      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52956      * but if manual update is required this method will initiate it.
52957      */
52958     autoSize : function(){
52959         if(this.rendered){
52960             this.view.layout();
52961             if(this.view.adjustForScroll){
52962                 this.view.adjustForScroll();
52963             }
52964         }
52965     },
52966
52967     /**
52968      * Returns the grid's underlying element.
52969      * @return {Element} The element
52970      */
52971     getGridEl : function(){
52972         return this.container;
52973     },
52974
52975     // private for compatibility, overridden by editor grid
52976     stopEditing : function(){},
52977
52978     /**
52979      * Returns the grid's SelectionModel.
52980      * @return {SelectionModel}
52981      */
52982     getSelectionModel : function(){
52983         if(!this.selModel){
52984             this.selModel = new Roo.grid.RowSelectionModel();
52985         }
52986         return this.selModel;
52987     },
52988
52989     /**
52990      * Returns the grid's DataSource.
52991      * @return {DataSource}
52992      */
52993     getDataSource : function(){
52994         return this.dataSource;
52995     },
52996
52997     /**
52998      * Returns the grid's ColumnModel.
52999      * @return {ColumnModel}
53000      */
53001     getColumnModel : function(){
53002         return this.colModel;
53003     },
53004
53005     /**
53006      * Returns the grid's GridView object.
53007      * @return {GridView}
53008      */
53009     getView : function(){
53010         if(!this.view){
53011             this.view = new Roo.grid.GridView(this.viewConfig);
53012         }
53013         return this.view;
53014     },
53015     /**
53016      * Called to get grid's drag proxy text, by default returns this.ddText.
53017      * @return {String}
53018      */
53019     getDragDropText : function(){
53020         var count = this.selModel.getCount();
53021         return String.format(this.ddText, count, count == 1 ? '' : 's');
53022     }
53023 });
53024 /**
53025  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
53026  * %0 is replaced with the number of selected rows.
53027  * @type String
53028  */
53029 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
53030  * Based on:
53031  * Ext JS Library 1.1.1
53032  * Copyright(c) 2006-2007, Ext JS, LLC.
53033  *
53034  * Originally Released Under LGPL - original licence link has changed is not relivant.
53035  *
53036  * Fork - LGPL
53037  * <script type="text/javascript">
53038  */
53039  
53040 Roo.grid.AbstractGridView = function(){
53041         this.grid = null;
53042         
53043         this.events = {
53044             "beforerowremoved" : true,
53045             "beforerowsinserted" : true,
53046             "beforerefresh" : true,
53047             "rowremoved" : true,
53048             "rowsinserted" : true,
53049             "rowupdated" : true,
53050             "refresh" : true
53051         };
53052     Roo.grid.AbstractGridView.superclass.constructor.call(this);
53053 };
53054
53055 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
53056     rowClass : "x-grid-row",
53057     cellClass : "x-grid-cell",
53058     tdClass : "x-grid-td",
53059     hdClass : "x-grid-hd",
53060     splitClass : "x-grid-hd-split",
53061     
53062     init: function(grid){
53063         this.grid = grid;
53064                 var cid = this.grid.getGridEl().id;
53065         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
53066         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
53067         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
53068         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
53069         },
53070         
53071     getColumnRenderers : function(){
53072         var renderers = [];
53073         var cm = this.grid.colModel;
53074         var colCount = cm.getColumnCount();
53075         for(var i = 0; i < colCount; i++){
53076             renderers[i] = cm.getRenderer(i);
53077         }
53078         return renderers;
53079     },
53080     
53081     getColumnIds : function(){
53082         var ids = [];
53083         var cm = this.grid.colModel;
53084         var colCount = cm.getColumnCount();
53085         for(var i = 0; i < colCount; i++){
53086             ids[i] = cm.getColumnId(i);
53087         }
53088         return ids;
53089     },
53090     
53091     getDataIndexes : function(){
53092         if(!this.indexMap){
53093             this.indexMap = this.buildIndexMap();
53094         }
53095         return this.indexMap.colToData;
53096     },
53097     
53098     getColumnIndexByDataIndex : function(dataIndex){
53099         if(!this.indexMap){
53100             this.indexMap = this.buildIndexMap();
53101         }
53102         return this.indexMap.dataToCol[dataIndex];
53103     },
53104     
53105     /**
53106      * Set a css style for a column dynamically. 
53107      * @param {Number} colIndex The index of the column
53108      * @param {String} name The css property name
53109      * @param {String} value The css value
53110      */
53111     setCSSStyle : function(colIndex, name, value){
53112         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
53113         Roo.util.CSS.updateRule(selector, name, value);
53114     },
53115     
53116     generateRules : function(cm){
53117         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
53118         Roo.util.CSS.removeStyleSheet(rulesId);
53119         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53120             var cid = cm.getColumnId(i);
53121             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
53122                          this.tdSelector, cid, " {\n}\n",
53123                          this.hdSelector, cid, " {\n}\n",
53124                          this.splitSelector, cid, " {\n}\n");
53125         }
53126         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53127     }
53128 });/*
53129  * Based on:
53130  * Ext JS Library 1.1.1
53131  * Copyright(c) 2006-2007, Ext JS, LLC.
53132  *
53133  * Originally Released Under LGPL - original licence link has changed is not relivant.
53134  *
53135  * Fork - LGPL
53136  * <script type="text/javascript">
53137  */
53138
53139 // private
53140 // This is a support class used internally by the Grid components
53141 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
53142     this.grid = grid;
53143     this.view = grid.getView();
53144     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53145     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
53146     if(hd2){
53147         this.setHandleElId(Roo.id(hd));
53148         this.setOuterHandleElId(Roo.id(hd2));
53149     }
53150     this.scroll = false;
53151 };
53152 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
53153     maxDragWidth: 120,
53154     getDragData : function(e){
53155         var t = Roo.lib.Event.getTarget(e);
53156         var h = this.view.findHeaderCell(t);
53157         if(h){
53158             return {ddel: h.firstChild, header:h};
53159         }
53160         return false;
53161     },
53162
53163     onInitDrag : function(e){
53164         this.view.headersDisabled = true;
53165         var clone = this.dragData.ddel.cloneNode(true);
53166         clone.id = Roo.id();
53167         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
53168         this.proxy.update(clone);
53169         return true;
53170     },
53171
53172     afterValidDrop : function(){
53173         var v = this.view;
53174         setTimeout(function(){
53175             v.headersDisabled = false;
53176         }, 50);
53177     },
53178
53179     afterInvalidDrop : function(){
53180         var v = this.view;
53181         setTimeout(function(){
53182             v.headersDisabled = false;
53183         }, 50);
53184     }
53185 });
53186 /*
53187  * Based on:
53188  * Ext JS Library 1.1.1
53189  * Copyright(c) 2006-2007, Ext JS, LLC.
53190  *
53191  * Originally Released Under LGPL - original licence link has changed is not relivant.
53192  *
53193  * Fork - LGPL
53194  * <script type="text/javascript">
53195  */
53196 // private
53197 // This is a support class used internally by the Grid components
53198 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
53199     this.grid = grid;
53200     this.view = grid.getView();
53201     // split the proxies so they don't interfere with mouse events
53202     this.proxyTop = Roo.DomHelper.append(document.body, {
53203         cls:"col-move-top", html:"&#160;"
53204     }, true);
53205     this.proxyBottom = Roo.DomHelper.append(document.body, {
53206         cls:"col-move-bottom", html:"&#160;"
53207     }, true);
53208     this.proxyTop.hide = this.proxyBottom.hide = function(){
53209         this.setLeftTop(-100,-100);
53210         this.setStyle("visibility", "hidden");
53211     };
53212     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
53213     // temporarily disabled
53214     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
53215     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
53216 };
53217 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
53218     proxyOffsets : [-4, -9],
53219     fly: Roo.Element.fly,
53220
53221     getTargetFromEvent : function(e){
53222         var t = Roo.lib.Event.getTarget(e);
53223         var cindex = this.view.findCellIndex(t);
53224         if(cindex !== false){
53225             return this.view.getHeaderCell(cindex);
53226         }
53227         return null;
53228     },
53229
53230     nextVisible : function(h){
53231         var v = this.view, cm = this.grid.colModel;
53232         h = h.nextSibling;
53233         while(h){
53234             if(!cm.isHidden(v.getCellIndex(h))){
53235                 return h;
53236             }
53237             h = h.nextSibling;
53238         }
53239         return null;
53240     },
53241
53242     prevVisible : function(h){
53243         var v = this.view, cm = this.grid.colModel;
53244         h = h.prevSibling;
53245         while(h){
53246             if(!cm.isHidden(v.getCellIndex(h))){
53247                 return h;
53248             }
53249             h = h.prevSibling;
53250         }
53251         return null;
53252     },
53253
53254     positionIndicator : function(h, n, e){
53255         var x = Roo.lib.Event.getPageX(e);
53256         var r = Roo.lib.Dom.getRegion(n.firstChild);
53257         var px, pt, py = r.top + this.proxyOffsets[1];
53258         if((r.right - x) <= (r.right-r.left)/2){
53259             px = r.right+this.view.borderWidth;
53260             pt = "after";
53261         }else{
53262             px = r.left;
53263             pt = "before";
53264         }
53265         var oldIndex = this.view.getCellIndex(h);
53266         var newIndex = this.view.getCellIndex(n);
53267
53268         if(this.grid.colModel.isFixed(newIndex)){
53269             return false;
53270         }
53271
53272         var locked = this.grid.colModel.isLocked(newIndex);
53273
53274         if(pt == "after"){
53275             newIndex++;
53276         }
53277         if(oldIndex < newIndex){
53278             newIndex--;
53279         }
53280         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
53281             return false;
53282         }
53283         px +=  this.proxyOffsets[0];
53284         this.proxyTop.setLeftTop(px, py);
53285         this.proxyTop.show();
53286         if(!this.bottomOffset){
53287             this.bottomOffset = this.view.mainHd.getHeight();
53288         }
53289         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
53290         this.proxyBottom.show();
53291         return pt;
53292     },
53293
53294     onNodeEnter : function(n, dd, e, data){
53295         if(data.header != n){
53296             this.positionIndicator(data.header, n, e);
53297         }
53298     },
53299
53300     onNodeOver : function(n, dd, e, data){
53301         var result = false;
53302         if(data.header != n){
53303             result = this.positionIndicator(data.header, n, e);
53304         }
53305         if(!result){
53306             this.proxyTop.hide();
53307             this.proxyBottom.hide();
53308         }
53309         return result ? this.dropAllowed : this.dropNotAllowed;
53310     },
53311
53312     onNodeOut : function(n, dd, e, data){
53313         this.proxyTop.hide();
53314         this.proxyBottom.hide();
53315     },
53316
53317     onNodeDrop : function(n, dd, e, data){
53318         var h = data.header;
53319         if(h != n){
53320             var cm = this.grid.colModel;
53321             var x = Roo.lib.Event.getPageX(e);
53322             var r = Roo.lib.Dom.getRegion(n.firstChild);
53323             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
53324             var oldIndex = this.view.getCellIndex(h);
53325             var newIndex = this.view.getCellIndex(n);
53326             var locked = cm.isLocked(newIndex);
53327             if(pt == "after"){
53328                 newIndex++;
53329             }
53330             if(oldIndex < newIndex){
53331                 newIndex--;
53332             }
53333             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
53334                 return false;
53335             }
53336             cm.setLocked(oldIndex, locked, true);
53337             cm.moveColumn(oldIndex, newIndex);
53338             this.grid.fireEvent("columnmove", oldIndex, newIndex);
53339             return true;
53340         }
53341         return false;
53342     }
53343 });
53344 /*
53345  * Based on:
53346  * Ext JS Library 1.1.1
53347  * Copyright(c) 2006-2007, Ext JS, LLC.
53348  *
53349  * Originally Released Under LGPL - original licence link has changed is not relivant.
53350  *
53351  * Fork - LGPL
53352  * <script type="text/javascript">
53353  */
53354   
53355 /**
53356  * @class Roo.grid.GridView
53357  * @extends Roo.util.Observable
53358  *
53359  * @constructor
53360  * @param {Object} config
53361  */
53362 Roo.grid.GridView = function(config){
53363     Roo.grid.GridView.superclass.constructor.call(this);
53364     this.el = null;
53365
53366     Roo.apply(this, config);
53367 };
53368
53369 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
53370
53371     unselectable :  'unselectable="on"',
53372     unselectableCls :  'x-unselectable',
53373     
53374     
53375     rowClass : "x-grid-row",
53376
53377     cellClass : "x-grid-col",
53378
53379     tdClass : "x-grid-td",
53380
53381     hdClass : "x-grid-hd",
53382
53383     splitClass : "x-grid-split",
53384
53385     sortClasses : ["sort-asc", "sort-desc"],
53386
53387     enableMoveAnim : false,
53388
53389     hlColor: "C3DAF9",
53390
53391     dh : Roo.DomHelper,
53392
53393     fly : Roo.Element.fly,
53394
53395     css : Roo.util.CSS,
53396
53397     borderWidth: 1,
53398
53399     splitOffset: 3,
53400
53401     scrollIncrement : 22,
53402
53403     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53404
53405     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53406
53407     bind : function(ds, cm){
53408         if(this.ds){
53409             this.ds.un("load", this.onLoad, this);
53410             this.ds.un("datachanged", this.onDataChange, this);
53411             this.ds.un("add", this.onAdd, this);
53412             this.ds.un("remove", this.onRemove, this);
53413             this.ds.un("update", this.onUpdate, this);
53414             this.ds.un("clear", this.onClear, this);
53415         }
53416         if(ds){
53417             ds.on("load", this.onLoad, this);
53418             ds.on("datachanged", this.onDataChange, this);
53419             ds.on("add", this.onAdd, this);
53420             ds.on("remove", this.onRemove, this);
53421             ds.on("update", this.onUpdate, this);
53422             ds.on("clear", this.onClear, this);
53423         }
53424         this.ds = ds;
53425
53426         if(this.cm){
53427             this.cm.un("widthchange", this.onColWidthChange, this);
53428             this.cm.un("headerchange", this.onHeaderChange, this);
53429             this.cm.un("hiddenchange", this.onHiddenChange, this);
53430             this.cm.un("columnmoved", this.onColumnMove, this);
53431             this.cm.un("columnlockchange", this.onColumnLock, this);
53432         }
53433         if(cm){
53434             this.generateRules(cm);
53435             cm.on("widthchange", this.onColWidthChange, this);
53436             cm.on("headerchange", this.onHeaderChange, this);
53437             cm.on("hiddenchange", this.onHiddenChange, this);
53438             cm.on("columnmoved", this.onColumnMove, this);
53439             cm.on("columnlockchange", this.onColumnLock, this);
53440         }
53441         this.cm = cm;
53442     },
53443
53444     init: function(grid){
53445         Roo.grid.GridView.superclass.init.call(this, grid);
53446
53447         this.bind(grid.dataSource, grid.colModel);
53448
53449         grid.on("headerclick", this.handleHeaderClick, this);
53450
53451         if(grid.trackMouseOver){
53452             grid.on("mouseover", this.onRowOver, this);
53453             grid.on("mouseout", this.onRowOut, this);
53454         }
53455         grid.cancelTextSelection = function(){};
53456         this.gridId = grid.id;
53457
53458         var tpls = this.templates || {};
53459
53460         if(!tpls.master){
53461             tpls.master = new Roo.Template(
53462                '<div class="x-grid" hidefocus="true">',
53463                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53464                   '<div class="x-grid-topbar"></div>',
53465                   '<div class="x-grid-scroller"><div></div></div>',
53466                   '<div class="x-grid-locked">',
53467                       '<div class="x-grid-header">{lockedHeader}</div>',
53468                       '<div class="x-grid-body">{lockedBody}</div>',
53469                   "</div>",
53470                   '<div class="x-grid-viewport">',
53471                       '<div class="x-grid-header">{header}</div>',
53472                       '<div class="x-grid-body">{body}</div>',
53473                   "</div>",
53474                   '<div class="x-grid-bottombar"></div>',
53475                  
53476                   '<div class="x-grid-resize-proxy">&#160;</div>',
53477                "</div>"
53478             );
53479             tpls.master.disableformats = true;
53480         }
53481
53482         if(!tpls.header){
53483             tpls.header = new Roo.Template(
53484                '<table border="0" cellspacing="0" cellpadding="0">',
53485                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53486                "</table>{splits}"
53487             );
53488             tpls.header.disableformats = true;
53489         }
53490         tpls.header.compile();
53491
53492         if(!tpls.hcell){
53493             tpls.hcell = new Roo.Template(
53494                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53495                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53496                 "</div></td>"
53497              );
53498              tpls.hcell.disableFormats = true;
53499         }
53500         tpls.hcell.compile();
53501
53502         if(!tpls.hsplit){
53503             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53504                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53505             tpls.hsplit.disableFormats = true;
53506         }
53507         tpls.hsplit.compile();
53508
53509         if(!tpls.body){
53510             tpls.body = new Roo.Template(
53511                '<table border="0" cellspacing="0" cellpadding="0">',
53512                "<tbody>{rows}</tbody>",
53513                "</table>"
53514             );
53515             tpls.body.disableFormats = true;
53516         }
53517         tpls.body.compile();
53518
53519         if(!tpls.row){
53520             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53521             tpls.row.disableFormats = true;
53522         }
53523         tpls.row.compile();
53524
53525         if(!tpls.cell){
53526             tpls.cell = new Roo.Template(
53527                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53528                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53529                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53530                 "</td>"
53531             );
53532             tpls.cell.disableFormats = true;
53533         }
53534         tpls.cell.compile();
53535
53536         this.templates = tpls;
53537     },
53538
53539     // remap these for backwards compat
53540     onColWidthChange : function(){
53541         this.updateColumns.apply(this, arguments);
53542     },
53543     onHeaderChange : function(){
53544         this.updateHeaders.apply(this, arguments);
53545     }, 
53546     onHiddenChange : function(){
53547         this.handleHiddenChange.apply(this, arguments);
53548     },
53549     onColumnMove : function(){
53550         this.handleColumnMove.apply(this, arguments);
53551     },
53552     onColumnLock : function(){
53553         this.handleLockChange.apply(this, arguments);
53554     },
53555
53556     onDataChange : function(){
53557         this.refresh();
53558         this.updateHeaderSortState();
53559     },
53560
53561     onClear : function(){
53562         this.refresh();
53563     },
53564
53565     onUpdate : function(ds, record){
53566         this.refreshRow(record);
53567     },
53568
53569     refreshRow : function(record){
53570         var ds = this.ds, index;
53571         if(typeof record == 'number'){
53572             index = record;
53573             record = ds.getAt(index);
53574         }else{
53575             index = ds.indexOf(record);
53576         }
53577         this.insertRows(ds, index, index, true);
53578         this.onRemove(ds, record, index+1, true);
53579         this.syncRowHeights(index, index);
53580         this.layout();
53581         this.fireEvent("rowupdated", this, index, record);
53582     },
53583
53584     onAdd : function(ds, records, index){
53585         this.insertRows(ds, index, index + (records.length-1));
53586     },
53587
53588     onRemove : function(ds, record, index, isUpdate){
53589         if(isUpdate !== true){
53590             this.fireEvent("beforerowremoved", this, index, record);
53591         }
53592         var bt = this.getBodyTable(), lt = this.getLockedTable();
53593         if(bt.rows[index]){
53594             bt.firstChild.removeChild(bt.rows[index]);
53595         }
53596         if(lt.rows[index]){
53597             lt.firstChild.removeChild(lt.rows[index]);
53598         }
53599         if(isUpdate !== true){
53600             this.stripeRows(index);
53601             this.syncRowHeights(index, index);
53602             this.layout();
53603             this.fireEvent("rowremoved", this, index, record);
53604         }
53605     },
53606
53607     onLoad : function(){
53608         this.scrollToTop();
53609     },
53610
53611     /**
53612      * Scrolls the grid to the top
53613      */
53614     scrollToTop : function(){
53615         if(this.scroller){
53616             this.scroller.dom.scrollTop = 0;
53617             this.syncScroll();
53618         }
53619     },
53620
53621     /**
53622      * Gets a panel in the header of the grid that can be used for toolbars etc.
53623      * After modifying the contents of this panel a call to grid.autoSize() may be
53624      * required to register any changes in size.
53625      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53626      * @return Roo.Element
53627      */
53628     getHeaderPanel : function(doShow){
53629         if(doShow){
53630             this.headerPanel.show();
53631         }
53632         return this.headerPanel;
53633     },
53634
53635     /**
53636      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53637      * After modifying the contents of this panel a call to grid.autoSize() may be
53638      * required to register any changes in size.
53639      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53640      * @return Roo.Element
53641      */
53642     getFooterPanel : function(doShow){
53643         if(doShow){
53644             this.footerPanel.show();
53645         }
53646         return this.footerPanel;
53647     },
53648
53649     initElements : function(){
53650         var E = Roo.Element;
53651         var el = this.grid.getGridEl().dom.firstChild;
53652         var cs = el.childNodes;
53653
53654         this.el = new E(el);
53655         
53656          this.focusEl = new E(el.firstChild);
53657         this.focusEl.swallowEvent("click", true);
53658         
53659         this.headerPanel = new E(cs[1]);
53660         this.headerPanel.enableDisplayMode("block");
53661
53662         this.scroller = new E(cs[2]);
53663         this.scrollSizer = new E(this.scroller.dom.firstChild);
53664
53665         this.lockedWrap = new E(cs[3]);
53666         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53667         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53668
53669         this.mainWrap = new E(cs[4]);
53670         this.mainHd = new E(this.mainWrap.dom.firstChild);
53671         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53672
53673         this.footerPanel = new E(cs[5]);
53674         this.footerPanel.enableDisplayMode("block");
53675
53676         this.resizeProxy = new E(cs[6]);
53677
53678         this.headerSelector = String.format(
53679            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53680            this.lockedHd.id, this.mainHd.id
53681         );
53682
53683         this.splitterSelector = String.format(
53684            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53685            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53686         );
53687     },
53688     idToCssName : function(s)
53689     {
53690         return s.replace(/[^a-z0-9]+/ig, '-');
53691     },
53692
53693     getHeaderCell : function(index){
53694         return Roo.DomQuery.select(this.headerSelector)[index];
53695     },
53696
53697     getHeaderCellMeasure : function(index){
53698         return this.getHeaderCell(index).firstChild;
53699     },
53700
53701     getHeaderCellText : function(index){
53702         return this.getHeaderCell(index).firstChild.firstChild;
53703     },
53704
53705     getLockedTable : function(){
53706         return this.lockedBody.dom.firstChild;
53707     },
53708
53709     getBodyTable : function(){
53710         return this.mainBody.dom.firstChild;
53711     },
53712
53713     getLockedRow : function(index){
53714         return this.getLockedTable().rows[index];
53715     },
53716
53717     getRow : function(index){
53718         return this.getBodyTable().rows[index];
53719     },
53720
53721     getRowComposite : function(index){
53722         if(!this.rowEl){
53723             this.rowEl = new Roo.CompositeElementLite();
53724         }
53725         var els = [], lrow, mrow;
53726         if(lrow = this.getLockedRow(index)){
53727             els.push(lrow);
53728         }
53729         if(mrow = this.getRow(index)){
53730             els.push(mrow);
53731         }
53732         this.rowEl.elements = els;
53733         return this.rowEl;
53734     },
53735     /**
53736      * Gets the 'td' of the cell
53737      * 
53738      * @param {Integer} rowIndex row to select
53739      * @param {Integer} colIndex column to select
53740      * 
53741      * @return {Object} 
53742      */
53743     getCell : function(rowIndex, colIndex){
53744         var locked = this.cm.getLockedCount();
53745         var source;
53746         if(colIndex < locked){
53747             source = this.lockedBody.dom.firstChild;
53748         }else{
53749             source = this.mainBody.dom.firstChild;
53750             colIndex -= locked;
53751         }
53752         return source.rows[rowIndex].childNodes[colIndex];
53753     },
53754
53755     getCellText : function(rowIndex, colIndex){
53756         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53757     },
53758
53759     getCellBox : function(cell){
53760         var b = this.fly(cell).getBox();
53761         if(Roo.isOpera){ // opera fails to report the Y
53762             b.y = cell.offsetTop + this.mainBody.getY();
53763         }
53764         return b;
53765     },
53766
53767     getCellIndex : function(cell){
53768         var id = String(cell.className).match(this.cellRE);
53769         if(id){
53770             return parseInt(id[1], 10);
53771         }
53772         return 0;
53773     },
53774
53775     findHeaderIndex : function(n){
53776         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53777         return r ? this.getCellIndex(r) : false;
53778     },
53779
53780     findHeaderCell : function(n){
53781         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53782         return r ? r : false;
53783     },
53784
53785     findRowIndex : function(n){
53786         if(!n){
53787             return false;
53788         }
53789         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53790         return r ? r.rowIndex : false;
53791     },
53792
53793     findCellIndex : function(node){
53794         var stop = this.el.dom;
53795         while(node && node != stop){
53796             if(this.findRE.test(node.className)){
53797                 return this.getCellIndex(node);
53798             }
53799             node = node.parentNode;
53800         }
53801         return false;
53802     },
53803
53804     getColumnId : function(index){
53805         return this.cm.getColumnId(index);
53806     },
53807
53808     getSplitters : function()
53809     {
53810         if(this.splitterSelector){
53811            return Roo.DomQuery.select(this.splitterSelector);
53812         }else{
53813             return null;
53814       }
53815     },
53816
53817     getSplitter : function(index){
53818         return this.getSplitters()[index];
53819     },
53820
53821     onRowOver : function(e, t){
53822         var row;
53823         if((row = this.findRowIndex(t)) !== false){
53824             this.getRowComposite(row).addClass("x-grid-row-over");
53825         }
53826     },
53827
53828     onRowOut : function(e, t){
53829         var row;
53830         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53831             this.getRowComposite(row).removeClass("x-grid-row-over");
53832         }
53833     },
53834
53835     renderHeaders : function(){
53836         var cm = this.cm;
53837         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53838         var cb = [], lb = [], sb = [], lsb = [], p = {};
53839         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53840             p.cellId = "x-grid-hd-0-" + i;
53841             p.splitId = "x-grid-csplit-0-" + i;
53842             p.id = cm.getColumnId(i);
53843             p.title = cm.getColumnTooltip(i) || "";
53844             p.value = cm.getColumnHeader(i) || "";
53845             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53846             if(!cm.isLocked(i)){
53847                 cb[cb.length] = ct.apply(p);
53848                 sb[sb.length] = st.apply(p);
53849             }else{
53850                 lb[lb.length] = ct.apply(p);
53851                 lsb[lsb.length] = st.apply(p);
53852             }
53853         }
53854         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53855                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53856     },
53857
53858     updateHeaders : function(){
53859         var html = this.renderHeaders();
53860         this.lockedHd.update(html[0]);
53861         this.mainHd.update(html[1]);
53862     },
53863
53864     /**
53865      * Focuses the specified row.
53866      * @param {Number} row The row index
53867      */
53868     focusRow : function(row)
53869     {
53870         //Roo.log('GridView.focusRow');
53871         var x = this.scroller.dom.scrollLeft;
53872         this.focusCell(row, 0, false);
53873         this.scroller.dom.scrollLeft = x;
53874     },
53875
53876     /**
53877      * Focuses the specified cell.
53878      * @param {Number} row The row index
53879      * @param {Number} col The column index
53880      * @param {Boolean} hscroll false to disable horizontal scrolling
53881      */
53882     focusCell : function(row, col, hscroll)
53883     {
53884         //Roo.log('GridView.focusCell');
53885         var el = this.ensureVisible(row, col, hscroll);
53886         this.focusEl.alignTo(el, "tl-tl");
53887         if(Roo.isGecko){
53888             this.focusEl.focus();
53889         }else{
53890             this.focusEl.focus.defer(1, this.focusEl);
53891         }
53892     },
53893
53894     /**
53895      * Scrolls the specified cell into view
53896      * @param {Number} row The row index
53897      * @param {Number} col The column index
53898      * @param {Boolean} hscroll false to disable horizontal scrolling
53899      */
53900     ensureVisible : function(row, col, hscroll)
53901     {
53902         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53903         //return null; //disable for testing.
53904         if(typeof row != "number"){
53905             row = row.rowIndex;
53906         }
53907         if(row < 0 && row >= this.ds.getCount()){
53908             return  null;
53909         }
53910         col = (col !== undefined ? col : 0);
53911         var cm = this.grid.colModel;
53912         while(cm.isHidden(col)){
53913             col++;
53914         }
53915
53916         var el = this.getCell(row, col);
53917         if(!el){
53918             return null;
53919         }
53920         var c = this.scroller.dom;
53921
53922         var ctop = parseInt(el.offsetTop, 10);
53923         var cleft = parseInt(el.offsetLeft, 10);
53924         var cbot = ctop + el.offsetHeight;
53925         var cright = cleft + el.offsetWidth;
53926         
53927         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53928         var stop = parseInt(c.scrollTop, 10);
53929         var sleft = parseInt(c.scrollLeft, 10);
53930         var sbot = stop + ch;
53931         var sright = sleft + c.clientWidth;
53932         /*
53933         Roo.log('GridView.ensureVisible:' +
53934                 ' ctop:' + ctop +
53935                 ' c.clientHeight:' + c.clientHeight +
53936                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53937                 ' stop:' + stop +
53938                 ' cbot:' + cbot +
53939                 ' sbot:' + sbot +
53940                 ' ch:' + ch  
53941                 );
53942         */
53943         if(ctop < stop){
53944              c.scrollTop = ctop;
53945             //Roo.log("set scrolltop to ctop DISABLE?");
53946         }else if(cbot > sbot){
53947             //Roo.log("set scrolltop to cbot-ch");
53948             c.scrollTop = cbot-ch;
53949         }
53950         
53951         if(hscroll !== false){
53952             if(cleft < sleft){
53953                 c.scrollLeft = cleft;
53954             }else if(cright > sright){
53955                 c.scrollLeft = cright-c.clientWidth;
53956             }
53957         }
53958          
53959         return el;
53960     },
53961
53962     updateColumns : function(){
53963         this.grid.stopEditing();
53964         var cm = this.grid.colModel, colIds = this.getColumnIds();
53965         //var totalWidth = cm.getTotalWidth();
53966         var pos = 0;
53967         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53968             //if(cm.isHidden(i)) continue;
53969             var w = cm.getColumnWidth(i);
53970             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53971             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53972         }
53973         this.updateSplitters();
53974     },
53975
53976     generateRules : function(cm){
53977         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53978         Roo.util.CSS.removeStyleSheet(rulesId);
53979         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53980             var cid = cm.getColumnId(i);
53981             var align = '';
53982             if(cm.config[i].align){
53983                 align = 'text-align:'+cm.config[i].align+';';
53984             }
53985             var hidden = '';
53986             if(cm.isHidden(i)){
53987                 hidden = 'display:none;';
53988             }
53989             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53990             ruleBuf.push(
53991                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53992                     this.hdSelector, cid, " {\n", align, width, "}\n",
53993                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53994                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53995         }
53996         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53997     },
53998
53999     updateSplitters : function(){
54000         var cm = this.cm, s = this.getSplitters();
54001         if(s){ // splitters not created yet
54002             var pos = 0, locked = true;
54003             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
54004                 if(cm.isHidden(i)) continue;
54005                 var w = cm.getColumnWidth(i); // make sure it's a number
54006                 if(!cm.isLocked(i) && locked){
54007                     pos = 0;
54008                     locked = false;
54009                 }
54010                 pos += w;
54011                 s[i].style.left = (pos-this.splitOffset) + "px";
54012             }
54013         }
54014     },
54015
54016     handleHiddenChange : function(colModel, colIndex, hidden){
54017         if(hidden){
54018             this.hideColumn(colIndex);
54019         }else{
54020             this.unhideColumn(colIndex);
54021         }
54022     },
54023
54024     hideColumn : function(colIndex){
54025         var cid = this.getColumnId(colIndex);
54026         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
54027         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
54028         if(Roo.isSafari){
54029             this.updateHeaders();
54030         }
54031         this.updateSplitters();
54032         this.layout();
54033     },
54034
54035     unhideColumn : function(colIndex){
54036         var cid = this.getColumnId(colIndex);
54037         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
54038         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
54039
54040         if(Roo.isSafari){
54041             this.updateHeaders();
54042         }
54043         this.updateSplitters();
54044         this.layout();
54045     },
54046
54047     insertRows : function(dm, firstRow, lastRow, isUpdate){
54048         if(firstRow == 0 && lastRow == dm.getCount()-1){
54049             this.refresh();
54050         }else{
54051             if(!isUpdate){
54052                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
54053             }
54054             var s = this.getScrollState();
54055             var markup = this.renderRows(firstRow, lastRow);
54056             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
54057             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
54058             this.restoreScroll(s);
54059             if(!isUpdate){
54060                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
54061                 this.syncRowHeights(firstRow, lastRow);
54062                 this.stripeRows(firstRow);
54063                 this.layout();
54064             }
54065         }
54066     },
54067
54068     bufferRows : function(markup, target, index){
54069         var before = null, trows = target.rows, tbody = target.tBodies[0];
54070         if(index < trows.length){
54071             before = trows[index];
54072         }
54073         var b = document.createElement("div");
54074         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
54075         var rows = b.firstChild.rows;
54076         for(var i = 0, len = rows.length; i < len; i++){
54077             if(before){
54078                 tbody.insertBefore(rows[0], before);
54079             }else{
54080                 tbody.appendChild(rows[0]);
54081             }
54082         }
54083         b.innerHTML = "";
54084         b = null;
54085     },
54086
54087     deleteRows : function(dm, firstRow, lastRow){
54088         if(dm.getRowCount()<1){
54089             this.fireEvent("beforerefresh", this);
54090             this.mainBody.update("");
54091             this.lockedBody.update("");
54092             this.fireEvent("refresh", this);
54093         }else{
54094             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
54095             var bt = this.getBodyTable();
54096             var tbody = bt.firstChild;
54097             var rows = bt.rows;
54098             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
54099                 tbody.removeChild(rows[firstRow]);
54100             }
54101             this.stripeRows(firstRow);
54102             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
54103         }
54104     },
54105
54106     updateRows : function(dataSource, firstRow, lastRow){
54107         var s = this.getScrollState();
54108         this.refresh();
54109         this.restoreScroll(s);
54110     },
54111
54112     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
54113         if(!noRefresh){
54114            this.refresh();
54115         }
54116         this.updateHeaderSortState();
54117     },
54118
54119     getScrollState : function(){
54120         
54121         var sb = this.scroller.dom;
54122         return {left: sb.scrollLeft, top: sb.scrollTop};
54123     },
54124
54125     stripeRows : function(startRow){
54126         if(!this.grid.stripeRows || this.ds.getCount() < 1){
54127             return;
54128         }
54129         startRow = startRow || 0;
54130         var rows = this.getBodyTable().rows;
54131         var lrows = this.getLockedTable().rows;
54132         var cls = ' x-grid-row-alt ';
54133         for(var i = startRow, len = rows.length; i < len; i++){
54134             var row = rows[i], lrow = lrows[i];
54135             var isAlt = ((i+1) % 2 == 0);
54136             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
54137             if(isAlt == hasAlt){
54138                 continue;
54139             }
54140             if(isAlt){
54141                 row.className += " x-grid-row-alt";
54142             }else{
54143                 row.className = row.className.replace("x-grid-row-alt", "");
54144             }
54145             if(lrow){
54146                 lrow.className = row.className;
54147             }
54148         }
54149     },
54150
54151     restoreScroll : function(state){
54152         //Roo.log('GridView.restoreScroll');
54153         var sb = this.scroller.dom;
54154         sb.scrollLeft = state.left;
54155         sb.scrollTop = state.top;
54156         this.syncScroll();
54157     },
54158
54159     syncScroll : function(){
54160         //Roo.log('GridView.syncScroll');
54161         var sb = this.scroller.dom;
54162         var sh = this.mainHd.dom;
54163         var bs = this.mainBody.dom;
54164         var lv = this.lockedBody.dom;
54165         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
54166         lv.scrollTop = bs.scrollTop = sb.scrollTop;
54167     },
54168
54169     handleScroll : function(e){
54170         this.syncScroll();
54171         var sb = this.scroller.dom;
54172         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
54173         e.stopEvent();
54174     },
54175
54176     handleWheel : function(e){
54177         var d = e.getWheelDelta();
54178         this.scroller.dom.scrollTop -= d*22;
54179         // set this here to prevent jumpy scrolling on large tables
54180         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
54181         e.stopEvent();
54182     },
54183
54184     renderRows : function(startRow, endRow){
54185         // pull in all the crap needed to render rows
54186         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
54187         var colCount = cm.getColumnCount();
54188
54189         if(ds.getCount() < 1){
54190             return ["", ""];
54191         }
54192
54193         // build a map for all the columns
54194         var cs = [];
54195         for(var i = 0; i < colCount; i++){
54196             var name = cm.getDataIndex(i);
54197             cs[i] = {
54198                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
54199                 renderer : cm.getRenderer(i),
54200                 id : cm.getColumnId(i),
54201                 locked : cm.isLocked(i)
54202             };
54203         }
54204
54205         startRow = startRow || 0;
54206         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
54207
54208         // records to render
54209         var rs = ds.getRange(startRow, endRow);
54210
54211         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
54212     },
54213
54214     // As much as I hate to duplicate code, this was branched because FireFox really hates
54215     // [].join("") on strings. The performance difference was substantial enough to
54216     // branch this function
54217     doRender : Roo.isGecko ?
54218             function(cs, rs, ds, startRow, colCount, stripe){
54219                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54220                 // buffers
54221                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54222                 
54223                 var hasListener = this.grid.hasListener('rowclass');
54224                 var rowcfg = {};
54225                 for(var j = 0, len = rs.length; j < len; j++){
54226                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
54227                     for(var i = 0; i < colCount; i++){
54228                         c = cs[i];
54229                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54230                         p.id = c.id;
54231                         p.css = p.attr = "";
54232                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54233                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54234                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54235                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54236                         }
54237                         var markup = ct.apply(p);
54238                         if(!c.locked){
54239                             cb+= markup;
54240                         }else{
54241                             lcb+= markup;
54242                         }
54243                     }
54244                     var alt = [];
54245                     if(stripe && ((rowIndex+1) % 2 == 0)){
54246                         alt.push("x-grid-row-alt")
54247                     }
54248                     if(r.dirty){
54249                         alt.push(  " x-grid-dirty-row");
54250                     }
54251                     rp.cells = lcb;
54252                     if(this.getRowClass){
54253                         alt.push(this.getRowClass(r, rowIndex));
54254                     }
54255                     if (hasListener) {
54256                         rowcfg = {
54257                              
54258                             record: r,
54259                             rowIndex : rowIndex,
54260                             rowClass : ''
54261                         }
54262                         this.grid.fireEvent('rowclass', this, rowcfg);
54263                         alt.push(rowcfg.rowClass);
54264                     }
54265                     rp.alt = alt.join(" ");
54266                     lbuf+= rt.apply(rp);
54267                     rp.cells = cb;
54268                     buf+=  rt.apply(rp);
54269                 }
54270                 return [lbuf, buf];
54271             } :
54272             function(cs, rs, ds, startRow, colCount, stripe){
54273                 var ts = this.templates, ct = ts.cell, rt = ts.row;
54274                 // buffers
54275                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
54276                 var hasListener = this.grid.hasListener('rowclass');
54277  
54278                 var rowcfg = {};
54279                 for(var j = 0, len = rs.length; j < len; j++){
54280                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
54281                     for(var i = 0; i < colCount; i++){
54282                         c = cs[i];
54283                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
54284                         p.id = c.id;
54285                         p.css = p.attr = "";
54286                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
54287                         if(p.value == undefined || p.value === "") p.value = "&#160;";
54288                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
54289                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
54290                         }
54291                         
54292                         var markup = ct.apply(p);
54293                         if(!c.locked){
54294                             cb[cb.length] = markup;
54295                         }else{
54296                             lcb[lcb.length] = markup;
54297                         }
54298                     }
54299                     var alt = [];
54300                     if(stripe && ((rowIndex+1) % 2 == 0)){
54301                         alt.push( "x-grid-row-alt");
54302                     }
54303                     if(r.dirty){
54304                         alt.push(" x-grid-dirty-row");
54305                     }
54306                     rp.cells = lcb;
54307                     if(this.getRowClass){
54308                         alt.push( this.getRowClass(r, rowIndex));
54309                     }
54310                     if (hasListener) {
54311                         rowcfg = {
54312                              
54313                             record: r,
54314                             rowIndex : rowIndex,
54315                             rowClass : ''
54316                         }
54317                         this.grid.fireEvent('rowclass', this, rowcfg);
54318                         alt.push(rowcfg.rowClass);
54319                     }
54320                     rp.alt = alt.join(" ");
54321                     rp.cells = lcb.join("");
54322                     lbuf[lbuf.length] = rt.apply(rp);
54323                     rp.cells = cb.join("");
54324                     buf[buf.length] =  rt.apply(rp);
54325                 }
54326                 return [lbuf.join(""), buf.join("")];
54327             },
54328
54329     renderBody : function(){
54330         var markup = this.renderRows();
54331         var bt = this.templates.body;
54332         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
54333     },
54334
54335     /**
54336      * Refreshes the grid
54337      * @param {Boolean} headersToo
54338      */
54339     refresh : function(headersToo){
54340         this.fireEvent("beforerefresh", this);
54341         this.grid.stopEditing();
54342         var result = this.renderBody();
54343         this.lockedBody.update(result[0]);
54344         this.mainBody.update(result[1]);
54345         if(headersToo === true){
54346             this.updateHeaders();
54347             this.updateColumns();
54348             this.updateSplitters();
54349             this.updateHeaderSortState();
54350         }
54351         this.syncRowHeights();
54352         this.layout();
54353         this.fireEvent("refresh", this);
54354     },
54355
54356     handleColumnMove : function(cm, oldIndex, newIndex){
54357         this.indexMap = null;
54358         var s = this.getScrollState();
54359         this.refresh(true);
54360         this.restoreScroll(s);
54361         this.afterMove(newIndex);
54362     },
54363
54364     afterMove : function(colIndex){
54365         if(this.enableMoveAnim && Roo.enableFx){
54366             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
54367         }
54368         // if multisort - fix sortOrder, and reload..
54369         if (this.grid.dataSource.multiSort) {
54370             // the we can call sort again..
54371             var dm = this.grid.dataSource;
54372             var cm = this.grid.colModel;
54373             var so = [];
54374             for(var i = 0; i < cm.config.length; i++ ) {
54375                 
54376                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
54377                     continue; // dont' bother, it's not in sort list or being set.
54378                 }
54379                 
54380                 so.push(cm.config[i].dataIndex);
54381             };
54382             dm.sortOrder = so;
54383             dm.load(dm.lastOptions);
54384             
54385             
54386         }
54387         
54388     },
54389
54390     updateCell : function(dm, rowIndex, dataIndex){
54391         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
54392         if(typeof colIndex == "undefined"){ // not present in grid
54393             return;
54394         }
54395         var cm = this.grid.colModel;
54396         var cell = this.getCell(rowIndex, colIndex);
54397         var cellText = this.getCellText(rowIndex, colIndex);
54398
54399         var p = {
54400             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54401             id : cm.getColumnId(colIndex),
54402             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54403         };
54404         var renderer = cm.getRenderer(colIndex);
54405         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54406         if(typeof val == "undefined" || val === "") val = "&#160;";
54407         cellText.innerHTML = val;
54408         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54409         this.syncRowHeights(rowIndex, rowIndex);
54410     },
54411
54412     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54413         var maxWidth = 0;
54414         if(this.grid.autoSizeHeaders){
54415             var h = this.getHeaderCellMeasure(colIndex);
54416             maxWidth = Math.max(maxWidth, h.scrollWidth);
54417         }
54418         var tb, index;
54419         if(this.cm.isLocked(colIndex)){
54420             tb = this.getLockedTable();
54421             index = colIndex;
54422         }else{
54423             tb = this.getBodyTable();
54424             index = colIndex - this.cm.getLockedCount();
54425         }
54426         if(tb && tb.rows){
54427             var rows = tb.rows;
54428             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54429             for(var i = 0; i < stopIndex; i++){
54430                 var cell = rows[i].childNodes[index].firstChild;
54431                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54432             }
54433         }
54434         return maxWidth + /*margin for error in IE*/ 5;
54435     },
54436     /**
54437      * Autofit a column to its content.
54438      * @param {Number} colIndex
54439      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54440      */
54441      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54442          if(this.cm.isHidden(colIndex)){
54443              return; // can't calc a hidden column
54444          }
54445         if(forceMinSize){
54446             var cid = this.cm.getColumnId(colIndex);
54447             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54448            if(this.grid.autoSizeHeaders){
54449                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54450            }
54451         }
54452         var newWidth = this.calcColumnWidth(colIndex);
54453         this.cm.setColumnWidth(colIndex,
54454             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54455         if(!suppressEvent){
54456             this.grid.fireEvent("columnresize", colIndex, newWidth);
54457         }
54458     },
54459
54460     /**
54461      * Autofits all columns to their content and then expands to fit any extra space in the grid
54462      */
54463      autoSizeColumns : function(){
54464         var cm = this.grid.colModel;
54465         var colCount = cm.getColumnCount();
54466         for(var i = 0; i < colCount; i++){
54467             this.autoSizeColumn(i, true, true);
54468         }
54469         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54470             this.fitColumns();
54471         }else{
54472             this.updateColumns();
54473             this.layout();
54474         }
54475     },
54476
54477     /**
54478      * Autofits all columns to the grid's width proportionate with their current size
54479      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54480      */
54481     fitColumns : function(reserveScrollSpace){
54482         var cm = this.grid.colModel;
54483         var colCount = cm.getColumnCount();
54484         var cols = [];
54485         var width = 0;
54486         var i, w;
54487         for (i = 0; i < colCount; i++){
54488             if(!cm.isHidden(i) && !cm.isFixed(i)){
54489                 w = cm.getColumnWidth(i);
54490                 cols.push(i);
54491                 cols.push(w);
54492                 width += w;
54493             }
54494         }
54495         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54496         if(reserveScrollSpace){
54497             avail -= 17;
54498         }
54499         var frac = (avail - cm.getTotalWidth())/width;
54500         while (cols.length){
54501             w = cols.pop();
54502             i = cols.pop();
54503             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54504         }
54505         this.updateColumns();
54506         this.layout();
54507     },
54508
54509     onRowSelect : function(rowIndex){
54510         var row = this.getRowComposite(rowIndex);
54511         row.addClass("x-grid-row-selected");
54512     },
54513
54514     onRowDeselect : function(rowIndex){
54515         var row = this.getRowComposite(rowIndex);
54516         row.removeClass("x-grid-row-selected");
54517     },
54518
54519     onCellSelect : function(row, col){
54520         var cell = this.getCell(row, col);
54521         if(cell){
54522             Roo.fly(cell).addClass("x-grid-cell-selected");
54523         }
54524     },
54525
54526     onCellDeselect : function(row, col){
54527         var cell = this.getCell(row, col);
54528         if(cell){
54529             Roo.fly(cell).removeClass("x-grid-cell-selected");
54530         }
54531     },
54532
54533     updateHeaderSortState : function(){
54534         
54535         // sort state can be single { field: xxx, direction : yyy}
54536         // or   { xxx=>ASC , yyy : DESC ..... }
54537         
54538         var mstate = {};
54539         if (!this.ds.multiSort) { 
54540             var state = this.ds.getSortState();
54541             if(!state){
54542                 return;
54543             }
54544             mstate[state.field] = state.direction;
54545             // FIXME... - this is not used here.. but might be elsewhere..
54546             this.sortState = state;
54547             
54548         } else {
54549             mstate = this.ds.sortToggle;
54550         }
54551         //remove existing sort classes..
54552         
54553         var sc = this.sortClasses;
54554         var hds = this.el.select(this.headerSelector).removeClass(sc);
54555         
54556         for(var f in mstate) {
54557         
54558             var sortColumn = this.cm.findColumnIndex(f);
54559             
54560             if(sortColumn != -1){
54561                 var sortDir = mstate[f];        
54562                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54563             }
54564         }
54565         
54566          
54567         
54568     },
54569
54570
54571     handleHeaderClick : function(g, index,e){
54572         
54573         Roo.log("header click");
54574         
54575         if (Roo.isTouch) {
54576             // touch events on header are handled by context
54577             this.handleHdCtx(g,index,e);
54578             return;
54579         }
54580         
54581         
54582         if(this.headersDisabled){
54583             return;
54584         }
54585         var dm = g.dataSource, cm = g.colModel;
54586         if(!cm.isSortable(index)){
54587             return;
54588         }
54589         g.stopEditing();
54590         
54591         if (dm.multiSort) {
54592             // update the sortOrder
54593             var so = [];
54594             for(var i = 0; i < cm.config.length; i++ ) {
54595                 
54596                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54597                     continue; // dont' bother, it's not in sort list or being set.
54598                 }
54599                 
54600                 so.push(cm.config[i].dataIndex);
54601             };
54602             dm.sortOrder = so;
54603         }
54604         
54605         
54606         dm.sort(cm.getDataIndex(index));
54607     },
54608
54609
54610     destroy : function(){
54611         if(this.colMenu){
54612             this.colMenu.removeAll();
54613             Roo.menu.MenuMgr.unregister(this.colMenu);
54614             this.colMenu.getEl().remove();
54615             delete this.colMenu;
54616         }
54617         if(this.hmenu){
54618             this.hmenu.removeAll();
54619             Roo.menu.MenuMgr.unregister(this.hmenu);
54620             this.hmenu.getEl().remove();
54621             delete this.hmenu;
54622         }
54623         if(this.grid.enableColumnMove){
54624             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54625             if(dds){
54626                 for(var dd in dds){
54627                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54628                         var elid = dds[dd].dragElId;
54629                         dds[dd].unreg();
54630                         Roo.get(elid).remove();
54631                     } else if(dds[dd].config.isTarget){
54632                         dds[dd].proxyTop.remove();
54633                         dds[dd].proxyBottom.remove();
54634                         dds[dd].unreg();
54635                     }
54636                     if(Roo.dd.DDM.locationCache[dd]){
54637                         delete Roo.dd.DDM.locationCache[dd];
54638                     }
54639                 }
54640                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54641             }
54642         }
54643         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54644         this.bind(null, null);
54645         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54646     },
54647
54648     handleLockChange : function(){
54649         this.refresh(true);
54650     },
54651
54652     onDenyColumnLock : function(){
54653
54654     },
54655
54656     onDenyColumnHide : function(){
54657
54658     },
54659
54660     handleHdMenuClick : function(item){
54661         var index = this.hdCtxIndex;
54662         var cm = this.cm, ds = this.ds;
54663         switch(item.id){
54664             case "asc":
54665                 ds.sort(cm.getDataIndex(index), "ASC");
54666                 break;
54667             case "desc":
54668                 ds.sort(cm.getDataIndex(index), "DESC");
54669                 break;
54670             case "lock":
54671                 var lc = cm.getLockedCount();
54672                 if(cm.getColumnCount(true) <= lc+1){
54673                     this.onDenyColumnLock();
54674                     return;
54675                 }
54676                 if(lc != index){
54677                     cm.setLocked(index, true, true);
54678                     cm.moveColumn(index, lc);
54679                     this.grid.fireEvent("columnmove", index, lc);
54680                 }else{
54681                     cm.setLocked(index, true);
54682                 }
54683             break;
54684             case "unlock":
54685                 var lc = cm.getLockedCount();
54686                 if((lc-1) != index){
54687                     cm.setLocked(index, false, true);
54688                     cm.moveColumn(index, lc-1);
54689                     this.grid.fireEvent("columnmove", index, lc-1);
54690                 }else{
54691                     cm.setLocked(index, false);
54692                 }
54693             break;
54694             case 'wider': // used to expand cols on touch..
54695             case 'narrow':
54696                 var cw = cm.getColumnWidth(index);
54697                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54698                 cw = Math.max(0, cw);
54699                 cw = Math.min(cw,4000);
54700                 cm.setColumnWidth(index, cw);
54701                 break;
54702                 
54703             default:
54704                 index = cm.getIndexById(item.id.substr(4));
54705                 if(index != -1){
54706                     if(item.checked && cm.getColumnCount(true) <= 1){
54707                         this.onDenyColumnHide();
54708                         return false;
54709                     }
54710                     cm.setHidden(index, item.checked);
54711                 }
54712         }
54713         return true;
54714     },
54715
54716     beforeColMenuShow : function(){
54717         var cm = this.cm,  colCount = cm.getColumnCount();
54718         this.colMenu.removeAll();
54719         for(var i = 0; i < colCount; i++){
54720             this.colMenu.add(new Roo.menu.CheckItem({
54721                 id: "col-"+cm.getColumnId(i),
54722                 text: cm.getColumnHeader(i),
54723                 checked: !cm.isHidden(i),
54724                 hideOnClick:false
54725             }));
54726         }
54727     },
54728
54729     handleHdCtx : function(g, index, e){
54730         e.stopEvent();
54731         var hd = this.getHeaderCell(index);
54732         this.hdCtxIndex = index;
54733         var ms = this.hmenu.items, cm = this.cm;
54734         ms.get("asc").setDisabled(!cm.isSortable(index));
54735         ms.get("desc").setDisabled(!cm.isSortable(index));
54736         if(this.grid.enableColLock !== false){
54737             ms.get("lock").setDisabled(cm.isLocked(index));
54738             ms.get("unlock").setDisabled(!cm.isLocked(index));
54739         }
54740         this.hmenu.show(hd, "tl-bl");
54741     },
54742
54743     handleHdOver : function(e){
54744         var hd = this.findHeaderCell(e.getTarget());
54745         if(hd && !this.headersDisabled){
54746             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54747                this.fly(hd).addClass("x-grid-hd-over");
54748             }
54749         }
54750     },
54751
54752     handleHdOut : function(e){
54753         var hd = this.findHeaderCell(e.getTarget());
54754         if(hd){
54755             this.fly(hd).removeClass("x-grid-hd-over");
54756         }
54757     },
54758
54759     handleSplitDblClick : function(e, t){
54760         var i = this.getCellIndex(t);
54761         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54762             this.autoSizeColumn(i, true);
54763             this.layout();
54764         }
54765     },
54766
54767     render : function(){
54768
54769         var cm = this.cm;
54770         var colCount = cm.getColumnCount();
54771
54772         if(this.grid.monitorWindowResize === true){
54773             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54774         }
54775         var header = this.renderHeaders();
54776         var body = this.templates.body.apply({rows:""});
54777         var html = this.templates.master.apply({
54778             lockedBody: body,
54779             body: body,
54780             lockedHeader: header[0],
54781             header: header[1]
54782         });
54783
54784         //this.updateColumns();
54785
54786         this.grid.getGridEl().dom.innerHTML = html;
54787
54788         this.initElements();
54789         
54790         // a kludge to fix the random scolling effect in webkit
54791         this.el.on("scroll", function() {
54792             this.el.dom.scrollTop=0; // hopefully not recursive..
54793         },this);
54794
54795         this.scroller.on("scroll", this.handleScroll, this);
54796         this.lockedBody.on("mousewheel", this.handleWheel, this);
54797         this.mainBody.on("mousewheel", this.handleWheel, this);
54798
54799         this.mainHd.on("mouseover", this.handleHdOver, this);
54800         this.mainHd.on("mouseout", this.handleHdOut, this);
54801         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54802                 {delegate: "."+this.splitClass});
54803
54804         this.lockedHd.on("mouseover", this.handleHdOver, this);
54805         this.lockedHd.on("mouseout", this.handleHdOut, this);
54806         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54807                 {delegate: "."+this.splitClass});
54808
54809         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54810             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54811         }
54812
54813         this.updateSplitters();
54814
54815         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54816             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54817             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54818         }
54819
54820         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54821             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54822             this.hmenu.add(
54823                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54824                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54825             );
54826             if(this.grid.enableColLock !== false){
54827                 this.hmenu.add('-',
54828                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54829                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54830                 );
54831             }
54832             if (Roo.isTouch) {
54833                  this.hmenu.add('-',
54834                     {id:"wider", text: this.columnsWiderText},
54835                     {id:"narrow", text: this.columnsNarrowText }
54836                 );
54837                 
54838                  
54839             }
54840             
54841             if(this.grid.enableColumnHide !== false){
54842
54843                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54844                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54845                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54846
54847                 this.hmenu.add('-',
54848                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54849                 );
54850             }
54851             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54852
54853             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54854         }
54855
54856         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54857             this.dd = new Roo.grid.GridDragZone(this.grid, {
54858                 ddGroup : this.grid.ddGroup || 'GridDD'
54859             });
54860             
54861         }
54862
54863         /*
54864         for(var i = 0; i < colCount; i++){
54865             if(cm.isHidden(i)){
54866                 this.hideColumn(i);
54867             }
54868             if(cm.config[i].align){
54869                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54870                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54871             }
54872         }*/
54873         
54874         this.updateHeaderSortState();
54875
54876         this.beforeInitialResize();
54877         this.layout(true);
54878
54879         // two part rendering gives faster view to the user
54880         this.renderPhase2.defer(1, this);
54881     },
54882
54883     renderPhase2 : function(){
54884         // render the rows now
54885         this.refresh();
54886         if(this.grid.autoSizeColumns){
54887             this.autoSizeColumns();
54888         }
54889     },
54890
54891     beforeInitialResize : function(){
54892
54893     },
54894
54895     onColumnSplitterMoved : function(i, w){
54896         this.userResized = true;
54897         var cm = this.grid.colModel;
54898         cm.setColumnWidth(i, w, true);
54899         var cid = cm.getColumnId(i);
54900         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54901         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54902         this.updateSplitters();
54903         this.layout();
54904         this.grid.fireEvent("columnresize", i, w);
54905     },
54906
54907     syncRowHeights : function(startIndex, endIndex){
54908         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54909             startIndex = startIndex || 0;
54910             var mrows = this.getBodyTable().rows;
54911             var lrows = this.getLockedTable().rows;
54912             var len = mrows.length-1;
54913             endIndex = Math.min(endIndex || len, len);
54914             for(var i = startIndex; i <= endIndex; i++){
54915                 var m = mrows[i], l = lrows[i];
54916                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54917                 m.style.height = l.style.height = h + "px";
54918             }
54919         }
54920     },
54921
54922     layout : function(initialRender, is2ndPass){
54923         var g = this.grid;
54924         var auto = g.autoHeight;
54925         var scrollOffset = 16;
54926         var c = g.getGridEl(), cm = this.cm,
54927                 expandCol = g.autoExpandColumn,
54928                 gv = this;
54929         //c.beginMeasure();
54930
54931         if(!c.dom.offsetWidth){ // display:none?
54932             if(initialRender){
54933                 this.lockedWrap.show();
54934                 this.mainWrap.show();
54935             }
54936             return;
54937         }
54938
54939         var hasLock = this.cm.isLocked(0);
54940
54941         var tbh = this.headerPanel.getHeight();
54942         var bbh = this.footerPanel.getHeight();
54943
54944         if(auto){
54945             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54946             var newHeight = ch + c.getBorderWidth("tb");
54947             if(g.maxHeight){
54948                 newHeight = Math.min(g.maxHeight, newHeight);
54949             }
54950             c.setHeight(newHeight);
54951         }
54952
54953         if(g.autoWidth){
54954             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54955         }
54956
54957         var s = this.scroller;
54958
54959         var csize = c.getSize(true);
54960
54961         this.el.setSize(csize.width, csize.height);
54962
54963         this.headerPanel.setWidth(csize.width);
54964         this.footerPanel.setWidth(csize.width);
54965
54966         var hdHeight = this.mainHd.getHeight();
54967         var vw = csize.width;
54968         var vh = csize.height - (tbh + bbh);
54969
54970         s.setSize(vw, vh);
54971
54972         var bt = this.getBodyTable();
54973         var ltWidth = hasLock ?
54974                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54975
54976         var scrollHeight = bt.offsetHeight;
54977         var scrollWidth = ltWidth + bt.offsetWidth;
54978         var vscroll = false, hscroll = false;
54979
54980         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54981
54982         var lw = this.lockedWrap, mw = this.mainWrap;
54983         var lb = this.lockedBody, mb = this.mainBody;
54984
54985         setTimeout(function(){
54986             var t = s.dom.offsetTop;
54987             var w = s.dom.clientWidth,
54988                 h = s.dom.clientHeight;
54989
54990             lw.setTop(t);
54991             lw.setSize(ltWidth, h);
54992
54993             mw.setLeftTop(ltWidth, t);
54994             mw.setSize(w-ltWidth, h);
54995
54996             lb.setHeight(h-hdHeight);
54997             mb.setHeight(h-hdHeight);
54998
54999             if(is2ndPass !== true && !gv.userResized && expandCol){
55000                 // high speed resize without full column calculation
55001                 
55002                 var ci = cm.getIndexById(expandCol);
55003                 if (ci < 0) {
55004                     ci = cm.findColumnIndex(expandCol);
55005                 }
55006                 ci = Math.max(0, ci); // make sure it's got at least the first col.
55007                 var expandId = cm.getColumnId(ci);
55008                 var  tw = cm.getTotalWidth(false);
55009                 var currentWidth = cm.getColumnWidth(ci);
55010                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
55011                 if(currentWidth != cw){
55012                     cm.setColumnWidth(ci, cw, true);
55013                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55014                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
55015                     gv.updateSplitters();
55016                     gv.layout(false, true);
55017                 }
55018             }
55019
55020             if(initialRender){
55021                 lw.show();
55022                 mw.show();
55023             }
55024             //c.endMeasure();
55025         }, 10);
55026     },
55027
55028     onWindowResize : function(){
55029         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
55030             return;
55031         }
55032         this.layout();
55033     },
55034
55035     appendFooter : function(parentEl){
55036         return null;
55037     },
55038
55039     sortAscText : "Sort Ascending",
55040     sortDescText : "Sort Descending",
55041     lockText : "Lock Column",
55042     unlockText : "Unlock Column",
55043     columnsText : "Columns",
55044  
55045     columnsWiderText : "Wider",
55046     columnsNarrowText : "Thinner"
55047 });
55048
55049
55050 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
55051     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
55052     this.proxy.el.addClass('x-grid3-col-dd');
55053 };
55054
55055 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
55056     handleMouseDown : function(e){
55057
55058     },
55059
55060     callHandleMouseDown : function(e){
55061         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
55062     }
55063 });
55064 /*
55065  * Based on:
55066  * Ext JS Library 1.1.1
55067  * Copyright(c) 2006-2007, Ext JS, LLC.
55068  *
55069  * Originally Released Under LGPL - original licence link has changed is not relivant.
55070  *
55071  * Fork - LGPL
55072  * <script type="text/javascript">
55073  */
55074  
55075 // private
55076 // This is a support class used internally by the Grid components
55077 Roo.grid.SplitDragZone = function(grid, hd, hd2){
55078     this.grid = grid;
55079     this.view = grid.getView();
55080     this.proxy = this.view.resizeProxy;
55081     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
55082         "gridSplitters" + this.grid.getGridEl().id, {
55083         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
55084     });
55085     this.setHandleElId(Roo.id(hd));
55086     this.setOuterHandleElId(Roo.id(hd2));
55087     this.scroll = false;
55088 };
55089 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
55090     fly: Roo.Element.fly,
55091
55092     b4StartDrag : function(x, y){
55093         this.view.headersDisabled = true;
55094         this.proxy.setHeight(this.view.mainWrap.getHeight());
55095         var w = this.cm.getColumnWidth(this.cellIndex);
55096         var minw = Math.max(w-this.grid.minColumnWidth, 0);
55097         this.resetConstraints();
55098         this.setXConstraint(minw, 1000);
55099         this.setYConstraint(0, 0);
55100         this.minX = x - minw;
55101         this.maxX = x + 1000;
55102         this.startPos = x;
55103         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
55104     },
55105
55106
55107     handleMouseDown : function(e){
55108         ev = Roo.EventObject.setEvent(e);
55109         var t = this.fly(ev.getTarget());
55110         if(t.hasClass("x-grid-split")){
55111             this.cellIndex = this.view.getCellIndex(t.dom);
55112             this.split = t.dom;
55113             this.cm = this.grid.colModel;
55114             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
55115                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
55116             }
55117         }
55118     },
55119
55120     endDrag : function(e){
55121         this.view.headersDisabled = false;
55122         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
55123         var diff = endX - this.startPos;
55124         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
55125     },
55126
55127     autoOffset : function(){
55128         this.setDelta(0,0);
55129     }
55130 });/*
55131  * Based on:
55132  * Ext JS Library 1.1.1
55133  * Copyright(c) 2006-2007, Ext JS, LLC.
55134  *
55135  * Originally Released Under LGPL - original licence link has changed is not relivant.
55136  *
55137  * Fork - LGPL
55138  * <script type="text/javascript">
55139  */
55140  
55141 // private
55142 // This is a support class used internally by the Grid components
55143 Roo.grid.GridDragZone = function(grid, config){
55144     this.view = grid.getView();
55145     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
55146     if(this.view.lockedBody){
55147         this.setHandleElId(Roo.id(this.view.mainBody.dom));
55148         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
55149     }
55150     this.scroll = false;
55151     this.grid = grid;
55152     this.ddel = document.createElement('div');
55153     this.ddel.className = 'x-grid-dd-wrap';
55154 };
55155
55156 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
55157     ddGroup : "GridDD",
55158
55159     getDragData : function(e){
55160         var t = Roo.lib.Event.getTarget(e);
55161         var rowIndex = this.view.findRowIndex(t);
55162         var sm = this.grid.selModel;
55163             
55164         //Roo.log(rowIndex);
55165         
55166         if (sm.getSelectedCell) {
55167             // cell selection..
55168             if (!sm.getSelectedCell()) {
55169                 return false;
55170             }
55171             if (rowIndex != sm.getSelectedCell()[0]) {
55172                 return false;
55173             }
55174         
55175         }
55176         
55177         if(rowIndex !== false){
55178             
55179             // if editorgrid.. 
55180             
55181             
55182             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
55183                
55184             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
55185               //  
55186             //}
55187             if (e.hasModifier()){
55188                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
55189             }
55190             
55191             Roo.log("getDragData");
55192             
55193             return {
55194                 grid: this.grid,
55195                 ddel: this.ddel,
55196                 rowIndex: rowIndex,
55197                 selections:sm.getSelections ? sm.getSelections() : (
55198                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
55199                 )
55200             };
55201         }
55202         return false;
55203     },
55204
55205     onInitDrag : function(e){
55206         var data = this.dragData;
55207         this.ddel.innerHTML = this.grid.getDragDropText();
55208         this.proxy.update(this.ddel);
55209         // fire start drag?
55210     },
55211
55212     afterRepair : function(){
55213         this.dragging = false;
55214     },
55215
55216     getRepairXY : function(e, data){
55217         return false;
55218     },
55219
55220     onEndDrag : function(data, e){
55221         // fire end drag?
55222     },
55223
55224     onValidDrop : function(dd, e, id){
55225         // fire drag drop?
55226         this.hideProxy();
55227     },
55228
55229     beforeInvalidDrop : function(e, id){
55230
55231     }
55232 });/*
55233  * Based on:
55234  * Ext JS Library 1.1.1
55235  * Copyright(c) 2006-2007, Ext JS, LLC.
55236  *
55237  * Originally Released Under LGPL - original licence link has changed is not relivant.
55238  *
55239  * Fork - LGPL
55240  * <script type="text/javascript">
55241  */
55242  
55243
55244 /**
55245  * @class Roo.grid.ColumnModel
55246  * @extends Roo.util.Observable
55247  * This is the default implementation of a ColumnModel used by the Grid. It defines
55248  * the columns in the grid.
55249  * <br>Usage:<br>
55250  <pre><code>
55251  var colModel = new Roo.grid.ColumnModel([
55252         {header: "Ticker", width: 60, sortable: true, locked: true},
55253         {header: "Company Name", width: 150, sortable: true},
55254         {header: "Market Cap.", width: 100, sortable: true},
55255         {header: "$ Sales", width: 100, sortable: true, renderer: money},
55256         {header: "Employees", width: 100, sortable: true, resizable: false}
55257  ]);
55258  </code></pre>
55259  * <p>
55260  
55261  * The config options listed for this class are options which may appear in each
55262  * individual column definition.
55263  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
55264  * @constructor
55265  * @param {Object} config An Array of column config objects. See this class's
55266  * config objects for details.
55267 */
55268 Roo.grid.ColumnModel = function(config){
55269         /**
55270      * The config passed into the constructor
55271      */
55272     this.config = config;
55273     this.lookup = {};
55274
55275     // if no id, create one
55276     // if the column does not have a dataIndex mapping,
55277     // map it to the order it is in the config
55278     for(var i = 0, len = config.length; i < len; i++){
55279         var c = config[i];
55280         if(typeof c.dataIndex == "undefined"){
55281             c.dataIndex = i;
55282         }
55283         if(typeof c.renderer == "string"){
55284             c.renderer = Roo.util.Format[c.renderer];
55285         }
55286         if(typeof c.id == "undefined"){
55287             c.id = Roo.id();
55288         }
55289         if(c.editor && c.editor.xtype){
55290             c.editor  = Roo.factory(c.editor, Roo.grid);
55291         }
55292         if(c.editor && c.editor.isFormField){
55293             c.editor = new Roo.grid.GridEditor(c.editor);
55294         }
55295         this.lookup[c.id] = c;
55296     }
55297
55298     /**
55299      * The width of columns which have no width specified (defaults to 100)
55300      * @type Number
55301      */
55302     this.defaultWidth = 100;
55303
55304     /**
55305      * Default sortable of columns which have no sortable specified (defaults to false)
55306      * @type Boolean
55307      */
55308     this.defaultSortable = false;
55309
55310     this.addEvents({
55311         /**
55312              * @event widthchange
55313              * Fires when the width of a column changes.
55314              * @param {ColumnModel} this
55315              * @param {Number} columnIndex The column index
55316              * @param {Number} newWidth The new width
55317              */
55318             "widthchange": true,
55319         /**
55320              * @event headerchange
55321              * Fires when the text of a header changes.
55322              * @param {ColumnModel} this
55323              * @param {Number} columnIndex The column index
55324              * @param {Number} newText The new header text
55325              */
55326             "headerchange": true,
55327         /**
55328              * @event hiddenchange
55329              * Fires when a column is hidden or "unhidden".
55330              * @param {ColumnModel} this
55331              * @param {Number} columnIndex The column index
55332              * @param {Boolean} hidden true if hidden, false otherwise
55333              */
55334             "hiddenchange": true,
55335             /**
55336          * @event columnmoved
55337          * Fires when a column is moved.
55338          * @param {ColumnModel} this
55339          * @param {Number} oldIndex
55340          * @param {Number} newIndex
55341          */
55342         "columnmoved" : true,
55343         /**
55344          * @event columlockchange
55345          * Fires when a column's locked state is changed
55346          * @param {ColumnModel} this
55347          * @param {Number} colIndex
55348          * @param {Boolean} locked true if locked
55349          */
55350         "columnlockchange" : true
55351     });
55352     Roo.grid.ColumnModel.superclass.constructor.call(this);
55353 };
55354 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
55355     /**
55356      * @cfg {String} header The header text to display in the Grid view.
55357      */
55358     /**
55359      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
55360      * {@link Roo.data.Record} definition from which to draw the column's value. If not
55361      * specified, the column's index is used as an index into the Record's data Array.
55362      */
55363     /**
55364      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
55365      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
55366      */
55367     /**
55368      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
55369      * Defaults to the value of the {@link #defaultSortable} property.
55370      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
55371      */
55372     /**
55373      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
55374      */
55375     /**
55376      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
55377      */
55378     /**
55379      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
55380      */
55381     /**
55382      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
55383      */
55384     /**
55385      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
55386      * given the cell's data value. See {@link #setRenderer}. If not specified, the
55387      * default renderer uses the raw data value. If an object is returned (bootstrap only)
55388      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
55389      */
55390        /**
55391      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
55392      */
55393     /**
55394      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55395      */
55396     /**
55397      * @cfg {String} cursor (Optional)
55398      */
55399     /**
55400      * @cfg {String} tooltip (Optional)
55401      */
55402     /**
55403      * Returns the id of the column at the specified index.
55404      * @param {Number} index The column index
55405      * @return {String} the id
55406      */
55407     getColumnId : function(index){
55408         return this.config[index].id;
55409     },
55410
55411     /**
55412      * Returns the column for a specified id.
55413      * @param {String} id The column id
55414      * @return {Object} the column
55415      */
55416     getColumnById : function(id){
55417         return this.lookup[id];
55418     },
55419
55420     
55421     /**
55422      * Returns the column for a specified dataIndex.
55423      * @param {String} dataIndex The column dataIndex
55424      * @return {Object|Boolean} the column or false if not found
55425      */
55426     getColumnByDataIndex: function(dataIndex){
55427         var index = this.findColumnIndex(dataIndex);
55428         return index > -1 ? this.config[index] : false;
55429     },
55430     
55431     /**
55432      * Returns the index for a specified column id.
55433      * @param {String} id The column id
55434      * @return {Number} the index, or -1 if not found
55435      */
55436     getIndexById : function(id){
55437         for(var i = 0, len = this.config.length; i < len; i++){
55438             if(this.config[i].id == id){
55439                 return i;
55440             }
55441         }
55442         return -1;
55443     },
55444     
55445     /**
55446      * Returns the index for a specified column dataIndex.
55447      * @param {String} dataIndex The column dataIndex
55448      * @return {Number} the index, or -1 if not found
55449      */
55450     
55451     findColumnIndex : function(dataIndex){
55452         for(var i = 0, len = this.config.length; i < len; i++){
55453             if(this.config[i].dataIndex == dataIndex){
55454                 return i;
55455             }
55456         }
55457         return -1;
55458     },
55459     
55460     
55461     moveColumn : function(oldIndex, newIndex){
55462         var c = this.config[oldIndex];
55463         this.config.splice(oldIndex, 1);
55464         this.config.splice(newIndex, 0, c);
55465         this.dataMap = null;
55466         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55467     },
55468
55469     isLocked : function(colIndex){
55470         return this.config[colIndex].locked === true;
55471     },
55472
55473     setLocked : function(colIndex, value, suppressEvent){
55474         if(this.isLocked(colIndex) == value){
55475             return;
55476         }
55477         this.config[colIndex].locked = value;
55478         if(!suppressEvent){
55479             this.fireEvent("columnlockchange", this, colIndex, value);
55480         }
55481     },
55482
55483     getTotalLockedWidth : function(){
55484         var totalWidth = 0;
55485         for(var i = 0; i < this.config.length; i++){
55486             if(this.isLocked(i) && !this.isHidden(i)){
55487                 this.totalWidth += this.getColumnWidth(i);
55488             }
55489         }
55490         return totalWidth;
55491     },
55492
55493     getLockedCount : function(){
55494         for(var i = 0, len = this.config.length; i < len; i++){
55495             if(!this.isLocked(i)){
55496                 return i;
55497             }
55498         }
55499     },
55500
55501     /**
55502      * Returns the number of columns.
55503      * @return {Number}
55504      */
55505     getColumnCount : function(visibleOnly){
55506         if(visibleOnly === true){
55507             var c = 0;
55508             for(var i = 0, len = this.config.length; i < len; i++){
55509                 if(!this.isHidden(i)){
55510                     c++;
55511                 }
55512             }
55513             return c;
55514         }
55515         return this.config.length;
55516     },
55517
55518     /**
55519      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55520      * @param {Function} fn
55521      * @param {Object} scope (optional)
55522      * @return {Array} result
55523      */
55524     getColumnsBy : function(fn, scope){
55525         var r = [];
55526         for(var i = 0, len = this.config.length; i < len; i++){
55527             var c = this.config[i];
55528             if(fn.call(scope||this, c, i) === true){
55529                 r[r.length] = c;
55530             }
55531         }
55532         return r;
55533     },
55534
55535     /**
55536      * Returns true if the specified column is sortable.
55537      * @param {Number} col The column index
55538      * @return {Boolean}
55539      */
55540     isSortable : function(col){
55541         if(typeof this.config[col].sortable == "undefined"){
55542             return this.defaultSortable;
55543         }
55544         return this.config[col].sortable;
55545     },
55546
55547     /**
55548      * Returns the rendering (formatting) function defined for the column.
55549      * @param {Number} col The column index.
55550      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55551      */
55552     getRenderer : function(col){
55553         if(!this.config[col].renderer){
55554             return Roo.grid.ColumnModel.defaultRenderer;
55555         }
55556         return this.config[col].renderer;
55557     },
55558
55559     /**
55560      * Sets the rendering (formatting) function for a column.
55561      * @param {Number} col The column index
55562      * @param {Function} fn The function to use to process the cell's raw data
55563      * to return HTML markup for the grid view. The render function is called with
55564      * the following parameters:<ul>
55565      * <li>Data value.</li>
55566      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55567      * <li>css A CSS style string to apply to the table cell.</li>
55568      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55569      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55570      * <li>Row index</li>
55571      * <li>Column index</li>
55572      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55573      */
55574     setRenderer : function(col, fn){
55575         this.config[col].renderer = fn;
55576     },
55577
55578     /**
55579      * Returns the width for the specified column.
55580      * @param {Number} col The column index
55581      * @return {Number}
55582      */
55583     getColumnWidth : function(col){
55584         return this.config[col].width * 1 || this.defaultWidth;
55585     },
55586
55587     /**
55588      * Sets the width for a column.
55589      * @param {Number} col The column index
55590      * @param {Number} width The new width
55591      */
55592     setColumnWidth : function(col, width, suppressEvent){
55593         this.config[col].width = width;
55594         this.totalWidth = null;
55595         if(!suppressEvent){
55596              this.fireEvent("widthchange", this, col, width);
55597         }
55598     },
55599
55600     /**
55601      * Returns the total width of all columns.
55602      * @param {Boolean} includeHidden True to include hidden column widths
55603      * @return {Number}
55604      */
55605     getTotalWidth : function(includeHidden){
55606         if(!this.totalWidth){
55607             this.totalWidth = 0;
55608             for(var i = 0, len = this.config.length; i < len; i++){
55609                 if(includeHidden || !this.isHidden(i)){
55610                     this.totalWidth += this.getColumnWidth(i);
55611                 }
55612             }
55613         }
55614         return this.totalWidth;
55615     },
55616
55617     /**
55618      * Returns the header for the specified column.
55619      * @param {Number} col The column index
55620      * @return {String}
55621      */
55622     getColumnHeader : function(col){
55623         return this.config[col].header;
55624     },
55625
55626     /**
55627      * Sets the header for a column.
55628      * @param {Number} col The column index
55629      * @param {String} header The new header
55630      */
55631     setColumnHeader : function(col, header){
55632         this.config[col].header = header;
55633         this.fireEvent("headerchange", this, col, header);
55634     },
55635
55636     /**
55637      * Returns the tooltip for the specified column.
55638      * @param {Number} col The column index
55639      * @return {String}
55640      */
55641     getColumnTooltip : function(col){
55642             return this.config[col].tooltip;
55643     },
55644     /**
55645      * Sets the tooltip for a column.
55646      * @param {Number} col The column index
55647      * @param {String} tooltip The new tooltip
55648      */
55649     setColumnTooltip : function(col, tooltip){
55650             this.config[col].tooltip = tooltip;
55651     },
55652
55653     /**
55654      * Returns the dataIndex for the specified column.
55655      * @param {Number} col The column index
55656      * @return {Number}
55657      */
55658     getDataIndex : function(col){
55659         return this.config[col].dataIndex;
55660     },
55661
55662     /**
55663      * Sets the dataIndex for a column.
55664      * @param {Number} col The column index
55665      * @param {Number} dataIndex The new dataIndex
55666      */
55667     setDataIndex : function(col, dataIndex){
55668         this.config[col].dataIndex = dataIndex;
55669     },
55670
55671     
55672     
55673     /**
55674      * Returns true if the cell is editable.
55675      * @param {Number} colIndex The column index
55676      * @param {Number} rowIndex The row index
55677      * @return {Boolean}
55678      */
55679     isCellEditable : function(colIndex, rowIndex){
55680         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55681     },
55682
55683     /**
55684      * Returns the editor defined for the cell/column.
55685      * return false or null to disable editing.
55686      * @param {Number} colIndex The column index
55687      * @param {Number} rowIndex The row index
55688      * @return {Object}
55689      */
55690     getCellEditor : function(colIndex, rowIndex){
55691         return this.config[colIndex].editor;
55692     },
55693
55694     /**
55695      * Sets if a column is editable.
55696      * @param {Number} col The column index
55697      * @param {Boolean} editable True if the column is editable
55698      */
55699     setEditable : function(col, editable){
55700         this.config[col].editable = editable;
55701     },
55702
55703
55704     /**
55705      * Returns true if the column is hidden.
55706      * @param {Number} colIndex The column index
55707      * @return {Boolean}
55708      */
55709     isHidden : function(colIndex){
55710         return this.config[colIndex].hidden;
55711     },
55712
55713
55714     /**
55715      * Returns true if the column width cannot be changed
55716      */
55717     isFixed : function(colIndex){
55718         return this.config[colIndex].fixed;
55719     },
55720
55721     /**
55722      * Returns true if the column can be resized
55723      * @return {Boolean}
55724      */
55725     isResizable : function(colIndex){
55726         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55727     },
55728     /**
55729      * Sets if a column is hidden.
55730      * @param {Number} colIndex The column index
55731      * @param {Boolean} hidden True if the column is hidden
55732      */
55733     setHidden : function(colIndex, hidden){
55734         this.config[colIndex].hidden = hidden;
55735         this.totalWidth = null;
55736         this.fireEvent("hiddenchange", this, colIndex, hidden);
55737     },
55738
55739     /**
55740      * Sets the editor for a column.
55741      * @param {Number} col The column index
55742      * @param {Object} editor The editor object
55743      */
55744     setEditor : function(col, editor){
55745         this.config[col].editor = editor;
55746     }
55747 });
55748
55749 Roo.grid.ColumnModel.defaultRenderer = function(value){
55750         if(typeof value == "string" && value.length < 1){
55751             return "&#160;";
55752         }
55753         return value;
55754 };
55755
55756 // Alias for backwards compatibility
55757 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55758 /*
55759  * Based on:
55760  * Ext JS Library 1.1.1
55761  * Copyright(c) 2006-2007, Ext JS, LLC.
55762  *
55763  * Originally Released Under LGPL - original licence link has changed is not relivant.
55764  *
55765  * Fork - LGPL
55766  * <script type="text/javascript">
55767  */
55768
55769 /**
55770  * @class Roo.grid.AbstractSelectionModel
55771  * @extends Roo.util.Observable
55772  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55773  * implemented by descendant classes.  This class should not be directly instantiated.
55774  * @constructor
55775  */
55776 Roo.grid.AbstractSelectionModel = function(){
55777     this.locked = false;
55778     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55779 };
55780
55781 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55782     /** @ignore Called by the grid automatically. Do not call directly. */
55783     init : function(grid){
55784         this.grid = grid;
55785         this.initEvents();
55786     },
55787
55788     /**
55789      * Locks the selections.
55790      */
55791     lock : function(){
55792         this.locked = true;
55793     },
55794
55795     /**
55796      * Unlocks the selections.
55797      */
55798     unlock : function(){
55799         this.locked = false;
55800     },
55801
55802     /**
55803      * Returns true if the selections are locked.
55804      * @return {Boolean}
55805      */
55806     isLocked : function(){
55807         return this.locked;
55808     }
55809 });/*
55810  * Based on:
55811  * Ext JS Library 1.1.1
55812  * Copyright(c) 2006-2007, Ext JS, LLC.
55813  *
55814  * Originally Released Under LGPL - original licence link has changed is not relivant.
55815  *
55816  * Fork - LGPL
55817  * <script type="text/javascript">
55818  */
55819 /**
55820  * @extends Roo.grid.AbstractSelectionModel
55821  * @class Roo.grid.RowSelectionModel
55822  * The default SelectionModel used by {@link Roo.grid.Grid}.
55823  * It supports multiple selections and keyboard selection/navigation. 
55824  * @constructor
55825  * @param {Object} config
55826  */
55827 Roo.grid.RowSelectionModel = function(config){
55828     Roo.apply(this, config);
55829     this.selections = new Roo.util.MixedCollection(false, function(o){
55830         return o.id;
55831     });
55832
55833     this.last = false;
55834     this.lastActive = false;
55835
55836     this.addEvents({
55837         /**
55838              * @event selectionchange
55839              * Fires when the selection changes
55840              * @param {SelectionModel} this
55841              */
55842             "selectionchange" : true,
55843         /**
55844              * @event afterselectionchange
55845              * Fires after the selection changes (eg. by key press or clicking)
55846              * @param {SelectionModel} this
55847              */
55848             "afterselectionchange" : true,
55849         /**
55850              * @event beforerowselect
55851              * Fires when a row is selected being selected, return false to cancel.
55852              * @param {SelectionModel} this
55853              * @param {Number} rowIndex The selected index
55854              * @param {Boolean} keepExisting False if other selections will be cleared
55855              */
55856             "beforerowselect" : true,
55857         /**
55858              * @event rowselect
55859              * Fires when a row is selected.
55860              * @param {SelectionModel} this
55861              * @param {Number} rowIndex The selected index
55862              * @param {Roo.data.Record} r The record
55863              */
55864             "rowselect" : true,
55865         /**
55866              * @event rowdeselect
55867              * Fires when a row is deselected.
55868              * @param {SelectionModel} this
55869              * @param {Number} rowIndex The selected index
55870              */
55871         "rowdeselect" : true
55872     });
55873     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55874     this.locked = false;
55875 };
55876
55877 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55878     /**
55879      * @cfg {Boolean} singleSelect
55880      * True to allow selection of only one row at a time (defaults to false)
55881      */
55882     singleSelect : false,
55883
55884     // private
55885     initEvents : function(){
55886
55887         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55888             this.grid.on("mousedown", this.handleMouseDown, this);
55889         }else{ // allow click to work like normal
55890             this.grid.on("rowclick", this.handleDragableRowClick, this);
55891         }
55892
55893         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55894             "up" : function(e){
55895                 if(!e.shiftKey){
55896                     this.selectPrevious(e.shiftKey);
55897                 }else if(this.last !== false && this.lastActive !== false){
55898                     var last = this.last;
55899                     this.selectRange(this.last,  this.lastActive-1);
55900                     this.grid.getView().focusRow(this.lastActive);
55901                     if(last !== false){
55902                         this.last = last;
55903                     }
55904                 }else{
55905                     this.selectFirstRow();
55906                 }
55907                 this.fireEvent("afterselectionchange", this);
55908             },
55909             "down" : function(e){
55910                 if(!e.shiftKey){
55911                     this.selectNext(e.shiftKey);
55912                 }else if(this.last !== false && this.lastActive !== false){
55913                     var last = this.last;
55914                     this.selectRange(this.last,  this.lastActive+1);
55915                     this.grid.getView().focusRow(this.lastActive);
55916                     if(last !== false){
55917                         this.last = last;
55918                     }
55919                 }else{
55920                     this.selectFirstRow();
55921                 }
55922                 this.fireEvent("afterselectionchange", this);
55923             },
55924             scope: this
55925         });
55926
55927         var view = this.grid.view;
55928         view.on("refresh", this.onRefresh, this);
55929         view.on("rowupdated", this.onRowUpdated, this);
55930         view.on("rowremoved", this.onRemove, this);
55931     },
55932
55933     // private
55934     onRefresh : function(){
55935         var ds = this.grid.dataSource, i, v = this.grid.view;
55936         var s = this.selections;
55937         s.each(function(r){
55938             if((i = ds.indexOfId(r.id)) != -1){
55939                 v.onRowSelect(i);
55940                 s.add(ds.getAt(i)); // updating the selection relate data
55941             }else{
55942                 s.remove(r);
55943             }
55944         });
55945     },
55946
55947     // private
55948     onRemove : function(v, index, r){
55949         this.selections.remove(r);
55950     },
55951
55952     // private
55953     onRowUpdated : function(v, index, r){
55954         if(this.isSelected(r)){
55955             v.onRowSelect(index);
55956         }
55957     },
55958
55959     /**
55960      * Select records.
55961      * @param {Array} records The records to select
55962      * @param {Boolean} keepExisting (optional) True to keep existing selections
55963      */
55964     selectRecords : function(records, keepExisting){
55965         if(!keepExisting){
55966             this.clearSelections();
55967         }
55968         var ds = this.grid.dataSource;
55969         for(var i = 0, len = records.length; i < len; i++){
55970             this.selectRow(ds.indexOf(records[i]), true);
55971         }
55972     },
55973
55974     /**
55975      * Gets the number of selected rows.
55976      * @return {Number}
55977      */
55978     getCount : function(){
55979         return this.selections.length;
55980     },
55981
55982     /**
55983      * Selects the first row in the grid.
55984      */
55985     selectFirstRow : function(){
55986         this.selectRow(0);
55987     },
55988
55989     /**
55990      * Select the last row.
55991      * @param {Boolean} keepExisting (optional) True to keep existing selections
55992      */
55993     selectLastRow : function(keepExisting){
55994         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55995     },
55996
55997     /**
55998      * Selects the row immediately following the last selected row.
55999      * @param {Boolean} keepExisting (optional) True to keep existing selections
56000      */
56001     selectNext : function(keepExisting){
56002         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
56003             this.selectRow(this.last+1, keepExisting);
56004             this.grid.getView().focusRow(this.last);
56005         }
56006     },
56007
56008     /**
56009      * Selects the row that precedes the last selected row.
56010      * @param {Boolean} keepExisting (optional) True to keep existing selections
56011      */
56012     selectPrevious : function(keepExisting){
56013         if(this.last){
56014             this.selectRow(this.last-1, keepExisting);
56015             this.grid.getView().focusRow(this.last);
56016         }
56017     },
56018
56019     /**
56020      * Returns the selected records
56021      * @return {Array} Array of selected records
56022      */
56023     getSelections : function(){
56024         return [].concat(this.selections.items);
56025     },
56026
56027     /**
56028      * Returns the first selected record.
56029      * @return {Record}
56030      */
56031     getSelected : function(){
56032         return this.selections.itemAt(0);
56033     },
56034
56035
56036     /**
56037      * Clears all selections.
56038      */
56039     clearSelections : function(fast){
56040         if(this.locked) return;
56041         if(fast !== true){
56042             var ds = this.grid.dataSource;
56043             var s = this.selections;
56044             s.each(function(r){
56045                 this.deselectRow(ds.indexOfId(r.id));
56046             }, this);
56047             s.clear();
56048         }else{
56049             this.selections.clear();
56050         }
56051         this.last = false;
56052     },
56053
56054
56055     /**
56056      * Selects all rows.
56057      */
56058     selectAll : function(){
56059         if(this.locked) return;
56060         this.selections.clear();
56061         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
56062             this.selectRow(i, true);
56063         }
56064     },
56065
56066     /**
56067      * Returns True if there is a selection.
56068      * @return {Boolean}
56069      */
56070     hasSelection : function(){
56071         return this.selections.length > 0;
56072     },
56073
56074     /**
56075      * Returns True if the specified row is selected.
56076      * @param {Number/Record} record The record or index of the record to check
56077      * @return {Boolean}
56078      */
56079     isSelected : function(index){
56080         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
56081         return (r && this.selections.key(r.id) ? true : false);
56082     },
56083
56084     /**
56085      * Returns True if the specified record id is selected.
56086      * @param {String} id The id of record to check
56087      * @return {Boolean}
56088      */
56089     isIdSelected : function(id){
56090         return (this.selections.key(id) ? true : false);
56091     },
56092
56093     // private
56094     handleMouseDown : function(e, t){
56095         var view = this.grid.getView(), rowIndex;
56096         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
56097             return;
56098         };
56099         if(e.shiftKey && this.last !== false){
56100             var last = this.last;
56101             this.selectRange(last, rowIndex, e.ctrlKey);
56102             this.last = last; // reset the last
56103             view.focusRow(rowIndex);
56104         }else{
56105             var isSelected = this.isSelected(rowIndex);
56106             if(e.button !== 0 && isSelected){
56107                 view.focusRow(rowIndex);
56108             }else if(e.ctrlKey && isSelected){
56109                 this.deselectRow(rowIndex);
56110             }else if(!isSelected){
56111                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
56112                 view.focusRow(rowIndex);
56113             }
56114         }
56115         this.fireEvent("afterselectionchange", this);
56116     },
56117     // private
56118     handleDragableRowClick :  function(grid, rowIndex, e) 
56119     {
56120         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
56121             this.selectRow(rowIndex, false);
56122             grid.view.focusRow(rowIndex);
56123              this.fireEvent("afterselectionchange", this);
56124         }
56125     },
56126     
56127     /**
56128      * Selects multiple rows.
56129      * @param {Array} rows Array of the indexes of the row to select
56130      * @param {Boolean} keepExisting (optional) True to keep existing selections
56131      */
56132     selectRows : function(rows, keepExisting){
56133         if(!keepExisting){
56134             this.clearSelections();
56135         }
56136         for(var i = 0, len = rows.length; i < len; i++){
56137             this.selectRow(rows[i], true);
56138         }
56139     },
56140
56141     /**
56142      * Selects a range of rows. All rows in between startRow and endRow are also selected.
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      * @param {Boolean} keepExisting (optional) True to retain existing selections
56146      */
56147     selectRange : function(startRow, endRow, keepExisting){
56148         if(this.locked) return;
56149         if(!keepExisting){
56150             this.clearSelections();
56151         }
56152         if(startRow <= endRow){
56153             for(var i = startRow; i <= endRow; i++){
56154                 this.selectRow(i, true);
56155             }
56156         }else{
56157             for(var i = startRow; i >= endRow; i--){
56158                 this.selectRow(i, true);
56159             }
56160         }
56161     },
56162
56163     /**
56164      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
56165      * @param {Number} startRow The index of the first row in the range
56166      * @param {Number} endRow The index of the last row in the range
56167      */
56168     deselectRange : function(startRow, endRow, preventViewNotify){
56169         if(this.locked) return;
56170         for(var i = startRow; i <= endRow; i++){
56171             this.deselectRow(i, preventViewNotify);
56172         }
56173     },
56174
56175     /**
56176      * Selects a row.
56177      * @param {Number} row The index of the row to select
56178      * @param {Boolean} keepExisting (optional) True to keep existing selections
56179      */
56180     selectRow : function(index, keepExisting, preventViewNotify){
56181         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
56182         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
56183             if(!keepExisting || this.singleSelect){
56184                 this.clearSelections();
56185             }
56186             var r = this.grid.dataSource.getAt(index);
56187             this.selections.add(r);
56188             this.last = this.lastActive = index;
56189             if(!preventViewNotify){
56190                 this.grid.getView().onRowSelect(index);
56191             }
56192             this.fireEvent("rowselect", this, index, r);
56193             this.fireEvent("selectionchange", this);
56194         }
56195     },
56196
56197     /**
56198      * Deselects a row.
56199      * @param {Number} row The index of the row to deselect
56200      */
56201     deselectRow : function(index, preventViewNotify){
56202         if(this.locked) return;
56203         if(this.last == index){
56204             this.last = false;
56205         }
56206         if(this.lastActive == index){
56207             this.lastActive = false;
56208         }
56209         var r = this.grid.dataSource.getAt(index);
56210         this.selections.remove(r);
56211         if(!preventViewNotify){
56212             this.grid.getView().onRowDeselect(index);
56213         }
56214         this.fireEvent("rowdeselect", this, index);
56215         this.fireEvent("selectionchange", this);
56216     },
56217
56218     // private
56219     restoreLast : function(){
56220         if(this._last){
56221             this.last = this._last;
56222         }
56223     },
56224
56225     // private
56226     acceptsNav : function(row, col, cm){
56227         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56228     },
56229
56230     // private
56231     onEditorKey : function(field, e){
56232         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
56233         if(k == e.TAB){
56234             e.stopEvent();
56235             ed.completeEdit();
56236             if(e.shiftKey){
56237                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56238             }else{
56239                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56240             }
56241         }else if(k == e.ENTER && !e.ctrlKey){
56242             e.stopEvent();
56243             ed.completeEdit();
56244             if(e.shiftKey){
56245                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
56246             }else{
56247                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
56248             }
56249         }else if(k == e.ESC){
56250             ed.cancelEdit();
56251         }
56252         if(newCell){
56253             g.startEditing(newCell[0], newCell[1]);
56254         }
56255     }
56256 });/*
56257  * Based on:
56258  * Ext JS Library 1.1.1
56259  * Copyright(c) 2006-2007, Ext JS, LLC.
56260  *
56261  * Originally Released Under LGPL - original licence link has changed is not relivant.
56262  *
56263  * Fork - LGPL
56264  * <script type="text/javascript">
56265  */
56266 /**
56267  * @class Roo.grid.CellSelectionModel
56268  * @extends Roo.grid.AbstractSelectionModel
56269  * This class provides the basic implementation for cell selection in a grid.
56270  * @constructor
56271  * @param {Object} config The object containing the configuration of this model.
56272  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
56273  */
56274 Roo.grid.CellSelectionModel = function(config){
56275     Roo.apply(this, config);
56276
56277     this.selection = null;
56278
56279     this.addEvents({
56280         /**
56281              * @event beforerowselect
56282              * Fires before a cell is selected.
56283              * @param {SelectionModel} this
56284              * @param {Number} rowIndex The selected row index
56285              * @param {Number} colIndex The selected cell index
56286              */
56287             "beforecellselect" : true,
56288         /**
56289              * @event cellselect
56290              * Fires when a cell is selected.
56291              * @param {SelectionModel} this
56292              * @param {Number} rowIndex The selected row index
56293              * @param {Number} colIndex The selected cell index
56294              */
56295             "cellselect" : true,
56296         /**
56297              * @event selectionchange
56298              * Fires when the active selection changes.
56299              * @param {SelectionModel} this
56300              * @param {Object} selection null for no selection or an object (o) with two properties
56301                 <ul>
56302                 <li>o.record: the record object for the row the selection is in</li>
56303                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
56304                 </ul>
56305              */
56306             "selectionchange" : true,
56307         /**
56308              * @event tabend
56309              * Fires when the tab (or enter) was pressed on the last editable cell
56310              * You can use this to trigger add new row.
56311              * @param {SelectionModel} this
56312              */
56313             "tabend" : true,
56314          /**
56315              * @event beforeeditnext
56316              * Fires before the next editable sell is made active
56317              * You can use this to skip to another cell or fire the tabend
56318              *    if you set cell to false
56319              * @param {Object} eventdata object : { cell : [ row, col ] } 
56320              */
56321             "beforeeditnext" : true
56322     });
56323     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
56324 };
56325
56326 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
56327     
56328     enter_is_tab: false,
56329
56330     /** @ignore */
56331     initEvents : function(){
56332         this.grid.on("mousedown", this.handleMouseDown, this);
56333         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
56334         var view = this.grid.view;
56335         view.on("refresh", this.onViewChange, this);
56336         view.on("rowupdated", this.onRowUpdated, this);
56337         view.on("beforerowremoved", this.clearSelections, this);
56338         view.on("beforerowsinserted", this.clearSelections, this);
56339         if(this.grid.isEditor){
56340             this.grid.on("beforeedit", this.beforeEdit,  this);
56341         }
56342     },
56343
56344         //private
56345     beforeEdit : function(e){
56346         this.select(e.row, e.column, false, true, e.record);
56347     },
56348
56349         //private
56350     onRowUpdated : function(v, index, r){
56351         if(this.selection && this.selection.record == r){
56352             v.onCellSelect(index, this.selection.cell[1]);
56353         }
56354     },
56355
56356         //private
56357     onViewChange : function(){
56358         this.clearSelections(true);
56359     },
56360
56361         /**
56362          * Returns the currently selected cell,.
56363          * @return {Array} The selected cell (row, column) or null if none selected.
56364          */
56365     getSelectedCell : function(){
56366         return this.selection ? this.selection.cell : null;
56367     },
56368
56369     /**
56370      * Clears all selections.
56371      * @param {Boolean} true to prevent the gridview from being notified about the change.
56372      */
56373     clearSelections : function(preventNotify){
56374         var s = this.selection;
56375         if(s){
56376             if(preventNotify !== true){
56377                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
56378             }
56379             this.selection = null;
56380             this.fireEvent("selectionchange", this, null);
56381         }
56382     },
56383
56384     /**
56385      * Returns true if there is a selection.
56386      * @return {Boolean}
56387      */
56388     hasSelection : function(){
56389         return this.selection ? true : false;
56390     },
56391
56392     /** @ignore */
56393     handleMouseDown : function(e, t){
56394         var v = this.grid.getView();
56395         if(this.isLocked()){
56396             return;
56397         };
56398         var row = v.findRowIndex(t);
56399         var cell = v.findCellIndex(t);
56400         if(row !== false && cell !== false){
56401             this.select(row, cell);
56402         }
56403     },
56404
56405     /**
56406      * Selects a cell.
56407      * @param {Number} rowIndex
56408      * @param {Number} collIndex
56409      */
56410     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56411         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56412             this.clearSelections();
56413             r = r || this.grid.dataSource.getAt(rowIndex);
56414             this.selection = {
56415                 record : r,
56416                 cell : [rowIndex, colIndex]
56417             };
56418             if(!preventViewNotify){
56419                 var v = this.grid.getView();
56420                 v.onCellSelect(rowIndex, colIndex);
56421                 if(preventFocus !== true){
56422                     v.focusCell(rowIndex, colIndex);
56423                 }
56424             }
56425             this.fireEvent("cellselect", this, rowIndex, colIndex);
56426             this.fireEvent("selectionchange", this, this.selection);
56427         }
56428     },
56429
56430         //private
56431     isSelectable : function(rowIndex, colIndex, cm){
56432         return !cm.isHidden(colIndex);
56433     },
56434
56435     /** @ignore */
56436     handleKeyDown : function(e){
56437         //Roo.log('Cell Sel Model handleKeyDown');
56438         if(!e.isNavKeyPress()){
56439             return;
56440         }
56441         var g = this.grid, s = this.selection;
56442         if(!s){
56443             e.stopEvent();
56444             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56445             if(cell){
56446                 this.select(cell[0], cell[1]);
56447             }
56448             return;
56449         }
56450         var sm = this;
56451         var walk = function(row, col, step){
56452             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56453         };
56454         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56455         var newCell;
56456
56457       
56458
56459         switch(k){
56460             case e.TAB:
56461                 // handled by onEditorKey
56462                 if (g.isEditor && g.editing) {
56463                     return;
56464                 }
56465                 if(e.shiftKey) {
56466                     newCell = walk(r, c-1, -1);
56467                 } else {
56468                     newCell = walk(r, c+1, 1);
56469                 }
56470                 break;
56471             
56472             case e.DOWN:
56473                newCell = walk(r+1, c, 1);
56474                 break;
56475             
56476             case e.UP:
56477                 newCell = walk(r-1, c, -1);
56478                 break;
56479             
56480             case e.RIGHT:
56481                 newCell = walk(r, c+1, 1);
56482                 break;
56483             
56484             case e.LEFT:
56485                 newCell = walk(r, c-1, -1);
56486                 break;
56487             
56488             case e.ENTER:
56489                 
56490                 if(g.isEditor && !g.editing){
56491                    g.startEditing(r, c);
56492                    e.stopEvent();
56493                    return;
56494                 }
56495                 
56496                 
56497              break;
56498         };
56499         if(newCell){
56500             this.select(newCell[0], newCell[1]);
56501             e.stopEvent();
56502             
56503         }
56504     },
56505
56506     acceptsNav : function(row, col, cm){
56507         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56508     },
56509     /**
56510      * Selects a cell.
56511      * @param {Number} field (not used) - as it's normally used as a listener
56512      * @param {Number} e - event - fake it by using
56513      *
56514      * var e = Roo.EventObjectImpl.prototype;
56515      * e.keyCode = e.TAB
56516      *
56517      * 
56518      */
56519     onEditorKey : function(field, e){
56520         
56521         var k = e.getKey(),
56522             newCell,
56523             g = this.grid,
56524             ed = g.activeEditor,
56525             forward = false;
56526         ///Roo.log('onEditorKey' + k);
56527         
56528         
56529         if (this.enter_is_tab && k == e.ENTER) {
56530             k = e.TAB;
56531         }
56532         
56533         if(k == e.TAB){
56534             if(e.shiftKey){
56535                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56536             }else{
56537                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56538                 forward = true;
56539             }
56540             
56541             e.stopEvent();
56542             
56543         } else if(k == e.ENTER &&  !e.ctrlKey){
56544             ed.completeEdit();
56545             e.stopEvent();
56546             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56547         
56548                 } else if(k == e.ESC){
56549             ed.cancelEdit();
56550         }
56551                 
56552         if (newCell) {
56553             var ecall = { cell : newCell, forward : forward };
56554             this.fireEvent('beforeeditnext', ecall );
56555             newCell = ecall.cell;
56556                         forward = ecall.forward;
56557         }
56558                 
56559         if(newCell){
56560             //Roo.log('next cell after edit');
56561             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56562         } else if (forward) {
56563             // tabbed past last
56564             this.fireEvent.defer(100, this, ['tabend',this]);
56565         }
56566     }
56567 });/*
56568  * Based on:
56569  * Ext JS Library 1.1.1
56570  * Copyright(c) 2006-2007, Ext JS, LLC.
56571  *
56572  * Originally Released Under LGPL - original licence link has changed is not relivant.
56573  *
56574  * Fork - LGPL
56575  * <script type="text/javascript">
56576  */
56577  
56578 /**
56579  * @class Roo.grid.EditorGrid
56580  * @extends Roo.grid.Grid
56581  * Class for creating and editable grid.
56582  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56583  * The container MUST have some type of size defined for the grid to fill. The container will be 
56584  * automatically set to position relative if it isn't already.
56585  * @param {Object} dataSource The data model to bind to
56586  * @param {Object} colModel The column model with info about this grid's columns
56587  */
56588 Roo.grid.EditorGrid = function(container, config){
56589     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56590     this.getGridEl().addClass("xedit-grid");
56591
56592     if(!this.selModel){
56593         this.selModel = new Roo.grid.CellSelectionModel();
56594     }
56595
56596     this.activeEditor = null;
56597
56598         this.addEvents({
56599             /**
56600              * @event beforeedit
56601              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56602              * <ul style="padding:5px;padding-left:16px;">
56603              * <li>grid - This grid</li>
56604              * <li>record - The record being edited</li>
56605              * <li>field - The field name being edited</li>
56606              * <li>value - The value for the field being edited.</li>
56607              * <li>row - The grid row index</li>
56608              * <li>column - The grid column index</li>
56609              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56610              * </ul>
56611              * @param {Object} e An edit event (see above for description)
56612              */
56613             "beforeedit" : true,
56614             /**
56615              * @event afteredit
56616              * Fires after a cell is edited. <br />
56617              * <ul style="padding:5px;padding-left:16px;">
56618              * <li>grid - This grid</li>
56619              * <li>record - The record being edited</li>
56620              * <li>field - The field name being edited</li>
56621              * <li>value - The value being set</li>
56622              * <li>originalValue - The original value for the field, before the edit.</li>
56623              * <li>row - The grid row index</li>
56624              * <li>column - The grid column index</li>
56625              * </ul>
56626              * @param {Object} e An edit event (see above for description)
56627              */
56628             "afteredit" : true,
56629             /**
56630              * @event validateedit
56631              * Fires after a cell is edited, but before the value is set in the record. 
56632          * You can use this to modify the value being set in the field, Return false
56633              * to cancel the change. The edit event object has the following properties <br />
56634              * <ul style="padding:5px;padding-left:16px;">
56635          * <li>editor - This editor</li>
56636              * <li>grid - This grid</li>
56637              * <li>record - The record being edited</li>
56638              * <li>field - The field name being edited</li>
56639              * <li>value - The value being set</li>
56640              * <li>originalValue - The original value for the field, before the edit.</li>
56641              * <li>row - The grid row index</li>
56642              * <li>column - The grid column index</li>
56643              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56644              * </ul>
56645              * @param {Object} e An edit event (see above for description)
56646              */
56647             "validateedit" : true
56648         });
56649     this.on("bodyscroll", this.stopEditing,  this);
56650     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56651 };
56652
56653 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56654     /**
56655      * @cfg {Number} clicksToEdit
56656      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56657      */
56658     clicksToEdit: 2,
56659
56660     // private
56661     isEditor : true,
56662     // private
56663     trackMouseOver: false, // causes very odd FF errors
56664
56665     onCellDblClick : function(g, row, col){
56666         this.startEditing(row, col);
56667     },
56668
56669     onEditComplete : function(ed, value, startValue){
56670         this.editing = false;
56671         this.activeEditor = null;
56672         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56673         var r = ed.record;
56674         var field = this.colModel.getDataIndex(ed.col);
56675         var e = {
56676             grid: this,
56677             record: r,
56678             field: field,
56679             originalValue: startValue,
56680             value: value,
56681             row: ed.row,
56682             column: ed.col,
56683             cancel:false,
56684             editor: ed
56685         };
56686         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56687         cell.show();
56688           
56689         if(String(value) !== String(startValue)){
56690             
56691             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56692                 r.set(field, e.value);
56693                 // if we are dealing with a combo box..
56694                 // then we also set the 'name' colum to be the displayField
56695                 if (ed.field.displayField && ed.field.name) {
56696                     r.set(ed.field.name, ed.field.el.dom.value);
56697                 }
56698                 
56699                 delete e.cancel; //?? why!!!
56700                 this.fireEvent("afteredit", e);
56701             }
56702         } else {
56703             this.fireEvent("afteredit", e); // always fire it!
56704         }
56705         this.view.focusCell(ed.row, ed.col);
56706     },
56707
56708     /**
56709      * Starts editing the specified for the specified row/column
56710      * @param {Number} rowIndex
56711      * @param {Number} colIndex
56712      */
56713     startEditing : function(row, col){
56714         this.stopEditing();
56715         if(this.colModel.isCellEditable(col, row)){
56716             this.view.ensureVisible(row, col, true);
56717           
56718             var r = this.dataSource.getAt(row);
56719             var field = this.colModel.getDataIndex(col);
56720             var cell = Roo.get(this.view.getCell(row,col));
56721             var e = {
56722                 grid: this,
56723                 record: r,
56724                 field: field,
56725                 value: r.data[field],
56726                 row: row,
56727                 column: col,
56728                 cancel:false 
56729             };
56730             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56731                 this.editing = true;
56732                 var ed = this.colModel.getCellEditor(col, row);
56733                 
56734                 if (!ed) {
56735                     return;
56736                 }
56737                 if(!ed.rendered){
56738                     ed.render(ed.parentEl || document.body);
56739                 }
56740                 ed.field.reset();
56741                
56742                 cell.hide();
56743                 
56744                 (function(){ // complex but required for focus issues in safari, ie and opera
56745                     ed.row = row;
56746                     ed.col = col;
56747                     ed.record = r;
56748                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56749                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56750                     this.activeEditor = ed;
56751                     var v = r.data[field];
56752                     ed.startEdit(this.view.getCell(row, col), v);
56753                     // combo's with 'displayField and name set
56754                     if (ed.field.displayField && ed.field.name) {
56755                         ed.field.el.dom.value = r.data[ed.field.name];
56756                     }
56757                     
56758                     
56759                 }).defer(50, this);
56760             }
56761         }
56762     },
56763         
56764     /**
56765      * Stops any active editing
56766      */
56767     stopEditing : function(){
56768         if(this.activeEditor){
56769             this.activeEditor.completeEdit();
56770         }
56771         this.activeEditor = null;
56772     },
56773         
56774          /**
56775      * Called to get grid's drag proxy text, by default returns this.ddText.
56776      * @return {String}
56777      */
56778     getDragDropText : function(){
56779         var count = this.selModel.getSelectedCell() ? 1 : 0;
56780         return String.format(this.ddText, count, count == 1 ? '' : 's');
56781     }
56782         
56783 });/*
56784  * Based on:
56785  * Ext JS Library 1.1.1
56786  * Copyright(c) 2006-2007, Ext JS, LLC.
56787  *
56788  * Originally Released Under LGPL - original licence link has changed is not relivant.
56789  *
56790  * Fork - LGPL
56791  * <script type="text/javascript">
56792  */
56793
56794 // private - not really -- you end up using it !
56795 // This is a support class used internally by the Grid components
56796
56797 /**
56798  * @class Roo.grid.GridEditor
56799  * @extends Roo.Editor
56800  * Class for creating and editable grid elements.
56801  * @param {Object} config any settings (must include field)
56802  */
56803 Roo.grid.GridEditor = function(field, config){
56804     if (!config && field.field) {
56805         config = field;
56806         field = Roo.factory(config.field, Roo.form);
56807     }
56808     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56809     field.monitorTab = false;
56810 };
56811
56812 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56813     
56814     /**
56815      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56816      */
56817     
56818     alignment: "tl-tl",
56819     autoSize: "width",
56820     hideEl : false,
56821     cls: "x-small-editor x-grid-editor",
56822     shim:false,
56823     shadow:"frame"
56824 });/*
56825  * Based on:
56826  * Ext JS Library 1.1.1
56827  * Copyright(c) 2006-2007, Ext JS, LLC.
56828  *
56829  * Originally Released Under LGPL - original licence link has changed is not relivant.
56830  *
56831  * Fork - LGPL
56832  * <script type="text/javascript">
56833  */
56834   
56835
56836   
56837 Roo.grid.PropertyRecord = Roo.data.Record.create([
56838     {name:'name',type:'string'},  'value'
56839 ]);
56840
56841
56842 Roo.grid.PropertyStore = function(grid, source){
56843     this.grid = grid;
56844     this.store = new Roo.data.Store({
56845         recordType : Roo.grid.PropertyRecord
56846     });
56847     this.store.on('update', this.onUpdate,  this);
56848     if(source){
56849         this.setSource(source);
56850     }
56851     Roo.grid.PropertyStore.superclass.constructor.call(this);
56852 };
56853
56854
56855
56856 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56857     setSource : function(o){
56858         this.source = o;
56859         this.store.removeAll();
56860         var data = [];
56861         for(var k in o){
56862             if(this.isEditableValue(o[k])){
56863                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56864             }
56865         }
56866         this.store.loadRecords({records: data}, {}, true);
56867     },
56868
56869     onUpdate : function(ds, record, type){
56870         if(type == Roo.data.Record.EDIT){
56871             var v = record.data['value'];
56872             var oldValue = record.modified['value'];
56873             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56874                 this.source[record.id] = v;
56875                 record.commit();
56876                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56877             }else{
56878                 record.reject();
56879             }
56880         }
56881     },
56882
56883     getProperty : function(row){
56884        return this.store.getAt(row);
56885     },
56886
56887     isEditableValue: function(val){
56888         if(val && val instanceof Date){
56889             return true;
56890         }else if(typeof val == 'object' || typeof val == 'function'){
56891             return false;
56892         }
56893         return true;
56894     },
56895
56896     setValue : function(prop, value){
56897         this.source[prop] = value;
56898         this.store.getById(prop).set('value', value);
56899     },
56900
56901     getSource : function(){
56902         return this.source;
56903     }
56904 });
56905
56906 Roo.grid.PropertyColumnModel = function(grid, store){
56907     this.grid = grid;
56908     var g = Roo.grid;
56909     g.PropertyColumnModel.superclass.constructor.call(this, [
56910         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56911         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56912     ]);
56913     this.store = store;
56914     this.bselect = Roo.DomHelper.append(document.body, {
56915         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56916             {tag: 'option', value: 'true', html: 'true'},
56917             {tag: 'option', value: 'false', html: 'false'}
56918         ]
56919     });
56920     Roo.id(this.bselect);
56921     var f = Roo.form;
56922     this.editors = {
56923         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56924         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56925         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56926         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56927         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56928     };
56929     this.renderCellDelegate = this.renderCell.createDelegate(this);
56930     this.renderPropDelegate = this.renderProp.createDelegate(this);
56931 };
56932
56933 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56934     
56935     
56936     nameText : 'Name',
56937     valueText : 'Value',
56938     
56939     dateFormat : 'm/j/Y',
56940     
56941     
56942     renderDate : function(dateVal){
56943         return dateVal.dateFormat(this.dateFormat);
56944     },
56945
56946     renderBool : function(bVal){
56947         return bVal ? 'true' : 'false';
56948     },
56949
56950     isCellEditable : function(colIndex, rowIndex){
56951         return colIndex == 1;
56952     },
56953
56954     getRenderer : function(col){
56955         return col == 1 ?
56956             this.renderCellDelegate : this.renderPropDelegate;
56957     },
56958
56959     renderProp : function(v){
56960         return this.getPropertyName(v);
56961     },
56962
56963     renderCell : function(val){
56964         var rv = val;
56965         if(val instanceof Date){
56966             rv = this.renderDate(val);
56967         }else if(typeof val == 'boolean'){
56968             rv = this.renderBool(val);
56969         }
56970         return Roo.util.Format.htmlEncode(rv);
56971     },
56972
56973     getPropertyName : function(name){
56974         var pn = this.grid.propertyNames;
56975         return pn && pn[name] ? pn[name] : name;
56976     },
56977
56978     getCellEditor : function(colIndex, rowIndex){
56979         var p = this.store.getProperty(rowIndex);
56980         var n = p.data['name'], val = p.data['value'];
56981         
56982         if(typeof(this.grid.customEditors[n]) == 'string'){
56983             return this.editors[this.grid.customEditors[n]];
56984         }
56985         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56986             return this.grid.customEditors[n];
56987         }
56988         if(val instanceof Date){
56989             return this.editors['date'];
56990         }else if(typeof val == 'number'){
56991             return this.editors['number'];
56992         }else if(typeof val == 'boolean'){
56993             return this.editors['boolean'];
56994         }else{
56995             return this.editors['string'];
56996         }
56997     }
56998 });
56999
57000 /**
57001  * @class Roo.grid.PropertyGrid
57002  * @extends Roo.grid.EditorGrid
57003  * This class represents the  interface of a component based property grid control.
57004  * <br><br>Usage:<pre><code>
57005  var grid = new Roo.grid.PropertyGrid("my-container-id", {
57006       
57007  });
57008  // set any options
57009  grid.render();
57010  * </code></pre>
57011   
57012  * @constructor
57013  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57014  * The container MUST have some type of size defined for the grid to fill. The container will be
57015  * automatically set to position relative if it isn't already.
57016  * @param {Object} config A config object that sets properties on this grid.
57017  */
57018 Roo.grid.PropertyGrid = function(container, config){
57019     config = config || {};
57020     var store = new Roo.grid.PropertyStore(this);
57021     this.store = store;
57022     var cm = new Roo.grid.PropertyColumnModel(this, store);
57023     store.store.sort('name', 'ASC');
57024     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
57025         ds: store.store,
57026         cm: cm,
57027         enableColLock:false,
57028         enableColumnMove:false,
57029         stripeRows:false,
57030         trackMouseOver: false,
57031         clicksToEdit:1
57032     }, config));
57033     this.getGridEl().addClass('x-props-grid');
57034     this.lastEditRow = null;
57035     this.on('columnresize', this.onColumnResize, this);
57036     this.addEvents({
57037          /**
57038              * @event beforepropertychange
57039              * Fires before a property changes (return false to stop?)
57040              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57041              * @param {String} id Record Id
57042              * @param {String} newval New Value
57043          * @param {String} oldval Old Value
57044              */
57045         "beforepropertychange": true,
57046         /**
57047              * @event propertychange
57048              * Fires after a property changes
57049              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
57050              * @param {String} id Record Id
57051              * @param {String} newval New Value
57052          * @param {String} oldval Old Value
57053              */
57054         "propertychange": true
57055     });
57056     this.customEditors = this.customEditors || {};
57057 };
57058 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
57059     
57060      /**
57061      * @cfg {Object} customEditors map of colnames=> custom editors.
57062      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
57063      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
57064      * false disables editing of the field.
57065          */
57066     
57067       /**
57068      * @cfg {Object} propertyNames map of property Names to their displayed value
57069          */
57070     
57071     render : function(){
57072         Roo.grid.PropertyGrid.superclass.render.call(this);
57073         this.autoSize.defer(100, this);
57074     },
57075
57076     autoSize : function(){
57077         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
57078         if(this.view){
57079             this.view.fitColumns();
57080         }
57081     },
57082
57083     onColumnResize : function(){
57084         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
57085         this.autoSize();
57086     },
57087     /**
57088      * Sets the data for the Grid
57089      * accepts a Key => Value object of all the elements avaiable.
57090      * @param {Object} data  to appear in grid.
57091      */
57092     setSource : function(source){
57093         this.store.setSource(source);
57094         //this.autoSize();
57095     },
57096     /**
57097      * Gets all the data from the grid.
57098      * @return {Object} data  data stored in grid
57099      */
57100     getSource : function(){
57101         return this.store.getSource();
57102     }
57103 });/*
57104   
57105  * Licence LGPL
57106  
57107  */
57108  
57109 /**
57110  * @class Roo.grid.Calendar
57111  * @extends Roo.util.Grid
57112  * This class extends the Grid to provide a calendar widget
57113  * <br><br>Usage:<pre><code>
57114  var grid = new Roo.grid.Calendar("my-container-id", {
57115      ds: myDataStore,
57116      cm: myColModel,
57117      selModel: mySelectionModel,
57118      autoSizeColumns: true,
57119      monitorWindowResize: false,
57120      trackMouseOver: true
57121      eventstore : real data store..
57122  });
57123  // set any options
57124  grid.render();
57125   
57126   * @constructor
57127  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57128  * The container MUST have some type of size defined for the grid to fill. The container will be
57129  * automatically set to position relative if it isn't already.
57130  * @param {Object} config A config object that sets properties on this grid.
57131  */
57132 Roo.grid.Calendar = function(container, config){
57133         // initialize the container
57134         this.container = Roo.get(container);
57135         this.container.update("");
57136         this.container.setStyle("overflow", "hidden");
57137     this.container.addClass('x-grid-container');
57138
57139     this.id = this.container.id;
57140
57141     Roo.apply(this, config);
57142     // check and correct shorthanded configs
57143     
57144     var rows = [];
57145     var d =1;
57146     for (var r = 0;r < 6;r++) {
57147         
57148         rows[r]=[];
57149         for (var c =0;c < 7;c++) {
57150             rows[r][c]= '';
57151         }
57152     }
57153     if (this.eventStore) {
57154         this.eventStore= Roo.factory(this.eventStore, Roo.data);
57155         this.eventStore.on('load',this.onLoad, this);
57156         this.eventStore.on('beforeload',this.clearEvents, this);
57157          
57158     }
57159     
57160     this.dataSource = new Roo.data.Store({
57161             proxy: new Roo.data.MemoryProxy(rows),
57162             reader: new Roo.data.ArrayReader({}, [
57163                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
57164     });
57165
57166     this.dataSource.load();
57167     this.ds = this.dataSource;
57168     this.ds.xmodule = this.xmodule || false;
57169     
57170     
57171     var cellRender = function(v,x,r)
57172     {
57173         return String.format(
57174             '<div class="fc-day  fc-widget-content"><div>' +
57175                 '<div class="fc-event-container"></div>' +
57176                 '<div class="fc-day-number">{0}</div>'+
57177                 
57178                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
57179             '</div></div>', v);
57180     
57181     }
57182     
57183     
57184     this.colModel = new Roo.grid.ColumnModel( [
57185         {
57186             xtype: 'ColumnModel',
57187             xns: Roo.grid,
57188             dataIndex : 'weekday0',
57189             header : 'Sunday',
57190             renderer : cellRender
57191         },
57192         {
57193             xtype: 'ColumnModel',
57194             xns: Roo.grid,
57195             dataIndex : 'weekday1',
57196             header : 'Monday',
57197             renderer : cellRender
57198         },
57199         {
57200             xtype: 'ColumnModel',
57201             xns: Roo.grid,
57202             dataIndex : 'weekday2',
57203             header : 'Tuesday',
57204             renderer : cellRender
57205         },
57206         {
57207             xtype: 'ColumnModel',
57208             xns: Roo.grid,
57209             dataIndex : 'weekday3',
57210             header : 'Wednesday',
57211             renderer : cellRender
57212         },
57213         {
57214             xtype: 'ColumnModel',
57215             xns: Roo.grid,
57216             dataIndex : 'weekday4',
57217             header : 'Thursday',
57218             renderer : cellRender
57219         },
57220         {
57221             xtype: 'ColumnModel',
57222             xns: Roo.grid,
57223             dataIndex : 'weekday5',
57224             header : 'Friday',
57225             renderer : cellRender
57226         },
57227         {
57228             xtype: 'ColumnModel',
57229             xns: Roo.grid,
57230             dataIndex : 'weekday6',
57231             header : 'Saturday',
57232             renderer : cellRender
57233         }
57234     ]);
57235     this.cm = this.colModel;
57236     this.cm.xmodule = this.xmodule || false;
57237  
57238         
57239           
57240     //this.selModel = new Roo.grid.CellSelectionModel();
57241     //this.sm = this.selModel;
57242     //this.selModel.init(this);
57243     
57244     
57245     if(this.width){
57246         this.container.setWidth(this.width);
57247     }
57248
57249     if(this.height){
57250         this.container.setHeight(this.height);
57251     }
57252     /** @private */
57253         this.addEvents({
57254         // raw events
57255         /**
57256          * @event click
57257          * The raw click event for the entire grid.
57258          * @param {Roo.EventObject} e
57259          */
57260         "click" : true,
57261         /**
57262          * @event dblclick
57263          * The raw dblclick event for the entire grid.
57264          * @param {Roo.EventObject} e
57265          */
57266         "dblclick" : true,
57267         /**
57268          * @event contextmenu
57269          * The raw contextmenu event for the entire grid.
57270          * @param {Roo.EventObject} e
57271          */
57272         "contextmenu" : true,
57273         /**
57274          * @event mousedown
57275          * The raw mousedown event for the entire grid.
57276          * @param {Roo.EventObject} e
57277          */
57278         "mousedown" : true,
57279         /**
57280          * @event mouseup
57281          * The raw mouseup event for the entire grid.
57282          * @param {Roo.EventObject} e
57283          */
57284         "mouseup" : true,
57285         /**
57286          * @event mouseover
57287          * The raw mouseover event for the entire grid.
57288          * @param {Roo.EventObject} e
57289          */
57290         "mouseover" : true,
57291         /**
57292          * @event mouseout
57293          * The raw mouseout event for the entire grid.
57294          * @param {Roo.EventObject} e
57295          */
57296         "mouseout" : true,
57297         /**
57298          * @event keypress
57299          * The raw keypress event for the entire grid.
57300          * @param {Roo.EventObject} e
57301          */
57302         "keypress" : true,
57303         /**
57304          * @event keydown
57305          * The raw keydown event for the entire grid.
57306          * @param {Roo.EventObject} e
57307          */
57308         "keydown" : true,
57309
57310         // custom events
57311
57312         /**
57313          * @event cellclick
57314          * Fires when a cell is clicked
57315          * @param {Grid} this
57316          * @param {Number} rowIndex
57317          * @param {Number} columnIndex
57318          * @param {Roo.EventObject} e
57319          */
57320         "cellclick" : true,
57321         /**
57322          * @event celldblclick
57323          * Fires when a cell is double clicked
57324          * @param {Grid} this
57325          * @param {Number} rowIndex
57326          * @param {Number} columnIndex
57327          * @param {Roo.EventObject} e
57328          */
57329         "celldblclick" : true,
57330         /**
57331          * @event rowclick
57332          * Fires when a row is clicked
57333          * @param {Grid} this
57334          * @param {Number} rowIndex
57335          * @param {Roo.EventObject} e
57336          */
57337         "rowclick" : true,
57338         /**
57339          * @event rowdblclick
57340          * Fires when a row is double clicked
57341          * @param {Grid} this
57342          * @param {Number} rowIndex
57343          * @param {Roo.EventObject} e
57344          */
57345         "rowdblclick" : true,
57346         /**
57347          * @event headerclick
57348          * Fires when a header is clicked
57349          * @param {Grid} this
57350          * @param {Number} columnIndex
57351          * @param {Roo.EventObject} e
57352          */
57353         "headerclick" : true,
57354         /**
57355          * @event headerdblclick
57356          * Fires when a header cell is double clicked
57357          * @param {Grid} this
57358          * @param {Number} columnIndex
57359          * @param {Roo.EventObject} e
57360          */
57361         "headerdblclick" : true,
57362         /**
57363          * @event rowcontextmenu
57364          * Fires when a row is right clicked
57365          * @param {Grid} this
57366          * @param {Number} rowIndex
57367          * @param {Roo.EventObject} e
57368          */
57369         "rowcontextmenu" : true,
57370         /**
57371          * @event cellcontextmenu
57372          * Fires when a cell is right clicked
57373          * @param {Grid} this
57374          * @param {Number} rowIndex
57375          * @param {Number} cellIndex
57376          * @param {Roo.EventObject} e
57377          */
57378          "cellcontextmenu" : true,
57379         /**
57380          * @event headercontextmenu
57381          * Fires when a header is right clicked
57382          * @param {Grid} this
57383          * @param {Number} columnIndex
57384          * @param {Roo.EventObject} e
57385          */
57386         "headercontextmenu" : true,
57387         /**
57388          * @event bodyscroll
57389          * Fires when the body element is scrolled
57390          * @param {Number} scrollLeft
57391          * @param {Number} scrollTop
57392          */
57393         "bodyscroll" : true,
57394         /**
57395          * @event columnresize
57396          * Fires when the user resizes a column
57397          * @param {Number} columnIndex
57398          * @param {Number} newSize
57399          */
57400         "columnresize" : true,
57401         /**
57402          * @event columnmove
57403          * Fires when the user moves a column
57404          * @param {Number} oldIndex
57405          * @param {Number} newIndex
57406          */
57407         "columnmove" : true,
57408         /**
57409          * @event startdrag
57410          * Fires when row(s) start being dragged
57411          * @param {Grid} this
57412          * @param {Roo.GridDD} dd The drag drop object
57413          * @param {event} e The raw browser event
57414          */
57415         "startdrag" : true,
57416         /**
57417          * @event enddrag
57418          * Fires when a drag operation is complete
57419          * @param {Grid} this
57420          * @param {Roo.GridDD} dd The drag drop object
57421          * @param {event} e The raw browser event
57422          */
57423         "enddrag" : true,
57424         /**
57425          * @event dragdrop
57426          * Fires when dragged row(s) are dropped on a valid DD target
57427          * @param {Grid} this
57428          * @param {Roo.GridDD} dd The drag drop object
57429          * @param {String} targetId The target drag drop object
57430          * @param {event} e The raw browser event
57431          */
57432         "dragdrop" : true,
57433         /**
57434          * @event dragover
57435          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57436          * @param {Grid} this
57437          * @param {Roo.GridDD} dd The drag drop object
57438          * @param {String} targetId The target drag drop object
57439          * @param {event} e The raw browser event
57440          */
57441         "dragover" : true,
57442         /**
57443          * @event dragenter
57444          *  Fires when the dragged row(s) first cross another DD target while being dragged
57445          * @param {Grid} this
57446          * @param {Roo.GridDD} dd The drag drop object
57447          * @param {String} targetId The target drag drop object
57448          * @param {event} e The raw browser event
57449          */
57450         "dragenter" : true,
57451         /**
57452          * @event dragout
57453          * Fires when the dragged row(s) leave another DD target while being dragged
57454          * @param {Grid} this
57455          * @param {Roo.GridDD} dd The drag drop object
57456          * @param {String} targetId The target drag drop object
57457          * @param {event} e The raw browser event
57458          */
57459         "dragout" : true,
57460         /**
57461          * @event rowclass
57462          * Fires when a row is rendered, so you can change add a style to it.
57463          * @param {GridView} gridview   The grid view
57464          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57465          */
57466         'rowclass' : true,
57467
57468         /**
57469          * @event render
57470          * Fires when the grid is rendered
57471          * @param {Grid} grid
57472          */
57473         'render' : true,
57474             /**
57475              * @event select
57476              * Fires when a date is selected
57477              * @param {DatePicker} this
57478              * @param {Date} date The selected date
57479              */
57480         'select': true,
57481         /**
57482              * @event monthchange
57483              * Fires when the displayed month changes 
57484              * @param {DatePicker} this
57485              * @param {Date} date The selected month
57486              */
57487         'monthchange': true,
57488         /**
57489              * @event evententer
57490              * Fires when mouse over an event
57491              * @param {Calendar} this
57492              * @param {event} Event
57493              */
57494         'evententer': true,
57495         /**
57496              * @event eventleave
57497              * Fires when the mouse leaves an
57498              * @param {Calendar} this
57499              * @param {event}
57500              */
57501         'eventleave': true,
57502         /**
57503              * @event eventclick
57504              * Fires when the mouse click an
57505              * @param {Calendar} this
57506              * @param {event}
57507              */
57508         'eventclick': true,
57509         /**
57510              * @event eventrender
57511              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57512              * @param {Calendar} this
57513              * @param {data} data to be modified
57514              */
57515         'eventrender': true
57516         
57517     });
57518
57519     Roo.grid.Grid.superclass.constructor.call(this);
57520     this.on('render', function() {
57521         this.view.el.addClass('x-grid-cal'); 
57522         
57523         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57524
57525     },this);
57526     
57527     if (!Roo.grid.Calendar.style) {
57528         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57529             
57530             
57531             '.x-grid-cal .x-grid-col' :  {
57532                 height: 'auto !important',
57533                 'vertical-align': 'top'
57534             },
57535             '.x-grid-cal  .fc-event-hori' : {
57536                 height: '14px'
57537             }
57538              
57539             
57540         }, Roo.id());
57541     }
57542
57543     
57544     
57545 };
57546 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57547     /**
57548      * @cfg {Store} eventStore The store that loads events.
57549      */
57550     eventStore : 25,
57551
57552      
57553     activeDate : false,
57554     startDay : 0,
57555     autoWidth : true,
57556     monitorWindowResize : false,
57557
57558     
57559     resizeColumns : function() {
57560         var col = (this.view.el.getWidth() / 7) - 3;
57561         // loop through cols, and setWidth
57562         for(var i =0 ; i < 7 ; i++){
57563             this.cm.setColumnWidth(i, col);
57564         }
57565     },
57566      setDate :function(date) {
57567         
57568         Roo.log('setDate?');
57569         
57570         this.resizeColumns();
57571         var vd = this.activeDate;
57572         this.activeDate = date;
57573 //        if(vd && this.el){
57574 //            var t = date.getTime();
57575 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57576 //                Roo.log('using add remove');
57577 //                
57578 //                this.fireEvent('monthchange', this, date);
57579 //                
57580 //                this.cells.removeClass("fc-state-highlight");
57581 //                this.cells.each(function(c){
57582 //                   if(c.dateValue == t){
57583 //                       c.addClass("fc-state-highlight");
57584 //                       setTimeout(function(){
57585 //                            try{c.dom.firstChild.focus();}catch(e){}
57586 //                       }, 50);
57587 //                       return false;
57588 //                   }
57589 //                   return true;
57590 //                });
57591 //                return;
57592 //            }
57593 //        }
57594         
57595         var days = date.getDaysInMonth();
57596         
57597         var firstOfMonth = date.getFirstDateOfMonth();
57598         var startingPos = firstOfMonth.getDay()-this.startDay;
57599         
57600         if(startingPos < this.startDay){
57601             startingPos += 7;
57602         }
57603         
57604         var pm = date.add(Date.MONTH, -1);
57605         var prevStart = pm.getDaysInMonth()-startingPos;
57606 //        
57607         
57608         
57609         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57610         
57611         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57612         //this.cells.addClassOnOver('fc-state-hover');
57613         
57614         var cells = this.cells.elements;
57615         var textEls = this.textNodes;
57616         
57617         //Roo.each(cells, function(cell){
57618         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57619         //});
57620         
57621         days += startingPos;
57622
57623         // convert everything to numbers so it's fast
57624         var day = 86400000;
57625         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57626         //Roo.log(d);
57627         //Roo.log(pm);
57628         //Roo.log(prevStart);
57629         
57630         var today = new Date().clearTime().getTime();
57631         var sel = date.clearTime().getTime();
57632         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57633         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57634         var ddMatch = this.disabledDatesRE;
57635         var ddText = this.disabledDatesText;
57636         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57637         var ddaysText = this.disabledDaysText;
57638         var format = this.format;
57639         
57640         var setCellClass = function(cal, cell){
57641             
57642             //Roo.log('set Cell Class');
57643             cell.title = "";
57644             var t = d.getTime();
57645             
57646             //Roo.log(d);
57647             
57648             
57649             cell.dateValue = t;
57650             if(t == today){
57651                 cell.className += " fc-today";
57652                 cell.className += " fc-state-highlight";
57653                 cell.title = cal.todayText;
57654             }
57655             if(t == sel){
57656                 // disable highlight in other month..
57657                 cell.className += " fc-state-highlight";
57658                 
57659             }
57660             // disabling
57661             if(t < min) {
57662                 //cell.className = " fc-state-disabled";
57663                 cell.title = cal.minText;
57664                 return;
57665             }
57666             if(t > max) {
57667                 //cell.className = " fc-state-disabled";
57668                 cell.title = cal.maxText;
57669                 return;
57670             }
57671             if(ddays){
57672                 if(ddays.indexOf(d.getDay()) != -1){
57673                     // cell.title = ddaysText;
57674                    // cell.className = " fc-state-disabled";
57675                 }
57676             }
57677             if(ddMatch && format){
57678                 var fvalue = d.dateFormat(format);
57679                 if(ddMatch.test(fvalue)){
57680                     cell.title = ddText.replace("%0", fvalue);
57681                    cell.className = " fc-state-disabled";
57682                 }
57683             }
57684             
57685             if (!cell.initialClassName) {
57686                 cell.initialClassName = cell.dom.className;
57687             }
57688             
57689             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57690         };
57691
57692         var i = 0;
57693         
57694         for(; i < startingPos; i++) {
57695             cells[i].dayName =  (++prevStart);
57696             Roo.log(textEls[i]);
57697             d.setDate(d.getDate()+1);
57698             
57699             //cells[i].className = "fc-past fc-other-month";
57700             setCellClass(this, cells[i]);
57701         }
57702         
57703         var intDay = 0;
57704         
57705         for(; i < days; i++){
57706             intDay = i - startingPos + 1;
57707             cells[i].dayName =  (intDay);
57708             d.setDate(d.getDate()+1);
57709             
57710             cells[i].className = ''; // "x-date-active";
57711             setCellClass(this, cells[i]);
57712         }
57713         var extraDays = 0;
57714         
57715         for(; i < 42; i++) {
57716             //textEls[i].innerHTML = (++extraDays);
57717             
57718             d.setDate(d.getDate()+1);
57719             cells[i].dayName = (++extraDays);
57720             cells[i].className = "fc-future fc-other-month";
57721             setCellClass(this, cells[i]);
57722         }
57723         
57724         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57725         
57726         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57727         
57728         // this will cause all the cells to mis
57729         var rows= [];
57730         var i =0;
57731         for (var r = 0;r < 6;r++) {
57732             for (var c =0;c < 7;c++) {
57733                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57734             }    
57735         }
57736         
57737         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57738         for(i=0;i<cells.length;i++) {
57739             
57740             this.cells.elements[i].dayName = cells[i].dayName ;
57741             this.cells.elements[i].className = cells[i].className;
57742             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57743             this.cells.elements[i].title = cells[i].title ;
57744             this.cells.elements[i].dateValue = cells[i].dateValue ;
57745         }
57746         
57747         
57748         
57749         
57750         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57751         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57752         
57753         ////if(totalRows != 6){
57754             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57755            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57756        // }
57757         
57758         this.fireEvent('monthchange', this, date);
57759         
57760         
57761     },
57762  /**
57763      * Returns the grid's SelectionModel.
57764      * @return {SelectionModel}
57765      */
57766     getSelectionModel : function(){
57767         if(!this.selModel){
57768             this.selModel = new Roo.grid.CellSelectionModel();
57769         }
57770         return this.selModel;
57771     },
57772
57773     load: function() {
57774         this.eventStore.load()
57775         
57776         
57777         
57778     },
57779     
57780     findCell : function(dt) {
57781         dt = dt.clearTime().getTime();
57782         var ret = false;
57783         this.cells.each(function(c){
57784             //Roo.log("check " +c.dateValue + '?=' + dt);
57785             if(c.dateValue == dt){
57786                 ret = c;
57787                 return false;
57788             }
57789             return true;
57790         });
57791         
57792         return ret;
57793     },
57794     
57795     findCells : function(rec) {
57796         var s = rec.data.start_dt.clone().clearTime().getTime();
57797        // Roo.log(s);
57798         var e= rec.data.end_dt.clone().clearTime().getTime();
57799        // Roo.log(e);
57800         var ret = [];
57801         this.cells.each(function(c){
57802              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57803             
57804             if(c.dateValue > e){
57805                 return ;
57806             }
57807             if(c.dateValue < s){
57808                 return ;
57809             }
57810             ret.push(c);
57811         });
57812         
57813         return ret;    
57814     },
57815     
57816     findBestRow: function(cells)
57817     {
57818         var ret = 0;
57819         
57820         for (var i =0 ; i < cells.length;i++) {
57821             ret  = Math.max(cells[i].rows || 0,ret);
57822         }
57823         return ret;
57824         
57825     },
57826     
57827     
57828     addItem : function(rec)
57829     {
57830         // look for vertical location slot in
57831         var cells = this.findCells(rec);
57832         
57833         rec.row = this.findBestRow(cells);
57834         
57835         // work out the location.
57836         
57837         var crow = false;
57838         var rows = [];
57839         for(var i =0; i < cells.length; i++) {
57840             if (!crow) {
57841                 crow = {
57842                     start : cells[i],
57843                     end :  cells[i]
57844                 };
57845                 continue;
57846             }
57847             if (crow.start.getY() == cells[i].getY()) {
57848                 // on same row.
57849                 crow.end = cells[i];
57850                 continue;
57851             }
57852             // different row.
57853             rows.push(crow);
57854             crow = {
57855                 start: cells[i],
57856                 end : cells[i]
57857             };
57858             
57859         }
57860         
57861         rows.push(crow);
57862         rec.els = [];
57863         rec.rows = rows;
57864         rec.cells = cells;
57865         for (var i = 0; i < cells.length;i++) {
57866             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57867             
57868         }
57869         
57870         
57871     },
57872     
57873     clearEvents: function() {
57874         
57875         if (!this.eventStore.getCount()) {
57876             return;
57877         }
57878         // reset number of rows in cells.
57879         Roo.each(this.cells.elements, function(c){
57880             c.rows = 0;
57881         });
57882         
57883         this.eventStore.each(function(e) {
57884             this.clearEvent(e);
57885         },this);
57886         
57887     },
57888     
57889     clearEvent : function(ev)
57890     {
57891         if (ev.els) {
57892             Roo.each(ev.els, function(el) {
57893                 el.un('mouseenter' ,this.onEventEnter, this);
57894                 el.un('mouseleave' ,this.onEventLeave, this);
57895                 el.remove();
57896             },this);
57897             ev.els = [];
57898         }
57899     },
57900     
57901     
57902     renderEvent : function(ev,ctr) {
57903         if (!ctr) {
57904              ctr = this.view.el.select('.fc-event-container',true).first();
57905         }
57906         
57907          
57908         this.clearEvent(ev);
57909             //code
57910        
57911         
57912         
57913         ev.els = [];
57914         var cells = ev.cells;
57915         var rows = ev.rows;
57916         this.fireEvent('eventrender', this, ev);
57917         
57918         for(var i =0; i < rows.length; i++) {
57919             
57920             cls = '';
57921             if (i == 0) {
57922                 cls += ' fc-event-start';
57923             }
57924             if ((i+1) == rows.length) {
57925                 cls += ' fc-event-end';
57926             }
57927             
57928             //Roo.log(ev.data);
57929             // how many rows should it span..
57930             var cg = this.eventTmpl.append(ctr,Roo.apply({
57931                 fccls : cls
57932                 
57933             }, ev.data) , true);
57934             
57935             
57936             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57937             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57938             cg.on('click', this.onEventClick, this, ev);
57939             
57940             ev.els.push(cg);
57941             
57942             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57943             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57944             //Roo.log(cg);
57945              
57946             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57947             cg.setWidth(ebox.right - sbox.x -2);
57948         }
57949     },
57950     
57951     renderEvents: function()
57952     {   
57953         // first make sure there is enough space..
57954         
57955         if (!this.eventTmpl) {
57956             this.eventTmpl = new Roo.Template(
57957                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57958                     '<div class="fc-event-inner">' +
57959                         '<span class="fc-event-time">{time}</span>' +
57960                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57961                     '</div>' +
57962                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57963                 '</div>'
57964             );
57965                 
57966         }
57967                
57968         
57969         
57970         this.cells.each(function(c) {
57971             //Roo.log(c.select('.fc-day-content div',true).first());
57972             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57973         });
57974         
57975         var ctr = this.view.el.select('.fc-event-container',true).first();
57976         
57977         var cls;
57978         this.eventStore.each(function(ev){
57979             
57980             this.renderEvent(ev);
57981              
57982              
57983         }, this);
57984         this.view.layout();
57985         
57986     },
57987     
57988     onEventEnter: function (e, el,event,d) {
57989         this.fireEvent('evententer', this, el, event);
57990     },
57991     
57992     onEventLeave: function (e, el,event,d) {
57993         this.fireEvent('eventleave', this, el, event);
57994     },
57995     
57996     onEventClick: function (e, el,event,d) {
57997         this.fireEvent('eventclick', this, el, event);
57998     },
57999     
58000     onMonthChange: function () {
58001         this.store.load();
58002     },
58003     
58004     onLoad: function () {
58005         
58006         //Roo.log('calendar onload');
58007 //         
58008         if(this.eventStore.getCount() > 0){
58009             
58010            
58011             
58012             this.eventStore.each(function(d){
58013                 
58014                 
58015                 // FIXME..
58016                 var add =   d.data;
58017                 if (typeof(add.end_dt) == 'undefined')  {
58018                     Roo.log("Missing End time in calendar data: ");
58019                     Roo.log(d);
58020                     return;
58021                 }
58022                 if (typeof(add.start_dt) == 'undefined')  {
58023                     Roo.log("Missing Start time in calendar data: ");
58024                     Roo.log(d);
58025                     return;
58026                 }
58027                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
58028                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
58029                 add.id = add.id || d.id;
58030                 add.title = add.title || '??';
58031                 
58032                 this.addItem(d);
58033                 
58034              
58035             },this);
58036         }
58037         
58038         this.renderEvents();
58039     }
58040     
58041
58042 });
58043 /*
58044  grid : {
58045                 xtype: 'Grid',
58046                 xns: Roo.grid,
58047                 listeners : {
58048                     render : function ()
58049                     {
58050                         _this.grid = this;
58051                         
58052                         if (!this.view.el.hasClass('course-timesheet')) {
58053                             this.view.el.addClass('course-timesheet');
58054                         }
58055                         if (this.tsStyle) {
58056                             this.ds.load({});
58057                             return; 
58058                         }
58059                         Roo.log('width');
58060                         Roo.log(_this.grid.view.el.getWidth());
58061                         
58062                         
58063                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
58064                             '.course-timesheet .x-grid-row' : {
58065                                 height: '80px'
58066                             },
58067                             '.x-grid-row td' : {
58068                                 'vertical-align' : 0
58069                             },
58070                             '.course-edit-link' : {
58071                                 'color' : 'blue',
58072                                 'text-overflow' : 'ellipsis',
58073                                 'overflow' : 'hidden',
58074                                 'white-space' : 'nowrap',
58075                                 'cursor' : 'pointer'
58076                             },
58077                             '.sub-link' : {
58078                                 'color' : 'green'
58079                             },
58080                             '.de-act-sup-link' : {
58081                                 'color' : 'purple',
58082                                 'text-decoration' : 'line-through'
58083                             },
58084                             '.de-act-link' : {
58085                                 'color' : 'red',
58086                                 'text-decoration' : 'line-through'
58087                             },
58088                             '.course-timesheet .course-highlight' : {
58089                                 'border-top-style': 'dashed !important',
58090                                 'border-bottom-bottom': 'dashed !important'
58091                             },
58092                             '.course-timesheet .course-item' : {
58093                                 'font-family'   : 'tahoma, arial, helvetica',
58094                                 'font-size'     : '11px',
58095                                 'overflow'      : 'hidden',
58096                                 'padding-left'  : '10px',
58097                                 'padding-right' : '10px',
58098                                 'padding-top' : '10px' 
58099                             }
58100                             
58101                         }, Roo.id());
58102                                 this.ds.load({});
58103                     }
58104                 },
58105                 autoWidth : true,
58106                 monitorWindowResize : false,
58107                 cellrenderer : function(v,x,r)
58108                 {
58109                     return v;
58110                 },
58111                 sm : {
58112                     xtype: 'CellSelectionModel',
58113                     xns: Roo.grid
58114                 },
58115                 dataSource : {
58116                     xtype: 'Store',
58117                     xns: Roo.data,
58118                     listeners : {
58119                         beforeload : function (_self, options)
58120                         {
58121                             options.params = options.params || {};
58122                             options.params._month = _this.monthField.getValue();
58123                             options.params.limit = 9999;
58124                             options.params['sort'] = 'when_dt';    
58125                             options.params['dir'] = 'ASC';    
58126                             this.proxy.loadResponse = this.loadResponse;
58127                             Roo.log("load?");
58128                             //this.addColumns();
58129                         },
58130                         load : function (_self, records, options)
58131                         {
58132                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
58133                                 // if you click on the translation.. you can edit it...
58134                                 var el = Roo.get(this);
58135                                 var id = el.dom.getAttribute('data-id');
58136                                 var d = el.dom.getAttribute('data-date');
58137                                 var t = el.dom.getAttribute('data-time');
58138                                 //var id = this.child('span').dom.textContent;
58139                                 
58140                                 //Roo.log(this);
58141                                 Pman.Dialog.CourseCalendar.show({
58142                                     id : id,
58143                                     when_d : d,
58144                                     when_t : t,
58145                                     productitem_active : id ? 1 : 0
58146                                 }, function() {
58147                                     _this.grid.ds.load({});
58148                                 });
58149                            
58150                            });
58151                            
58152                            _this.panel.fireEvent('resize', [ '', '' ]);
58153                         }
58154                     },
58155                     loadResponse : function(o, success, response){
58156                             // this is overridden on before load..
58157                             
58158                             Roo.log("our code?");       
58159                             //Roo.log(success);
58160                             //Roo.log(response)
58161                             delete this.activeRequest;
58162                             if(!success){
58163                                 this.fireEvent("loadexception", this, o, response);
58164                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58165                                 return;
58166                             }
58167                             var result;
58168                             try {
58169                                 result = o.reader.read(response);
58170                             }catch(e){
58171                                 Roo.log("load exception?");
58172                                 this.fireEvent("loadexception", this, o, response, e);
58173                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
58174                                 return;
58175                             }
58176                             Roo.log("ready...");        
58177                             // loop through result.records;
58178                             // and set this.tdate[date] = [] << array of records..
58179                             _this.tdata  = {};
58180                             Roo.each(result.records, function(r){
58181                                 //Roo.log(r.data);
58182                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
58183                                     _this.tdata[r.data.when_dt.format('j')] = [];
58184                                 }
58185                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
58186                             });
58187                             
58188                             //Roo.log(_this.tdata);
58189                             
58190                             result.records = [];
58191                             result.totalRecords = 6;
58192                     
58193                             // let's generate some duumy records for the rows.
58194                             //var st = _this.dateField.getValue();
58195                             
58196                             // work out monday..
58197                             //st = st.add(Date.DAY, -1 * st.format('w'));
58198                             
58199                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58200                             
58201                             var firstOfMonth = date.getFirstDayOfMonth();
58202                             var days = date.getDaysInMonth();
58203                             var d = 1;
58204                             var firstAdded = false;
58205                             for (var i = 0; i < result.totalRecords ; i++) {
58206                                 //var d= st.add(Date.DAY, i);
58207                                 var row = {};
58208                                 var added = 0;
58209                                 for(var w = 0 ; w < 7 ; w++){
58210                                     if(!firstAdded && firstOfMonth != w){
58211                                         continue;
58212                                     }
58213                                     if(d > days){
58214                                         continue;
58215                                     }
58216                                     firstAdded = true;
58217                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
58218                                     row['weekday'+w] = String.format(
58219                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
58220                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
58221                                                     d,
58222                                                     date.format('Y-m-')+dd
58223                                                 );
58224                                     added++;
58225                                     if(typeof(_this.tdata[d]) != 'undefined'){
58226                                         Roo.each(_this.tdata[d], function(r){
58227                                             var is_sub = '';
58228                                             var deactive = '';
58229                                             var id = r.id;
58230                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
58231                                             if(r.parent_id*1>0){
58232                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
58233                                                 id = r.parent_id;
58234                                             }
58235                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
58236                                                 deactive = 'de-act-link';
58237                                             }
58238                                             
58239                                             row['weekday'+w] += String.format(
58240                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
58241                                                     id, //0
58242                                                     r.product_id_name, //1
58243                                                     r.when_dt.format('h:ia'), //2
58244                                                     is_sub, //3
58245                                                     deactive, //4
58246                                                     desc // 5
58247                                             );
58248                                         });
58249                                     }
58250                                     d++;
58251                                 }
58252                                 
58253                                 // only do this if something added..
58254                                 if(added > 0){ 
58255                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
58256                                 }
58257                                 
58258                                 
58259                                 // push it twice. (second one with an hour..
58260                                 
58261                             }
58262                             //Roo.log(result);
58263                             this.fireEvent("load", this, o, o.request.arg);
58264                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
58265                         },
58266                     sortInfo : {field: 'when_dt', direction : 'ASC' },
58267                     proxy : {
58268                         xtype: 'HttpProxy',
58269                         xns: Roo.data,
58270                         method : 'GET',
58271                         url : baseURL + '/Roo/Shop_course.php'
58272                     },
58273                     reader : {
58274                         xtype: 'JsonReader',
58275                         xns: Roo.data,
58276                         id : 'id',
58277                         fields : [
58278                             {
58279                                 'name': 'id',
58280                                 'type': 'int'
58281                             },
58282                             {
58283                                 'name': 'when_dt',
58284                                 'type': 'string'
58285                             },
58286                             {
58287                                 'name': 'end_dt',
58288                                 'type': 'string'
58289                             },
58290                             {
58291                                 'name': 'parent_id',
58292                                 'type': 'int'
58293                             },
58294                             {
58295                                 'name': 'product_id',
58296                                 'type': 'int'
58297                             },
58298                             {
58299                                 'name': 'productitem_id',
58300                                 'type': 'int'
58301                             },
58302                             {
58303                                 'name': 'guid',
58304                                 'type': 'int'
58305                             }
58306                         ]
58307                     }
58308                 },
58309                 toolbar : {
58310                     xtype: 'Toolbar',
58311                     xns: Roo,
58312                     items : [
58313                         {
58314                             xtype: 'Button',
58315                             xns: Roo.Toolbar,
58316                             listeners : {
58317                                 click : function (_self, e)
58318                                 {
58319                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58320                                     sd.setMonth(sd.getMonth()-1);
58321                                     _this.monthField.setValue(sd.format('Y-m-d'));
58322                                     _this.grid.ds.load({});
58323                                 }
58324                             },
58325                             text : "Back"
58326                         },
58327                         {
58328                             xtype: 'Separator',
58329                             xns: Roo.Toolbar
58330                         },
58331                         {
58332                             xtype: 'MonthField',
58333                             xns: Roo.form,
58334                             listeners : {
58335                                 render : function (_self)
58336                                 {
58337                                     _this.monthField = _self;
58338                                    // _this.monthField.set  today
58339                                 },
58340                                 select : function (combo, date)
58341                                 {
58342                                     _this.grid.ds.load({});
58343                                 }
58344                             },
58345                             value : (function() { return new Date(); })()
58346                         },
58347                         {
58348                             xtype: 'Separator',
58349                             xns: Roo.Toolbar
58350                         },
58351                         {
58352                             xtype: 'TextItem',
58353                             xns: Roo.Toolbar,
58354                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
58355                         },
58356                         {
58357                             xtype: 'Fill',
58358                             xns: Roo.Toolbar
58359                         },
58360                         {
58361                             xtype: 'Button',
58362                             xns: Roo.Toolbar,
58363                             listeners : {
58364                                 click : function (_self, e)
58365                                 {
58366                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
58367                                     sd.setMonth(sd.getMonth()+1);
58368                                     _this.monthField.setValue(sd.format('Y-m-d'));
58369                                     _this.grid.ds.load({});
58370                                 }
58371                             },
58372                             text : "Next"
58373                         }
58374                     ]
58375                 },
58376                  
58377             }
58378         };
58379         
58380         *//*
58381  * Based on:
58382  * Ext JS Library 1.1.1
58383  * Copyright(c) 2006-2007, Ext JS, LLC.
58384  *
58385  * Originally Released Under LGPL - original licence link has changed is not relivant.
58386  *
58387  * Fork - LGPL
58388  * <script type="text/javascript">
58389  */
58390  
58391 /**
58392  * @class Roo.LoadMask
58393  * A simple utility class for generically masking elements while loading data.  If the element being masked has
58394  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
58395  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
58396  * element's UpdateManager load indicator and will be destroyed after the initial load.
58397  * @constructor
58398  * Create a new LoadMask
58399  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
58400  * @param {Object} config The config object
58401  */
58402 Roo.LoadMask = function(el, config){
58403     this.el = Roo.get(el);
58404     Roo.apply(this, config);
58405     if(this.store){
58406         this.store.on('beforeload', this.onBeforeLoad, this);
58407         this.store.on('load', this.onLoad, this);
58408         this.store.on('loadexception', this.onLoadException, this);
58409         this.removeMask = false;
58410     }else{
58411         var um = this.el.getUpdateManager();
58412         um.showLoadIndicator = false; // disable the default indicator
58413         um.on('beforeupdate', this.onBeforeLoad, this);
58414         um.on('update', this.onLoad, this);
58415         um.on('failure', this.onLoad, this);
58416         this.removeMask = true;
58417     }
58418 };
58419
58420 Roo.LoadMask.prototype = {
58421     /**
58422      * @cfg {Boolean} removeMask
58423      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58424      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58425      */
58426     /**
58427      * @cfg {String} msg
58428      * The text to display in a centered loading message box (defaults to 'Loading...')
58429      */
58430     msg : 'Loading...',
58431     /**
58432      * @cfg {String} msgCls
58433      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58434      */
58435     msgCls : 'x-mask-loading',
58436
58437     /**
58438      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58439      * @type Boolean
58440      */
58441     disabled: false,
58442
58443     /**
58444      * Disables the mask to prevent it from being displayed
58445      */
58446     disable : function(){
58447        this.disabled = true;
58448     },
58449
58450     /**
58451      * Enables the mask so that it can be displayed
58452      */
58453     enable : function(){
58454         this.disabled = false;
58455     },
58456     
58457     onLoadException : function()
58458     {
58459         Roo.log(arguments);
58460         
58461         if (typeof(arguments[3]) != 'undefined') {
58462             Roo.MessageBox.alert("Error loading",arguments[3]);
58463         } 
58464         /*
58465         try {
58466             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58467                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58468             }   
58469         } catch(e) {
58470             
58471         }
58472         */
58473     
58474         
58475         
58476         this.el.unmask(this.removeMask);
58477     },
58478     // private
58479     onLoad : function()
58480     {
58481         this.el.unmask(this.removeMask);
58482     },
58483
58484     // private
58485     onBeforeLoad : function(){
58486         if(!this.disabled){
58487             this.el.mask(this.msg, this.msgCls);
58488         }
58489     },
58490
58491     // private
58492     destroy : function(){
58493         if(this.store){
58494             this.store.un('beforeload', this.onBeforeLoad, this);
58495             this.store.un('load', this.onLoad, this);
58496             this.store.un('loadexception', this.onLoadException, this);
58497         }else{
58498             var um = this.el.getUpdateManager();
58499             um.un('beforeupdate', this.onBeforeLoad, this);
58500             um.un('update', this.onLoad, this);
58501             um.un('failure', this.onLoad, this);
58502         }
58503     }
58504 };/*
58505  * Based on:
58506  * Ext JS Library 1.1.1
58507  * Copyright(c) 2006-2007, Ext JS, LLC.
58508  *
58509  * Originally Released Under LGPL - original licence link has changed is not relivant.
58510  *
58511  * Fork - LGPL
58512  * <script type="text/javascript">
58513  */
58514
58515
58516 /**
58517  * @class Roo.XTemplate
58518  * @extends Roo.Template
58519  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58520 <pre><code>
58521 var t = new Roo.XTemplate(
58522         '&lt;select name="{name}"&gt;',
58523                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58524         '&lt;/select&gt;'
58525 );
58526  
58527 // then append, applying the master template values
58528  </code></pre>
58529  *
58530  * Supported features:
58531  *
58532  *  Tags:
58533
58534 <pre><code>
58535       {a_variable} - output encoded.
58536       {a_variable.format:("Y-m-d")} - call a method on the variable
58537       {a_variable:raw} - unencoded output
58538       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58539       {a_variable:this.method_on_template(...)} - call a method on the template object.
58540  
58541 </code></pre>
58542  *  The tpl tag:
58543 <pre><code>
58544         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58545         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58546         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58547         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58548   
58549         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58550         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58551 </code></pre>
58552  *      
58553  */
58554 Roo.XTemplate = function()
58555 {
58556     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58557     if (this.html) {
58558         this.compile();
58559     }
58560 };
58561
58562
58563 Roo.extend(Roo.XTemplate, Roo.Template, {
58564
58565     /**
58566      * The various sub templates
58567      */
58568     tpls : false,
58569     /**
58570      *
58571      * basic tag replacing syntax
58572      * WORD:WORD()
58573      *
58574      * // you can fake an object call by doing this
58575      *  x.t:(test,tesT) 
58576      * 
58577      */
58578     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58579
58580     /**
58581      * compile the template
58582      *
58583      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58584      *
58585      */
58586     compile: function()
58587     {
58588         var s = this.html;
58589      
58590         s = ['<tpl>', s, '</tpl>'].join('');
58591     
58592         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58593             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58594             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58595             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58596             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58597             m,
58598             id     = 0,
58599             tpls   = [];
58600     
58601         while(true == !!(m = s.match(re))){
58602             var forMatch   = m[0].match(nameRe),
58603                 ifMatch   = m[0].match(ifRe),
58604                 execMatch   = m[0].match(execRe),
58605                 namedMatch   = m[0].match(namedRe),
58606                 
58607                 exp  = null, 
58608                 fn   = null,
58609                 exec = null,
58610                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58611                 
58612             if (ifMatch) {
58613                 // if - puts fn into test..
58614                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58615                 if(exp){
58616                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58617                 }
58618             }
58619             
58620             if (execMatch) {
58621                 // exec - calls a function... returns empty if true is  returned.
58622                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58623                 if(exp){
58624                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58625                 }
58626             }
58627             
58628             
58629             if (name) {
58630                 // for = 
58631                 switch(name){
58632                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58633                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58634                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58635                 }
58636             }
58637             var uid = namedMatch ? namedMatch[1] : id;
58638             
58639             
58640             tpls.push({
58641                 id:     namedMatch ? namedMatch[1] : id,
58642                 target: name,
58643                 exec:   exec,
58644                 test:   fn,
58645                 body:   m[1] || ''
58646             });
58647             if (namedMatch) {
58648                 s = s.replace(m[0], '');
58649             } else { 
58650                 s = s.replace(m[0], '{xtpl'+ id + '}');
58651             }
58652             ++id;
58653         }
58654         this.tpls = [];
58655         for(var i = tpls.length-1; i >= 0; --i){
58656             this.compileTpl(tpls[i]);
58657             this.tpls[tpls[i].id] = tpls[i];
58658         }
58659         this.master = tpls[tpls.length-1];
58660         return this;
58661     },
58662     /**
58663      * same as applyTemplate, except it's done to one of the subTemplates
58664      * when using named templates, you can do:
58665      *
58666      * var str = pl.applySubTemplate('your-name', values);
58667      *
58668      * 
58669      * @param {Number} id of the template
58670      * @param {Object} values to apply to template
58671      * @param {Object} parent (normaly the instance of this object)
58672      */
58673     applySubTemplate : function(id, values, parent)
58674     {
58675         
58676         
58677         var t = this.tpls[id];
58678         
58679         
58680         try { 
58681             if(t.test && !t.test.call(this, values, parent)){
58682                 return '';
58683             }
58684         } catch(e) {
58685             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58686             Roo.log(e.toString());
58687             Roo.log(t.test);
58688             return ''
58689         }
58690         try { 
58691             
58692             if(t.exec && t.exec.call(this, values, parent)){
58693                 return '';
58694             }
58695         } catch(e) {
58696             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58697             Roo.log(e.toString());
58698             Roo.log(t.exec);
58699             return ''
58700         }
58701         try {
58702             var vs = t.target ? t.target.call(this, values, parent) : values;
58703             parent = t.target ? values : parent;
58704             if(t.target && vs instanceof Array){
58705                 var buf = [];
58706                 for(var i = 0, len = vs.length; i < len; i++){
58707                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58708                 }
58709                 return buf.join('');
58710             }
58711             return t.compiled.call(this, vs, parent);
58712         } catch (e) {
58713             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58714             Roo.log(e.toString());
58715             Roo.log(t.compiled);
58716             return '';
58717         }
58718     },
58719
58720     compileTpl : function(tpl)
58721     {
58722         var fm = Roo.util.Format;
58723         var useF = this.disableFormats !== true;
58724         var sep = Roo.isGecko ? "+" : ",";
58725         var undef = function(str) {
58726             Roo.log("Property not found :"  + str);
58727             return '';
58728         };
58729         
58730         var fn = function(m, name, format, args)
58731         {
58732             //Roo.log(arguments);
58733             args = args ? args.replace(/\\'/g,"'") : args;
58734             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58735             if (typeof(format) == 'undefined') {
58736                 format= 'htmlEncode';
58737             }
58738             if (format == 'raw' ) {
58739                 format = false;
58740             }
58741             
58742             if(name.substr(0, 4) == 'xtpl'){
58743                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58744             }
58745             
58746             // build an array of options to determine if value is undefined..
58747             
58748             // basically get 'xxxx.yyyy' then do
58749             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58750             //    (function () { Roo.log("Property not found"); return ''; })() :
58751             //    ......
58752             
58753             var udef_ar = [];
58754             var lookfor = '';
58755             Roo.each(name.split('.'), function(st) {
58756                 lookfor += (lookfor.length ? '.': '') + st;
58757                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58758             });
58759             
58760             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58761             
58762             
58763             if(format && useF){
58764                 
58765                 args = args ? ',' + args : "";
58766                  
58767                 if(format.substr(0, 5) != "this."){
58768                     format = "fm." + format + '(';
58769                 }else{
58770                     format = 'this.call("'+ format.substr(5) + '", ';
58771                     args = ", values";
58772                 }
58773                 
58774                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58775             }
58776              
58777             if (args.length) {
58778                 // called with xxyx.yuu:(test,test)
58779                 // change to ()
58780                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58781             }
58782             // raw.. - :raw modifier..
58783             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58784             
58785         };
58786         var body;
58787         // branched to use + in gecko and [].join() in others
58788         if(Roo.isGecko){
58789             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58790                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58791                     "';};};";
58792         }else{
58793             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58794             body.push(tpl.body.replace(/(\r\n|\n)/g,
58795                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58796             body.push("'].join('');};};");
58797             body = body.join('');
58798         }
58799         
58800         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58801        
58802         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58803         eval(body);
58804         
58805         return this;
58806     },
58807
58808     applyTemplate : function(values){
58809         return this.master.compiled.call(this, values, {});
58810         //var s = this.subs;
58811     },
58812
58813     apply : function(){
58814         return this.applyTemplate.apply(this, arguments);
58815     }
58816
58817  });
58818
58819 Roo.XTemplate.from = function(el){
58820     el = Roo.getDom(el);
58821     return new Roo.XTemplate(el.value || el.innerHTML);
58822 };