Roo.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         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          *  Clone the object
126          *
127          *
128          */
129         clone : function(obj){
130             if (obj === null || typeof obj !== 'object') {
131                 return obj;
132             }
133
134             var temp = obj.constructor(); // give temp the original obj's constructor
135             for (var key in obj) {
136                 temp[key] = this.clone(obj[key]);
137             }
138
139             return temp;
140         },
141         
142         /**
143          * Copies all the properties of config to obj if they don't already exist.
144          * @param {Object} obj The receiver of the properties
145          * @param {Object} config The source of the properties
146          * @return {Object} returns obj
147          */
148         applyIf : function(o, c){
149             if(o && c){
150                 for(var p in c){
151                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
152                 }
153             }
154             return o;
155         },
156
157         /**
158          * Applies event listeners to elements by selectors when the document is ready.
159          * The event name is specified with an @ suffix.
160 <pre><code>
161 Roo.addBehaviors({
162    // add a listener for click on all anchors in element with id foo
163    '#foo a@click' : function(e, t){
164        // do something
165    },
166
167    // add the same listener to multiple selectors (separated by comma BEFORE the @)
168    '#foo a, #bar span.some-class@mouseover' : function(){
169        // do something
170    }
171 });
172 </code></pre>
173          * @param {Object} obj The list of behaviors to apply
174          */
175         addBehaviors : function(o){
176             if(!Roo.isReady){
177                 Roo.onReady(function(){
178                     Roo.addBehaviors(o);
179                 });
180                 return;
181             }
182             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
183             for(var b in o){
184                 var parts = b.split('@');
185                 if(parts[1]){ // for Object prototype breakers
186                     var s = parts[0];
187                     if(!cache[s]){
188                         cache[s] = Roo.select(s);
189                     }
190                     cache[s].on(parts[1], o[b]);
191                 }
192             }
193             cache = null;
194         },
195
196         /**
197          * Generates unique ids. If the element already has an id, it is unchanged
198          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
199          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
200          * @return {String} The generated Id.
201          */
202         id : function(el, prefix){
203             prefix = prefix || "roo-gen";
204             el = Roo.getDom(el);
205             var id = prefix + (++idSeed);
206             return el ? (el.id ? el.id : (el.id = id)) : id;
207         },
208          
209        
210         /**
211          * Extends one class with another class and optionally overrides members with the passed literal. This class
212          * also adds the function "override()" to the class that can be used to override
213          * members on an instance.
214          * @param {Object} subclass The class inheriting the functionality
215          * @param {Object} superclass The class being extended
216          * @param {Object} overrides (optional) A literal with members
217          * @method extend
218          */
219         extend : function(){
220             // inline overrides
221             var io = function(o){
222                 for(var m in o){
223                     this[m] = o[m];
224                 }
225             };
226             return function(sb, sp, overrides){
227                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
228                     overrides = sp;
229                     sp = sb;
230                     sb = function(){sp.apply(this, arguments);};
231                 }
232                 var F = function(){}, sbp, spp = sp.prototype;
233                 F.prototype = spp;
234                 sbp = sb.prototype = new F();
235                 sbp.constructor=sb;
236                 sb.superclass=spp;
237                 
238                 if(spp.constructor == Object.prototype.constructor){
239                     spp.constructor=sp;
240                    
241                 }
242                 
243                 sb.override = function(o){
244                     Roo.override(sb, o);
245                 };
246                 sbp.override = io;
247                 Roo.override(sb, overrides);
248                 return sb;
249             };
250         }(),
251
252         /**
253          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
254          * Usage:<pre><code>
255 Roo.override(MyClass, {
256     newMethod1: function(){
257         // etc.
258     },
259     newMethod2: function(foo){
260         // etc.
261     }
262 });
263  </code></pre>
264          * @param {Object} origclass The class to override
265          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
266          * containing one or more methods.
267          * @method override
268          */
269         override : function(origclass, overrides){
270             if(overrides){
271                 var p = origclass.prototype;
272                 for(var method in overrides){
273                     p[method] = overrides[method];
274                 }
275             }
276         },
277         /**
278          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
279          * <pre><code>
280 Roo.namespace('Company', 'Company.data');
281 Company.Widget = function() { ... }
282 Company.data.CustomStore = function(config) { ... }
283 </code></pre>
284          * @param {String} namespace1
285          * @param {String} namespace2
286          * @param {String} etc
287          * @method namespace
288          */
289         namespace : function(){
290             var a=arguments, o=null, i, j, d, rt;
291             for (i=0; i<a.length; ++i) {
292                 d=a[i].split(".");
293                 rt = d[0];
294                 /** eval:var:o */
295                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
296                 for (j=1; j<d.length; ++j) {
297                     o[d[j]]=o[d[j]] || {};
298                     o=o[d[j]];
299                 }
300             }
301         },
302         /**
303          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
304          * <pre><code>
305 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
306 Roo.factory(conf, Roo.data);
307 </code></pre>
308          * @param {String} classname
309          * @param {String} namespace (optional)
310          * @method factory
311          */
312          
313         factory : function(c, ns)
314         {
315             // no xtype, no ns or c.xns - or forced off by c.xns
316             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
317                 return c;
318             }
319             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
320             if (c.constructor == ns[c.xtype]) {// already created...
321                 return c;
322             }
323             if (ns[c.xtype]) {
324                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
325                 var ret = new ns[c.xtype](c);
326                 ret.xns = false;
327                 return ret;
328             }
329             c.xns = false; // prevent recursion..
330             return c;
331         },
332          /**
333          * Logs to console if it can.
334          *
335          * @param {String|Object} string
336          * @method log
337          */
338         log : function(s)
339         {
340             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
341                 return; // alerT?
342             }
343             console.log(s);
344             
345         },
346         /**
347          * 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.
348          * @param {Object} o
349          * @return {String}
350          */
351         urlEncode : function(o){
352             if(!o){
353                 return "";
354             }
355             var buf = [];
356             for(var key in o){
357                 var ov = o[key], k = Roo.encodeURIComponent(key);
358                 var type = typeof ov;
359                 if(type == 'undefined'){
360                     buf.push(k, "=&");
361                 }else if(type != "function" && type != "object"){
362                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
363                 }else if(ov instanceof Array){
364                     if (ov.length) {
365                             for(var i = 0, len = ov.length; i < len; i++) {
366                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
367                             }
368                         } else {
369                             buf.push(k, "=&");
370                         }
371                 }
372             }
373             buf.pop();
374             return buf.join("");
375         },
376          /**
377          * Safe version of encodeURIComponent
378          * @param {String} data 
379          * @return {String} 
380          */
381         
382         encodeURIComponent : function (data)
383         {
384             try {
385                 return encodeURIComponent(data);
386             } catch(e) {} // should be an uri encode error.
387             
388             if (data == '' || data == null){
389                return '';
390             }
391             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
392             function nibble_to_hex(nibble){
393                 var chars = '0123456789ABCDEF';
394                 return chars.charAt(nibble);
395             }
396             data = data.toString();
397             var buffer = '';
398             for(var i=0; i<data.length; i++){
399                 var c = data.charCodeAt(i);
400                 var bs = new Array();
401                 if (c > 0x10000){
402                         // 4 bytes
403                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
404                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
405                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
406                     bs[3] = 0x80 | (c & 0x3F);
407                 }else if (c > 0x800){
408                          // 3 bytes
409                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
410                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
411                     bs[2] = 0x80 | (c & 0x3F);
412                 }else if (c > 0x80){
413                        // 2 bytes
414                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
415                     bs[1] = 0x80 | (c & 0x3F);
416                 }else{
417                         // 1 byte
418                     bs[0] = c;
419                 }
420                 for(var j=0; j<bs.length; j++){
421                     var b = bs[j];
422                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
423                             + nibble_to_hex(b &0x0F);
424                     buffer += '%'+hex;
425                }
426             }
427             return buffer;    
428              
429         },
430
431         /**
432          * 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]}.
433          * @param {String} string
434          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
435          * @return {Object} A literal with members
436          */
437         urlDecode : function(string, overwrite){
438             if(!string || !string.length){
439                 return {};
440             }
441             var obj = {};
442             var pairs = string.split('&');
443             var pair, name, value;
444             for(var i = 0, len = pairs.length; i < len; i++){
445                 pair = pairs[i].split('=');
446                 name = decodeURIComponent(pair[0]);
447                 value = decodeURIComponent(pair[1]);
448                 if(overwrite !== true){
449                     if(typeof obj[name] == "undefined"){
450                         obj[name] = value;
451                     }else if(typeof obj[name] == "string"){
452                         obj[name] = [obj[name]];
453                         obj[name].push(value);
454                     }else{
455                         obj[name].push(value);
456                     }
457                 }else{
458                     obj[name] = value;
459                 }
460             }
461             return obj;
462         },
463
464         /**
465          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
466          * passed array is not really an array, your function is called once with it.
467          * The supplied function is called with (Object item, Number index, Array allItems).
468          * @param {Array/NodeList/Mixed} array
469          * @param {Function} fn
470          * @param {Object} scope
471          */
472         each : function(array, fn, scope){
473             if(typeof array.length == "undefined" || typeof array == "string"){
474                 array = [array];
475             }
476             for(var i = 0, len = array.length; i < len; i++){
477                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
478             }
479         },
480
481         // deprecated
482         combine : function(){
483             var as = arguments, l = as.length, r = [];
484             for(var i = 0; i < l; i++){
485                 var a = as[i];
486                 if(a instanceof Array){
487                     r = r.concat(a);
488                 }else if(a.length !== undefined && !a.substr){
489                     r = r.concat(Array.prototype.slice.call(a, 0));
490                 }else{
491                     r.push(a);
492                 }
493             }
494             return r;
495         },
496
497         /**
498          * Escapes the passed string for use in a regular expression
499          * @param {String} str
500          * @return {String}
501          */
502         escapeRe : function(s) {
503             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
504         },
505
506         // internal
507         callback : function(cb, scope, args, delay){
508             if(typeof cb == "function"){
509                 if(delay){
510                     cb.defer(delay, scope, args || []);
511                 }else{
512                     cb.apply(scope, args || []);
513                 }
514             }
515         },
516
517         /**
518          * Return the dom node for the passed string (id), dom node, or Roo.Element
519          * @param {String/HTMLElement/Roo.Element} el
520          * @return HTMLElement
521          */
522         getDom : function(el){
523             if(!el){
524                 return null;
525             }
526             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
527         },
528
529         /**
530         * Shorthand for {@link Roo.ComponentMgr#get}
531         * @param {String} id
532         * @return Roo.Component
533         */
534         getCmp : function(id){
535             return Roo.ComponentMgr.get(id);
536         },
537          
538         num : function(v, defaultValue){
539             if(typeof v != 'number'){
540                 return defaultValue;
541             }
542             return v;
543         },
544
545         destroy : function(){
546             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
547                 var as = a[i];
548                 if(as){
549                     if(as.dom){
550                         as.removeAllListeners();
551                         as.remove();
552                         continue;
553                     }
554                     if(typeof as.purgeListeners == 'function'){
555                         as.purgeListeners();
556                     }
557                     if(typeof as.destroy == 'function'){
558                         as.destroy();
559                     }
560                 }
561             }
562         },
563
564         // inpired by a similar function in mootools library
565         /**
566          * Returns the type of object that is passed in. If the object passed in is null or undefined it
567          * return false otherwise it returns one of the following values:<ul>
568          * <li><b>string</b>: If the object passed is a string</li>
569          * <li><b>number</b>: If the object passed is a number</li>
570          * <li><b>boolean</b>: If the object passed is a boolean value</li>
571          * <li><b>function</b>: If the object passed is a function reference</li>
572          * <li><b>object</b>: If the object passed is an object</li>
573          * <li><b>array</b>: If the object passed is an array</li>
574          * <li><b>regexp</b>: If the object passed is a regular expression</li>
575          * <li><b>element</b>: If the object passed is a DOM Element</li>
576          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
577          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
578          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
579          * @param {Mixed} object
580          * @return {String}
581          */
582         type : function(o){
583             if(o === undefined || o === null){
584                 return false;
585             }
586             if(o.htmlElement){
587                 return 'element';
588             }
589             var t = typeof o;
590             if(t == 'object' && o.nodeName) {
591                 switch(o.nodeType) {
592                     case 1: return 'element';
593                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
594                 }
595             }
596             if(t == 'object' || t == 'function') {
597                 switch(o.constructor) {
598                     case Array: return 'array';
599                     case RegExp: return 'regexp';
600                 }
601                 if(typeof o.length == 'number' && typeof o.item == 'function') {
602                     return 'nodelist';
603                 }
604             }
605             return t;
606         },
607
608         /**
609          * Returns true if the passed value is null, undefined or an empty string (optional).
610          * @param {Mixed} value The value to test
611          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
612          * @return {Boolean}
613          */
614         isEmpty : function(v, allowBlank){
615             return v === null || v === undefined || (!allowBlank ? v === '' : false);
616         },
617         
618         /** @type Boolean */
619         isOpera : isOpera,
620         /** @type Boolean */
621         isSafari : isSafari,
622         /** @type Boolean */
623         isIE : isIE,
624         /** @type Boolean */
625         isIE7 : isIE7,
626         /** @type Boolean */
627         isGecko : isGecko,
628         /** @type Boolean */
629         isBorderBox : isBorderBox,
630         /** @type Boolean */
631         isWindows : isWindows,
632         /** @type Boolean */
633         isLinux : isLinux,
634         /** @type Boolean */
635         isMac : isMac,
636
637         /**
638          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
639          * you may want to set this to true.
640          * @type Boolean
641          */
642         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
643         
644         
645                 
646         /**
647          * Selects a single element as a Roo Element
648          * This is about as close as you can get to jQuery's $('do crazy stuff')
649          * @param {String} selector The selector/xpath query
650          * @param {Node} root (optional) The start of the query (defaults to document).
651          * @return {Roo.Element}
652          */
653         selectNode : function(selector, root) 
654         {
655             var node = Roo.DomQuery.selectNode(selector,root);
656             return node ? Roo.get(node) : new Roo.Element(false);
657         }
658         
659     });
660
661
662 })();
663
664 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
665                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
666 /*
667  * Based on:
668  * Ext JS Library 1.1.1
669  * Copyright(c) 2006-2007, Ext JS, LLC.
670  *
671  * Originally Released Under LGPL - original licence link has changed is not relivant.
672  *
673  * Fork - LGPL
674  * <script type="text/javascript">
675  */
676
677 (function() {    
678     // wrappedn so fnCleanup is not in global scope...
679     if(Roo.isIE) {
680         function fnCleanUp() {
681             var p = Function.prototype;
682             delete p.createSequence;
683             delete p.defer;
684             delete p.createDelegate;
685             delete p.createCallback;
686             delete p.createInterceptor;
687
688             window.detachEvent("onunload", fnCleanUp);
689         }
690         window.attachEvent("onunload", fnCleanUp);
691     }
692 })();
693
694
695 /**
696  * @class Function
697  * These functions are available on every Function object (any JavaScript function).
698  */
699 Roo.apply(Function.prototype, {
700      /**
701      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
702      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
703      * Will create a function that is bound to those 2 args.
704      * @return {Function} The new function
705     */
706     createCallback : function(/*args...*/){
707         // make args available, in function below
708         var args = arguments;
709         var method = this;
710         return function() {
711             return method.apply(window, args);
712         };
713     },
714
715     /**
716      * Creates a delegate (callback) that sets the scope to obj.
717      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
718      * Will create a function that is automatically scoped to this.
719      * @param {Object} obj (optional) The object for which the scope is set
720      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
721      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
722      *                                             if a number the args are inserted at the specified position
723      * @return {Function} The new function
724      */
725     createDelegate : function(obj, args, appendArgs){
726         var method = this;
727         return function() {
728             var callArgs = args || arguments;
729             if(appendArgs === true){
730                 callArgs = Array.prototype.slice.call(arguments, 0);
731                 callArgs = callArgs.concat(args);
732             }else if(typeof appendArgs == "number"){
733                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
734                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
735                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
736             }
737             return method.apply(obj || window, callArgs);
738         };
739     },
740
741     /**
742      * Calls this function after the number of millseconds specified.
743      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
744      * @param {Object} obj (optional) The object for which the scope is set
745      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
746      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
747      *                                             if a number the args are inserted at the specified position
748      * @return {Number} The timeout id that can be used with clearTimeout
749      */
750     defer : function(millis, obj, args, appendArgs){
751         var fn = this.createDelegate(obj, args, appendArgs);
752         if(millis){
753             return setTimeout(fn, millis);
754         }
755         fn();
756         return 0;
757     },
758     /**
759      * Create a combined function call sequence of the original function + the passed function.
760      * The resulting function returns the results of the original function.
761      * The passed fcn is called with the parameters of the original function
762      * @param {Function} fcn The function to sequence
763      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
764      * @return {Function} The new function
765      */
766     createSequence : function(fcn, scope){
767         if(typeof fcn != "function"){
768             return this;
769         }
770         var method = this;
771         return function() {
772             var retval = method.apply(this || window, arguments);
773             fcn.apply(scope || this || window, arguments);
774             return retval;
775         };
776     },
777
778     /**
779      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
780      * The resulting function returns the results of the original function.
781      * The passed fcn is called with the parameters of the original function.
782      * @addon
783      * @param {Function} fcn The function to call before the original
784      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
785      * @return {Function} The new function
786      */
787     createInterceptor : function(fcn, scope){
788         if(typeof fcn != "function"){
789             return this;
790         }
791         var method = this;
792         return function() {
793             fcn.target = this;
794             fcn.method = method;
795             if(fcn.apply(scope || this || window, arguments) === false){
796                 return;
797             }
798             return method.apply(this || window, arguments);
799         };
800     }
801 });
802 /*
803  * Based on:
804  * Ext JS Library 1.1.1
805  * Copyright(c) 2006-2007, Ext JS, LLC.
806  *
807  * Originally Released Under LGPL - original licence link has changed is not relivant.
808  *
809  * Fork - LGPL
810  * <script type="text/javascript">
811  */
812
813 Roo.applyIf(String, {
814     
815     /** @scope String */
816     
817     /**
818      * Escapes the passed string for ' and \
819      * @param {String} string The string to escape
820      * @return {String} The escaped string
821      * @static
822      */
823     escape : function(string) {
824         return string.replace(/('|\\)/g, "\\$1");
825     },
826
827     /**
828      * Pads the left side of a string with a specified character.  This is especially useful
829      * for normalizing number and date strings.  Example usage:
830      * <pre><code>
831 var s = String.leftPad('123', 5, '0');
832 // s now contains the string: '00123'
833 </code></pre>
834      * @param {String} string The original string
835      * @param {Number} size The total length of the output string
836      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
837      * @return {String} The padded string
838      * @static
839      */
840     leftPad : function (val, size, ch) {
841         var result = new String(val);
842         if(ch === null || ch === undefined || ch === '') {
843             ch = " ";
844         }
845         while (result.length < size) {
846             result = ch + result;
847         }
848         return result;
849     },
850
851     /**
852      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
853      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
854      * <pre><code>
855 var cls = 'my-class', text = 'Some text';
856 var s = String.format('<div class="{0}">{1}</div>', cls, text);
857 // s now contains the string: '<div class="my-class">Some text</div>'
858 </code></pre>
859      * @param {String} string The tokenized string to be formatted
860      * @param {String} value1 The value to replace token {0}
861      * @param {String} value2 Etc...
862      * @return {String} The formatted string
863      * @static
864      */
865     format : function(format){
866         var args = Array.prototype.slice.call(arguments, 1);
867         return format.replace(/\{(\d+)\}/g, function(m, i){
868             return Roo.util.Format.htmlEncode(args[i]);
869         });
870     }
871 });
872
873 /**
874  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
875  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
876  * they are already different, the first value passed in is returned.  Note that this method returns the new value
877  * but does not change the current string.
878  * <pre><code>
879 // alternate sort directions
880 sort = sort.toggle('ASC', 'DESC');
881
882 // instead of conditional logic:
883 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
884 </code></pre>
885  * @param {String} value The value to compare to the current string
886  * @param {String} other The new value to use if the string already equals the first value passed in
887  * @return {String} The new value
888  */
889  
890 String.prototype.toggle = function(value, other){
891     return this == value ? other : value;
892 };/*
893  * Based on:
894  * Ext JS Library 1.1.1
895  * Copyright(c) 2006-2007, Ext JS, LLC.
896  *
897  * Originally Released Under LGPL - original licence link has changed is not relivant.
898  *
899  * Fork - LGPL
900  * <script type="text/javascript">
901  */
902
903  /**
904  * @class Number
905  */
906 Roo.applyIf(Number.prototype, {
907     /**
908      * Checks whether or not the current number is within a desired range.  If the number is already within the
909      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
910      * exceeded.  Note that this method returns the constrained value but does not change the current number.
911      * @param {Number} min The minimum number in the range
912      * @param {Number} max The maximum number in the range
913      * @return {Number} The constrained value if outside the range, otherwise the current value
914      */
915     constrain : function(min, max){
916         return Math.min(Math.max(this, min), max);
917     }
918 });/*
919  * Based on:
920  * Ext JS Library 1.1.1
921  * Copyright(c) 2006-2007, Ext JS, LLC.
922  *
923  * Originally Released Under LGPL - original licence link has changed is not relivant.
924  *
925  * Fork - LGPL
926  * <script type="text/javascript">
927  */
928  /**
929  * @class Array
930  */
931 Roo.applyIf(Array.prototype, {
932     /**
933      * Checks whether or not the specified object exists in the array.
934      * @param {Object} o The object to check for
935      * @return {Number} The index of o in the array (or -1 if it is not found)
936      */
937     indexOf : function(o){
938        for (var i = 0, len = this.length; i < len; i++){
939               if(this[i] == o) return i;
940        }
941            return -1;
942     },
943
944     /**
945      * Removes the specified object from the array.  If the object is not found nothing happens.
946      * @param {Object} o The object to remove
947      */
948     remove : function(o){
949        var index = this.indexOf(o);
950        if(index != -1){
951            this.splice(index, 1);
952        }
953     },
954     /**
955      * Map (JS 1.6 compatibility)
956      * @param {Function} function  to call
957      */
958     map : function(fun )
959     {
960         var len = this.length >>> 0;
961         if (typeof fun != "function")
962             throw new TypeError();
963
964         var res = new Array(len);
965         var thisp = arguments[1];
966         for (var i = 0; i < len; i++)
967         {
968             if (i in this)
969                 res[i] = fun.call(thisp, this[i], i, this);
970         }
971
972         return res;
973     }
974     
975 });
976
977
978  /*
979  * Based on:
980  * Ext JS Library 1.1.1
981  * Copyright(c) 2006-2007, Ext JS, LLC.
982  *
983  * Originally Released Under LGPL - original licence link has changed is not relivant.
984  *
985  * Fork - LGPL
986  * <script type="text/javascript">
987  */
988
989 /**
990  * @class Date
991  *
992  * The date parsing and format syntax is a subset of
993  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
994  * supported will provide results equivalent to their PHP versions.
995  *
996  * Following is the list of all currently supported formats:
997  *<pre>
998 Sample date:
999 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1000
1001 Format  Output      Description
1002 ------  ----------  --------------------------------------------------------------
1003   d      10         Day of the month, 2 digits with leading zeros
1004   D      Wed        A textual representation of a day, three letters
1005   j      10         Day of the month without leading zeros
1006   l      Wednesday  A full textual representation of the day of the week
1007   S      th         English ordinal day of month suffix, 2 chars (use with j)
1008   w      3          Numeric representation of the day of the week
1009   z      9          The julian date, or day of the year (0-365)
1010   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1011   F      January    A full textual representation of the month
1012   m      01         Numeric representation of a month, with leading zeros
1013   M      Jan        Month name abbreviation, three letters
1014   n      1          Numeric representation of a month, without leading zeros
1015   t      31         Number of days in the given month
1016   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1017   Y      2007       A full numeric representation of a year, 4 digits
1018   y      07         A two digit representation of a year
1019   a      pm         Lowercase Ante meridiem and Post meridiem
1020   A      PM         Uppercase Ante meridiem and Post meridiem
1021   g      3          12-hour format of an hour without leading zeros
1022   G      15         24-hour format of an hour without leading zeros
1023   h      03         12-hour format of an hour with leading zeros
1024   H      15         24-hour format of an hour with leading zeros
1025   i      05         Minutes with leading zeros
1026   s      01         Seconds, with leading zeros
1027   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1028   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1029   T      CST        Timezone setting of the machine running the code
1030   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1031 </pre>
1032  *
1033  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1034  * <pre><code>
1035 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1036 document.write(dt.format('Y-m-d'));                         //2007-01-10
1037 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1038 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
1039  </code></pre>
1040  *
1041  * Here are some standard date/time patterns that you might find helpful.  They
1042  * are not part of the source of Date.js, but to use them you can simply copy this
1043  * block of code into any script that is included after Date.js and they will also become
1044  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1045  * <pre><code>
1046 Date.patterns = {
1047     ISO8601Long:"Y-m-d H:i:s",
1048     ISO8601Short:"Y-m-d",
1049     ShortDate: "n/j/Y",
1050     LongDate: "l, F d, Y",
1051     FullDateTime: "l, F d, Y g:i:s A",
1052     MonthDay: "F d",
1053     ShortTime: "g:i A",
1054     LongTime: "g:i:s A",
1055     SortableDateTime: "Y-m-d\\TH:i:s",
1056     UniversalSortableDateTime: "Y-m-d H:i:sO",
1057     YearMonth: "F, Y"
1058 };
1059 </code></pre>
1060  *
1061  * Example usage:
1062  * <pre><code>
1063 var dt = new Date();
1064 document.write(dt.format(Date.patterns.ShortDate));
1065  </code></pre>
1066  */
1067
1068 /*
1069  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1070  * They generate precompiled functions from date formats instead of parsing and
1071  * processing the pattern every time you format a date.  These functions are available
1072  * on every Date object (any javascript function).
1073  *
1074  * The original article and download are here:
1075  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1076  *
1077  */
1078  
1079  
1080  // was in core
1081 /**
1082  Returns the number of milliseconds between this date and date
1083  @param {Date} date (optional) Defaults to now
1084  @return {Number} The diff in milliseconds
1085  @member Date getElapsed
1086  */
1087 Date.prototype.getElapsed = function(date) {
1088         return Math.abs((date || new Date()).getTime()-this.getTime());
1089 };
1090 // was in date file..
1091
1092
1093 // private
1094 Date.parseFunctions = {count:0};
1095 // private
1096 Date.parseRegexes = [];
1097 // private
1098 Date.formatFunctions = {count:0};
1099
1100 // private
1101 Date.prototype.dateFormat = function(format) {
1102     if (Date.formatFunctions[format] == null) {
1103         Date.createNewFormat(format);
1104     }
1105     var func = Date.formatFunctions[format];
1106     return this[func]();
1107 };
1108
1109
1110 /**
1111  * Formats a date given the supplied format string
1112  * @param {String} format The format string
1113  * @return {String} The formatted date
1114  * @method
1115  */
1116 Date.prototype.format = Date.prototype.dateFormat;
1117
1118 // private
1119 Date.createNewFormat = function(format) {
1120     var funcName = "format" + Date.formatFunctions.count++;
1121     Date.formatFunctions[format] = funcName;
1122     var code = "Date.prototype." + funcName + " = function(){return ";
1123     var special = false;
1124     var ch = '';
1125     for (var i = 0; i < format.length; ++i) {
1126         ch = format.charAt(i);
1127         if (!special && ch == "\\") {
1128             special = true;
1129         }
1130         else if (special) {
1131             special = false;
1132             code += "'" + String.escape(ch) + "' + ";
1133         }
1134         else {
1135             code += Date.getFormatCode(ch);
1136         }
1137     }
1138     /** eval:var:zzzzzzzzzzzzz */
1139     eval(code.substring(0, code.length - 3) + ";}");
1140 };
1141
1142 // private
1143 Date.getFormatCode = function(character) {
1144     switch (character) {
1145     case "d":
1146         return "String.leftPad(this.getDate(), 2, '0') + ";
1147     case "D":
1148         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1149     case "j":
1150         return "this.getDate() + ";
1151     case "l":
1152         return "Date.dayNames[this.getDay()] + ";
1153     case "S":
1154         return "this.getSuffix() + ";
1155     case "w":
1156         return "this.getDay() + ";
1157     case "z":
1158         return "this.getDayOfYear() + ";
1159     case "W":
1160         return "this.getWeekOfYear() + ";
1161     case "F":
1162         return "Date.monthNames[this.getMonth()] + ";
1163     case "m":
1164         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1165     case "M":
1166         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1167     case "n":
1168         return "(this.getMonth() + 1) + ";
1169     case "t":
1170         return "this.getDaysInMonth() + ";
1171     case "L":
1172         return "(this.isLeapYear() ? 1 : 0) + ";
1173     case "Y":
1174         return "this.getFullYear() + ";
1175     case "y":
1176         return "('' + this.getFullYear()).substring(2, 4) + ";
1177     case "a":
1178         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1179     case "A":
1180         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1181     case "g":
1182         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1183     case "G":
1184         return "this.getHours() + ";
1185     case "h":
1186         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1187     case "H":
1188         return "String.leftPad(this.getHours(), 2, '0') + ";
1189     case "i":
1190         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1191     case "s":
1192         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1193     case "O":
1194         return "this.getGMTOffset() + ";
1195     case "P":
1196         return "this.getGMTColonOffset() + ";
1197     case "T":
1198         return "this.getTimezone() + ";
1199     case "Z":
1200         return "(this.getTimezoneOffset() * -60) + ";
1201     default:
1202         return "'" + String.escape(character) + "' + ";
1203     }
1204 };
1205
1206 /**
1207  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1208  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1209  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1210  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1211  * string or the parse operation will fail.
1212  * Example Usage:
1213 <pre><code>
1214 //dt = Fri May 25 2007 (current date)
1215 var dt = new Date();
1216
1217 //dt = Thu May 25 2006 (today's month/day in 2006)
1218 dt = Date.parseDate("2006", "Y");
1219
1220 //dt = Sun Jan 15 2006 (all date parts specified)
1221 dt = Date.parseDate("2006-1-15", "Y-m-d");
1222
1223 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1224 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1225 </code></pre>
1226  * @param {String} input The unparsed date as a string
1227  * @param {String} format The format the date is in
1228  * @return {Date} The parsed date
1229  * @static
1230  */
1231 Date.parseDate = function(input, format) {
1232     if (Date.parseFunctions[format] == null) {
1233         Date.createParser(format);
1234     }
1235     var func = Date.parseFunctions[format];
1236     return Date[func](input);
1237 };
1238 /**
1239  * @private
1240  */
1241 Date.createParser = function(format) {
1242     var funcName = "parse" + Date.parseFunctions.count++;
1243     var regexNum = Date.parseRegexes.length;
1244     var currentGroup = 1;
1245     Date.parseFunctions[format] = funcName;
1246
1247     var code = "Date." + funcName + " = function(input){\n"
1248         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1249         + "var d = new Date();\n"
1250         + "y = d.getFullYear();\n"
1251         + "m = d.getMonth();\n"
1252         + "d = d.getDate();\n"
1253         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1254         + "if (results && results.length > 0) {";
1255     var regex = "";
1256
1257     var special = false;
1258     var ch = '';
1259     for (var i = 0; i < format.length; ++i) {
1260         ch = format.charAt(i);
1261         if (!special && ch == "\\") {
1262             special = true;
1263         }
1264         else if (special) {
1265             special = false;
1266             regex += String.escape(ch);
1267         }
1268         else {
1269             var obj = Date.formatCodeToRegex(ch, currentGroup);
1270             currentGroup += obj.g;
1271             regex += obj.s;
1272             if (obj.g && obj.c) {
1273                 code += obj.c;
1274             }
1275         }
1276     }
1277
1278     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1279         + "{v = new Date(y, m, d, h, i, s);}\n"
1280         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1281         + "{v = new Date(y, m, d, h, i);}\n"
1282         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1283         + "{v = new Date(y, m, d, h);}\n"
1284         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1285         + "{v = new Date(y, m, d);}\n"
1286         + "else if (y >= 0 && m >= 0)\n"
1287         + "{v = new Date(y, m);}\n"
1288         + "else if (y >= 0)\n"
1289         + "{v = new Date(y);}\n"
1290         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1291         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1292         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1293         + ";}";
1294
1295     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1296     /** eval:var:zzzzzzzzzzzzz */
1297     eval(code);
1298 };
1299
1300 // private
1301 Date.formatCodeToRegex = function(character, currentGroup) {
1302     switch (character) {
1303     case "D":
1304         return {g:0,
1305         c:null,
1306         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1307     case "j":
1308         return {g:1,
1309             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1310             s:"(\\d{1,2})"}; // day of month without leading zeroes
1311     case "d":
1312         return {g:1,
1313             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1314             s:"(\\d{2})"}; // day of month with leading zeroes
1315     case "l":
1316         return {g:0,
1317             c:null,
1318             s:"(?:" + Date.dayNames.join("|") + ")"};
1319     case "S":
1320         return {g:0,
1321             c:null,
1322             s:"(?:st|nd|rd|th)"};
1323     case "w":
1324         return {g:0,
1325             c:null,
1326             s:"\\d"};
1327     case "z":
1328         return {g:0,
1329             c:null,
1330             s:"(?:\\d{1,3})"};
1331     case "W":
1332         return {g:0,
1333             c:null,
1334             s:"(?:\\d{2})"};
1335     case "F":
1336         return {g:1,
1337             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1338             s:"(" + Date.monthNames.join("|") + ")"};
1339     case "M":
1340         return {g:1,
1341             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1342             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1343     case "n":
1344         return {g:1,
1345             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1346             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1347     case "m":
1348         return {g:1,
1349             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1350             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1351     case "t":
1352         return {g:0,
1353             c:null,
1354             s:"\\d{1,2}"};
1355     case "L":
1356         return {g:0,
1357             c:null,
1358             s:"(?:1|0)"};
1359     case "Y":
1360         return {g:1,
1361             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1362             s:"(\\d{4})"};
1363     case "y":
1364         return {g:1,
1365             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1366                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1367             s:"(\\d{1,2})"};
1368     case "a":
1369         return {g:1,
1370             c:"if (results[" + currentGroup + "] == 'am') {\n"
1371                 + "if (h == 12) { h = 0; }\n"
1372                 + "} else { if (h < 12) { h += 12; }}",
1373             s:"(am|pm)"};
1374     case "A":
1375         return {g:1,
1376             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1377                 + "if (h == 12) { h = 0; }\n"
1378                 + "} else { if (h < 12) { h += 12; }}",
1379             s:"(AM|PM)"};
1380     case "g":
1381     case "G":
1382         return {g:1,
1383             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1384             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1385     case "h":
1386     case "H":
1387         return {g:1,
1388             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1389             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1390     case "i":
1391         return {g:1,
1392             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1393             s:"(\\d{2})"};
1394     case "s":
1395         return {g:1,
1396             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1397             s:"(\\d{2})"};
1398     case "O":
1399         return {g:1,
1400             c:[
1401                 "o = results[", currentGroup, "];\n",
1402                 "var sn = o.substring(0,1);\n", // get + / - sign
1403                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1404                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1405                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1406                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1407             ].join(""),
1408             s:"([+\-]\\d{2,4})"};
1409     
1410     
1411     case "P":
1412         return {g:1,
1413                 c:[
1414                    "o = results[", currentGroup, "];\n",
1415                    "var sn = o.substring(0,1);\n",
1416                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1417                    "var mn = o.substring(4,6) % 60;\n",
1418                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1419                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1420             ].join(""),
1421             s:"([+\-]\\d{4})"};
1422     case "T":
1423         return {g:0,
1424             c:null,
1425             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1426     case "Z":
1427         return {g:1,
1428             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1429                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1430             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1431     default:
1432         return {g:0,
1433             c:null,
1434             s:String.escape(character)};
1435     }
1436 };
1437
1438 /**
1439  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1440  * @return {String} The abbreviated timezone name (e.g. 'CST')
1441  */
1442 Date.prototype.getTimezone = function() {
1443     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1444 };
1445
1446 /**
1447  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1448  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1449  */
1450 Date.prototype.getGMTOffset = function() {
1451     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1452         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1453         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1454 };
1455
1456 /**
1457  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1458  * @return {String} 2-characters representing hours and 2-characters representing minutes
1459  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1460  */
1461 Date.prototype.getGMTColonOffset = function() {
1462         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1463                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1464                 + ":"
1465                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1466 }
1467
1468 /**
1469  * Get the numeric day number of the year, adjusted for leap year.
1470  * @return {Number} 0 through 364 (365 in leap years)
1471  */
1472 Date.prototype.getDayOfYear = function() {
1473     var num = 0;
1474     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1475     for (var i = 0; i < this.getMonth(); ++i) {
1476         num += Date.daysInMonth[i];
1477     }
1478     return num + this.getDate() - 1;
1479 };
1480
1481 /**
1482  * Get the string representation of the numeric week number of the year
1483  * (equivalent to the format specifier 'W').
1484  * @return {String} '00' through '52'
1485  */
1486 Date.prototype.getWeekOfYear = function() {
1487     // Skip to Thursday of this week
1488     var now = this.getDayOfYear() + (4 - this.getDay());
1489     // Find the first Thursday of the year
1490     var jan1 = new Date(this.getFullYear(), 0, 1);
1491     var then = (7 - jan1.getDay() + 4);
1492     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1493 };
1494
1495 /**
1496  * Whether or not the current date is in a leap year.
1497  * @return {Boolean} True if the current date is in a leap year, else false
1498  */
1499 Date.prototype.isLeapYear = function() {
1500     var year = this.getFullYear();
1501     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1502 };
1503
1504 /**
1505  * Get the first day of the current month, adjusted for leap year.  The returned value
1506  * is the numeric day index within the week (0-6) which can be used in conjunction with
1507  * the {@link #monthNames} array to retrieve the textual day name.
1508  * Example:
1509  *<pre><code>
1510 var dt = new Date('1/10/2007');
1511 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1512 </code></pre>
1513  * @return {Number} The day number (0-6)
1514  */
1515 Date.prototype.getFirstDayOfMonth = function() {
1516     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1517     return (day < 0) ? (day + 7) : day;
1518 };
1519
1520 /**
1521  * Get the last day of the current month, adjusted for leap year.  The returned value
1522  * is the numeric day index within the week (0-6) which can be used in conjunction with
1523  * the {@link #monthNames} array to retrieve the textual day name.
1524  * Example:
1525  *<pre><code>
1526 var dt = new Date('1/10/2007');
1527 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1528 </code></pre>
1529  * @return {Number} The day number (0-6)
1530  */
1531 Date.prototype.getLastDayOfMonth = function() {
1532     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1533     return (day < 0) ? (day + 7) : day;
1534 };
1535
1536
1537 /**
1538  * Get the first date of this date's month
1539  * @return {Date}
1540  */
1541 Date.prototype.getFirstDateOfMonth = function() {
1542     return new Date(this.getFullYear(), this.getMonth(), 1);
1543 };
1544
1545 /**
1546  * Get the last date of this date's month
1547  * @return {Date}
1548  */
1549 Date.prototype.getLastDateOfMonth = function() {
1550     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1551 };
1552 /**
1553  * Get the number of days in the current month, adjusted for leap year.
1554  * @return {Number} The number of days in the month
1555  */
1556 Date.prototype.getDaysInMonth = function() {
1557     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1558     return Date.daysInMonth[this.getMonth()];
1559 };
1560
1561 /**
1562  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1563  * @return {String} 'st, 'nd', 'rd' or 'th'
1564  */
1565 Date.prototype.getSuffix = function() {
1566     switch (this.getDate()) {
1567         case 1:
1568         case 21:
1569         case 31:
1570             return "st";
1571         case 2:
1572         case 22:
1573             return "nd";
1574         case 3:
1575         case 23:
1576             return "rd";
1577         default:
1578             return "th";
1579     }
1580 };
1581
1582 // private
1583 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1584
1585 /**
1586  * An array of textual month names.
1587  * Override these values for international dates, for example...
1588  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1589  * @type Array
1590  * @static
1591  */
1592 Date.monthNames =
1593    ["January",
1594     "February",
1595     "March",
1596     "April",
1597     "May",
1598     "June",
1599     "July",
1600     "August",
1601     "September",
1602     "October",
1603     "November",
1604     "December"];
1605
1606 /**
1607  * An array of textual day names.
1608  * Override these values for international dates, for example...
1609  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1610  * @type Array
1611  * @static
1612  */
1613 Date.dayNames =
1614    ["Sunday",
1615     "Monday",
1616     "Tuesday",
1617     "Wednesday",
1618     "Thursday",
1619     "Friday",
1620     "Saturday"];
1621
1622 // private
1623 Date.y2kYear = 50;
1624 // private
1625 Date.monthNumbers = {
1626     Jan:0,
1627     Feb:1,
1628     Mar:2,
1629     Apr:3,
1630     May:4,
1631     Jun:5,
1632     Jul:6,
1633     Aug:7,
1634     Sep:8,
1635     Oct:9,
1636     Nov:10,
1637     Dec:11};
1638
1639 /**
1640  * Creates and returns a new Date instance with the exact same date value as the called instance.
1641  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1642  * variable will also be changed.  When the intention is to create a new variable that will not
1643  * modify the original instance, you should create a clone.
1644  *
1645  * Example of correctly cloning a date:
1646  * <pre><code>
1647 //wrong way:
1648 var orig = new Date('10/1/2006');
1649 var copy = orig;
1650 copy.setDate(5);
1651 document.write(orig);  //returns 'Thu Oct 05 2006'!
1652
1653 //correct way:
1654 var orig = new Date('10/1/2006');
1655 var copy = orig.clone();
1656 copy.setDate(5);
1657 document.write(orig);  //returns 'Thu Oct 01 2006'
1658 </code></pre>
1659  * @return {Date} The new Date instance
1660  */
1661 Date.prototype.clone = function() {
1662         return new Date(this.getTime());
1663 };
1664
1665 /**
1666  * Clears any time information from this date
1667  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1668  @return {Date} this or the clone
1669  */
1670 Date.prototype.clearTime = function(clone){
1671     if(clone){
1672         return this.clone().clearTime();
1673     }
1674     this.setHours(0);
1675     this.setMinutes(0);
1676     this.setSeconds(0);
1677     this.setMilliseconds(0);
1678     return this;
1679 };
1680
1681 // private
1682 // safari setMonth is broken
1683 if(Roo.isSafari){
1684     Date.brokenSetMonth = Date.prototype.setMonth;
1685         Date.prototype.setMonth = function(num){
1686                 if(num <= -1){
1687                         var n = Math.ceil(-num);
1688                         var back_year = Math.ceil(n/12);
1689                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1690                         this.setFullYear(this.getFullYear() - back_year);
1691                         return Date.brokenSetMonth.call(this, month);
1692                 } else {
1693                         return Date.brokenSetMonth.apply(this, arguments);
1694                 }
1695         };
1696 }
1697
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.MILLI = "ms";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.SECOND = "s";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.MINUTE = "mi";
1710 /** Date interval constant 
1711 * @static 
1712 * @type String */
1713 Date.HOUR = "h";
1714 /** Date interval constant 
1715 * @static 
1716 * @type String */
1717 Date.DAY = "d";
1718 /** Date interval constant 
1719 * @static 
1720 * @type String */
1721 Date.MONTH = "mo";
1722 /** Date interval constant 
1723 * @static 
1724 * @type String */
1725 Date.YEAR = "y";
1726
1727 /**
1728  * Provides a convenient method of performing basic date arithmetic.  This method
1729  * does not modify the Date instance being called - it creates and returns
1730  * a new Date instance containing the resulting date value.
1731  *
1732  * Examples:
1733  * <pre><code>
1734 //Basic usage:
1735 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1736 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1737
1738 //Negative values will subtract correctly:
1739 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1740 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1741
1742 //You can even chain several calls together in one line!
1743 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1744 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1745  </code></pre>
1746  *
1747  * @param {String} interval   A valid date interval enum value
1748  * @param {Number} value      The amount to add to the current date
1749  * @return {Date} The new Date instance
1750  */
1751 Date.prototype.add = function(interval, value){
1752   var d = this.clone();
1753   if (!interval || value === 0) return d;
1754   switch(interval.toLowerCase()){
1755     case Date.MILLI:
1756       d.setMilliseconds(this.getMilliseconds() + value);
1757       break;
1758     case Date.SECOND:
1759       d.setSeconds(this.getSeconds() + value);
1760       break;
1761     case Date.MINUTE:
1762       d.setMinutes(this.getMinutes() + value);
1763       break;
1764     case Date.HOUR:
1765       d.setHours(this.getHours() + value);
1766       break;
1767     case Date.DAY:
1768       d.setDate(this.getDate() + value);
1769       break;
1770     case Date.MONTH:
1771       var day = this.getDate();
1772       if(day > 28){
1773           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1774       }
1775       d.setDate(day);
1776       d.setMonth(this.getMonth() + value);
1777       break;
1778     case Date.YEAR:
1779       d.setFullYear(this.getFullYear() + value);
1780       break;
1781   }
1782   return d;
1783 };
1784 /*
1785  * Based on:
1786  * Ext JS Library 1.1.1
1787  * Copyright(c) 2006-2007, Ext JS, LLC.
1788  *
1789  * Originally Released Under LGPL - original licence link has changed is not relivant.
1790  *
1791  * Fork - LGPL
1792  * <script type="text/javascript">
1793  */
1794
1795 /**
1796  * @class Roo.lib.Dom
1797  * @static
1798  * 
1799  * Dom utils (from YIU afaik)
1800  * 
1801  **/
1802 Roo.lib.Dom = {
1803     /**
1804      * Get the view width
1805      * @param {Boolean} full True will get the full document, otherwise it's the view width
1806      * @return {Number} The width
1807      */
1808      
1809     getViewWidth : function(full) {
1810         return full ? this.getDocumentWidth() : this.getViewportWidth();
1811     },
1812     /**
1813      * Get the view height
1814      * @param {Boolean} full True will get the full document, otherwise it's the view height
1815      * @return {Number} The height
1816      */
1817     getViewHeight : function(full) {
1818         return full ? this.getDocumentHeight() : this.getViewportHeight();
1819     },
1820
1821     getDocumentHeight: function() {
1822         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1823         return Math.max(scrollHeight, this.getViewportHeight());
1824     },
1825
1826     getDocumentWidth: function() {
1827         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1828         return Math.max(scrollWidth, this.getViewportWidth());
1829     },
1830
1831     getViewportHeight: function() {
1832         var height = self.innerHeight;
1833         var mode = document.compatMode;
1834
1835         if ((mode || Roo.isIE) && !Roo.isOpera) {
1836             height = (mode == "CSS1Compat") ?
1837                      document.documentElement.clientHeight :
1838                      document.body.clientHeight;
1839         }
1840
1841         return height;
1842     },
1843
1844     getViewportWidth: function() {
1845         var width = self.innerWidth;
1846         var mode = document.compatMode;
1847
1848         if (mode || Roo.isIE) {
1849             width = (mode == "CSS1Compat") ?
1850                     document.documentElement.clientWidth :
1851                     document.body.clientWidth;
1852         }
1853         return width;
1854     },
1855
1856     isAncestor : function(p, c) {
1857         p = Roo.getDom(p);
1858         c = Roo.getDom(c);
1859         if (!p || !c) {
1860             return false;
1861         }
1862
1863         if (p.contains && !Roo.isSafari) {
1864             return p.contains(c);
1865         } else if (p.compareDocumentPosition) {
1866             return !!(p.compareDocumentPosition(c) & 16);
1867         } else {
1868             var parent = c.parentNode;
1869             while (parent) {
1870                 if (parent == p) {
1871                     return true;
1872                 }
1873                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1874                     return false;
1875                 }
1876                 parent = parent.parentNode;
1877             }
1878             return false;
1879         }
1880     },
1881
1882     getRegion : function(el) {
1883         return Roo.lib.Region.getRegion(el);
1884     },
1885
1886     getY : function(el) {
1887         return this.getXY(el)[1];
1888     },
1889
1890     getX : function(el) {
1891         return this.getXY(el)[0];
1892     },
1893
1894     getXY : function(el) {
1895         var p, pe, b, scroll, bd = document.body;
1896         el = Roo.getDom(el);
1897         var fly = Roo.lib.AnimBase.fly;
1898         if (el.getBoundingClientRect) {
1899             b = el.getBoundingClientRect();
1900             scroll = fly(document).getScroll();
1901             return [b.left + scroll.left, b.top + scroll.top];
1902         }
1903         var x = 0, y = 0;
1904
1905         p = el;
1906
1907         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1908
1909         while (p) {
1910
1911             x += p.offsetLeft;
1912             y += p.offsetTop;
1913
1914             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1915                 hasAbsolute = true;
1916             }
1917
1918             if (Roo.isGecko) {
1919                 pe = fly(p);
1920
1921                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1922                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1923
1924
1925                 x += bl;
1926                 y += bt;
1927
1928
1929                 if (p != el && pe.getStyle('overflow') != 'visible') {
1930                     x += bl;
1931                     y += bt;
1932                 }
1933             }
1934             p = p.offsetParent;
1935         }
1936
1937         if (Roo.isSafari && hasAbsolute) {
1938             x -= bd.offsetLeft;
1939             y -= bd.offsetTop;
1940         }
1941
1942         if (Roo.isGecko && !hasAbsolute) {
1943             var dbd = fly(bd);
1944             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1945             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1946         }
1947
1948         p = el.parentNode;
1949         while (p && p != bd) {
1950             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1951                 x -= p.scrollLeft;
1952                 y -= p.scrollTop;
1953             }
1954             p = p.parentNode;
1955         }
1956         return [x, y];
1957     },
1958  
1959   
1960
1961
1962     setXY : function(el, xy) {
1963         el = Roo.fly(el, '_setXY');
1964         el.position();
1965         var pts = el.translatePoints(xy);
1966         if (xy[0] !== false) {
1967             el.dom.style.left = pts.left + "px";
1968         }
1969         if (xy[1] !== false) {
1970             el.dom.style.top = pts.top + "px";
1971         }
1972     },
1973
1974     setX : function(el, x) {
1975         this.setXY(el, [x, false]);
1976     },
1977
1978     setY : function(el, y) {
1979         this.setXY(el, [false, y]);
1980     }
1981 };
1982 /*
1983  * Portions of this file are based on pieces of Yahoo User Interface Library
1984  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1985  * YUI licensed under the BSD License:
1986  * http://developer.yahoo.net/yui/license.txt
1987  * <script type="text/javascript">
1988  *
1989  */
1990
1991 Roo.lib.Event = function() {
1992     var loadComplete = false;
1993     var listeners = [];
1994     var unloadListeners = [];
1995     var retryCount = 0;
1996     var onAvailStack = [];
1997     var counter = 0;
1998     var lastError = null;
1999
2000     return {
2001         POLL_RETRYS: 200,
2002         POLL_INTERVAL: 20,
2003         EL: 0,
2004         TYPE: 1,
2005         FN: 2,
2006         WFN: 3,
2007         OBJ: 3,
2008         ADJ_SCOPE: 4,
2009         _interval: null,
2010
2011         startInterval: function() {
2012             if (!this._interval) {
2013                 var self = this;
2014                 var callback = function() {
2015                     self._tryPreloadAttach();
2016                 };
2017                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2018
2019             }
2020         },
2021
2022         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2023             onAvailStack.push({ id:         p_id,
2024                 fn:         p_fn,
2025                 obj:        p_obj,
2026                 override:   p_override,
2027                 checkReady: false    });
2028
2029             retryCount = this.POLL_RETRYS;
2030             this.startInterval();
2031         },
2032
2033
2034         addListener: function(el, eventName, fn) {
2035             el = Roo.getDom(el);
2036             if (!el || !fn) {
2037                 return false;
2038             }
2039
2040             if ("unload" == eventName) {
2041                 unloadListeners[unloadListeners.length] =
2042                 [el, eventName, fn];
2043                 return true;
2044             }
2045
2046             var wrappedFn = function(e) {
2047                 return fn(Roo.lib.Event.getEvent(e));
2048             };
2049
2050             var li = [el, eventName, fn, wrappedFn];
2051
2052             var index = listeners.length;
2053             listeners[index] = li;
2054
2055             this.doAdd(el, eventName, wrappedFn, false);
2056             return true;
2057
2058         },
2059
2060
2061         removeListener: function(el, eventName, fn) {
2062             var i, len;
2063
2064             el = Roo.getDom(el);
2065
2066             if(!fn) {
2067                 return this.purgeElement(el, false, eventName);
2068             }
2069
2070
2071             if ("unload" == eventName) {
2072
2073                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2074                     var li = unloadListeners[i];
2075                     if (li &&
2076                         li[0] == el &&
2077                         li[1] == eventName &&
2078                         li[2] == fn) {
2079                         unloadListeners.splice(i, 1);
2080                         return true;
2081                     }
2082                 }
2083
2084                 return false;
2085             }
2086
2087             var cacheItem = null;
2088
2089
2090             var index = arguments[3];
2091
2092             if ("undefined" == typeof index) {
2093                 index = this._getCacheIndex(el, eventName, fn);
2094             }
2095
2096             if (index >= 0) {
2097                 cacheItem = listeners[index];
2098             }
2099
2100             if (!el || !cacheItem) {
2101                 return false;
2102             }
2103
2104             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2105
2106             delete listeners[index][this.WFN];
2107             delete listeners[index][this.FN];
2108             listeners.splice(index, 1);
2109
2110             return true;
2111
2112         },
2113
2114
2115         getTarget: function(ev, resolveTextNode) {
2116             ev = ev.browserEvent || ev;
2117             var t = ev.target || ev.srcElement;
2118             return this.resolveTextNode(t);
2119         },
2120
2121
2122         resolveTextNode: function(node) {
2123             if (Roo.isSafari && node && 3 == node.nodeType) {
2124                 return node.parentNode;
2125             } else {
2126                 return node;
2127             }
2128         },
2129
2130
2131         getPageX: function(ev) {
2132             ev = ev.browserEvent || ev;
2133             var x = ev.pageX;
2134             if (!x && 0 !== x) {
2135                 x = ev.clientX || 0;
2136
2137                 if (Roo.isIE) {
2138                     x += this.getScroll()[1];
2139                 }
2140             }
2141
2142             return x;
2143         },
2144
2145
2146         getPageY: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             var y = ev.pageY;
2149             if (!y && 0 !== y) {
2150                 y = ev.clientY || 0;
2151
2152                 if (Roo.isIE) {
2153                     y += this.getScroll()[0];
2154                 }
2155             }
2156
2157
2158             return y;
2159         },
2160
2161
2162         getXY: function(ev) {
2163             ev = ev.browserEvent || ev;
2164             return [this.getPageX(ev), this.getPageY(ev)];
2165         },
2166
2167
2168         getRelatedTarget: function(ev) {
2169             ev = ev.browserEvent || ev;
2170             var t = ev.relatedTarget;
2171             if (!t) {
2172                 if (ev.type == "mouseout") {
2173                     t = ev.toElement;
2174                 } else if (ev.type == "mouseover") {
2175                     t = ev.fromElement;
2176                 }
2177             }
2178
2179             return this.resolveTextNode(t);
2180         },
2181
2182
2183         getTime: function(ev) {
2184             ev = ev.browserEvent || ev;
2185             if (!ev.time) {
2186                 var t = new Date().getTime();
2187                 try {
2188                     ev.time = t;
2189                 } catch(ex) {
2190                     this.lastError = ex;
2191                     return t;
2192                 }
2193             }
2194
2195             return ev.time;
2196         },
2197
2198
2199         stopEvent: function(ev) {
2200             this.stopPropagation(ev);
2201             this.preventDefault(ev);
2202         },
2203
2204
2205         stopPropagation: function(ev) {
2206             ev = ev.browserEvent || ev;
2207             if (ev.stopPropagation) {
2208                 ev.stopPropagation();
2209             } else {
2210                 ev.cancelBubble = true;
2211             }
2212         },
2213
2214
2215         preventDefault: function(ev) {
2216             ev = ev.browserEvent || ev;
2217             if(ev.preventDefault) {
2218                 ev.preventDefault();
2219             } else {
2220                 ev.returnValue = false;
2221             }
2222         },
2223
2224
2225         getEvent: function(e) {
2226             var ev = e || window.event;
2227             if (!ev) {
2228                 var c = this.getEvent.caller;
2229                 while (c) {
2230                     ev = c.arguments[0];
2231                     if (ev && Event == ev.constructor) {
2232                         break;
2233                     }
2234                     c = c.caller;
2235                 }
2236             }
2237             return ev;
2238         },
2239
2240
2241         getCharCode: function(ev) {
2242             ev = ev.browserEvent || ev;
2243             return ev.charCode || ev.keyCode || 0;
2244         },
2245
2246
2247         _getCacheIndex: function(el, eventName, fn) {
2248             for (var i = 0,len = listeners.length; i < len; ++i) {
2249                 var li = listeners[i];
2250                 if (li &&
2251                     li[this.FN] == fn &&
2252                     li[this.EL] == el &&
2253                     li[this.TYPE] == eventName) {
2254                     return i;
2255                 }
2256             }
2257
2258             return -1;
2259         },
2260
2261
2262         elCache: {},
2263
2264
2265         getEl: function(id) {
2266             return document.getElementById(id);
2267         },
2268
2269
2270         clearCache: function() {
2271         },
2272
2273
2274         _load: function(e) {
2275             loadComplete = true;
2276             var EU = Roo.lib.Event;
2277
2278
2279             if (Roo.isIE) {
2280                 EU.doRemove(window, "load", EU._load);
2281             }
2282         },
2283
2284
2285         _tryPreloadAttach: function() {
2286
2287             if (this.locked) {
2288                 return false;
2289             }
2290
2291             this.locked = true;
2292
2293
2294             var tryAgain = !loadComplete;
2295             if (!tryAgain) {
2296                 tryAgain = (retryCount > 0);
2297             }
2298
2299
2300             var notAvail = [];
2301             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2302                 var item = onAvailStack[i];
2303                 if (item) {
2304                     var el = this.getEl(item.id);
2305
2306                     if (el) {
2307                         if (!item.checkReady ||
2308                             loadComplete ||
2309                             el.nextSibling ||
2310                             (document && document.body)) {
2311
2312                             var scope = el;
2313                             if (item.override) {
2314                                 if (item.override === true) {
2315                                     scope = item.obj;
2316                                 } else {
2317                                     scope = item.override;
2318                                 }
2319                             }
2320                             item.fn.call(scope, item.obj);
2321                             onAvailStack[i] = null;
2322                         }
2323                     } else {
2324                         notAvail.push(item);
2325                     }
2326                 }
2327             }
2328
2329             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2330
2331             if (tryAgain) {
2332
2333                 this.startInterval();
2334             } else {
2335                 clearInterval(this._interval);
2336                 this._interval = null;
2337             }
2338
2339             this.locked = false;
2340
2341             return true;
2342
2343         },
2344
2345
2346         purgeElement: function(el, recurse, eventName) {
2347             var elListeners = this.getListeners(el, eventName);
2348             if (elListeners) {
2349                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2350                     var l = elListeners[i];
2351                     this.removeListener(el, l.type, l.fn);
2352                 }
2353             }
2354
2355             if (recurse && el && el.childNodes) {
2356                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2357                     this.purgeElement(el.childNodes[i], recurse, eventName);
2358                 }
2359             }
2360         },
2361
2362
2363         getListeners: function(el, eventName) {
2364             var results = [], searchLists;
2365             if (!eventName) {
2366                 searchLists = [listeners, unloadListeners];
2367             } else if (eventName == "unload") {
2368                 searchLists = [unloadListeners];
2369             } else {
2370                 searchLists = [listeners];
2371             }
2372
2373             for (var j = 0; j < searchLists.length; ++j) {
2374                 var searchList = searchLists[j];
2375                 if (searchList && searchList.length > 0) {
2376                     for (var i = 0,len = searchList.length; i < len; ++i) {
2377                         var l = searchList[i];
2378                         if (l && l[this.EL] === el &&
2379                             (!eventName || eventName === l[this.TYPE])) {
2380                             results.push({
2381                                 type:   l[this.TYPE],
2382                                 fn:     l[this.FN],
2383                                 obj:    l[this.OBJ],
2384                                 adjust: l[this.ADJ_SCOPE],
2385                                 index:  i
2386                             });
2387                         }
2388                     }
2389                 }
2390             }
2391
2392             return (results.length) ? results : null;
2393         },
2394
2395
2396         _unload: function(e) {
2397
2398             var EU = Roo.lib.Event, i, j, l, len, index;
2399
2400             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2401                 l = unloadListeners[i];
2402                 if (l) {
2403                     var scope = window;
2404                     if (l[EU.ADJ_SCOPE]) {
2405                         if (l[EU.ADJ_SCOPE] === true) {
2406                             scope = l[EU.OBJ];
2407                         } else {
2408                             scope = l[EU.ADJ_SCOPE];
2409                         }
2410                     }
2411                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2412                     unloadListeners[i] = null;
2413                     l = null;
2414                     scope = null;
2415                 }
2416             }
2417
2418             unloadListeners = null;
2419
2420             if (listeners && listeners.length > 0) {
2421                 j = listeners.length;
2422                 while (j) {
2423                     index = j - 1;
2424                     l = listeners[index];
2425                     if (l) {
2426                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2427                                 l[EU.FN], index);
2428                     }
2429                     j = j - 1;
2430                 }
2431                 l = null;
2432
2433                 EU.clearCache();
2434             }
2435
2436             EU.doRemove(window, "unload", EU._unload);
2437
2438         },
2439
2440
2441         getScroll: function() {
2442             var dd = document.documentElement, db = document.body;
2443             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2444                 return [dd.scrollTop, dd.scrollLeft];
2445             } else if (db) {
2446                 return [db.scrollTop, db.scrollLeft];
2447             } else {
2448                 return [0, 0];
2449             }
2450         },
2451
2452
2453         doAdd: function () {
2454             if (window.addEventListener) {
2455                 return function(el, eventName, fn, capture) {
2456                     el.addEventListener(eventName, fn, (capture));
2457                 };
2458             } else if (window.attachEvent) {
2459                 return function(el, eventName, fn, capture) {
2460                     el.attachEvent("on" + eventName, fn);
2461                 };
2462             } else {
2463                 return function() {
2464                 };
2465             }
2466         }(),
2467
2468
2469         doRemove: function() {
2470             if (window.removeEventListener) {
2471                 return function (el, eventName, fn, capture) {
2472                     el.removeEventListener(eventName, fn, (capture));
2473                 };
2474             } else if (window.detachEvent) {
2475                 return function (el, eventName, fn) {
2476                     el.detachEvent("on" + eventName, fn);
2477                 };
2478             } else {
2479                 return function() {
2480                 };
2481             }
2482         }()
2483     };
2484     
2485 }();
2486 (function() {     
2487    
2488     var E = Roo.lib.Event;
2489     E.on = E.addListener;
2490     E.un = E.removeListener;
2491
2492     if (document && document.body) {
2493         E._load();
2494     } else {
2495         E.doAdd(window, "load", E._load);
2496     }
2497     E.doAdd(window, "unload", E._unload);
2498     E._tryPreloadAttach();
2499 })();
2500
2501 /*
2502  * Portions of this file are based on pieces of Yahoo User Interface Library
2503  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2504  * YUI licensed under the BSD License:
2505  * http://developer.yahoo.net/yui/license.txt
2506  * <script type="text/javascript">
2507  *
2508  */
2509
2510 (function() {
2511     /**
2512      * @class Roo.lib.Ajax
2513      *
2514      */
2515     Roo.lib.Ajax = {
2516         /**
2517          * @static 
2518          */
2519         request : function(method, uri, cb, data, options) {
2520             if(options){
2521                 var hs = options.headers;
2522                 if(hs){
2523                     for(var h in hs){
2524                         if(hs.hasOwnProperty(h)){
2525                             this.initHeader(h, hs[h], false);
2526                         }
2527                     }
2528                 }
2529                 if(options.xmlData){
2530                     this.initHeader('Content-Type', 'text/xml', false);
2531                     method = 'POST';
2532                     data = options.xmlData;
2533                 }
2534             }
2535
2536             return this.asyncRequest(method, uri, cb, data);
2537         },
2538
2539         serializeForm : function(form) {
2540             if(typeof form == 'string') {
2541                 form = (document.getElementById(form) || document.forms[form]);
2542             }
2543
2544             var el, name, val, disabled, data = '', hasSubmit = false;
2545             for (var i = 0; i < form.elements.length; i++) {
2546                 el = form.elements[i];
2547                 disabled = form.elements[i].disabled;
2548                 name = form.elements[i].name;
2549                 val = form.elements[i].value;
2550
2551                 if (!disabled && name){
2552                     switch (el.type)
2553                             {
2554                         case 'select-one':
2555                         case 'select-multiple':
2556                             for (var j = 0; j < el.options.length; j++) {
2557                                 if (el.options[j].selected) {
2558                                     if (Roo.isIE) {
2559                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2560                                     }
2561                                     else {
2562                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2563                                     }
2564                                 }
2565                             }
2566                             break;
2567                         case 'radio':
2568                         case 'checkbox':
2569                             if (el.checked) {
2570                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2571                             }
2572                             break;
2573                         case 'file':
2574
2575                         case undefined:
2576
2577                         case 'reset':
2578
2579                         case 'button':
2580
2581                             break;
2582                         case 'submit':
2583                             if(hasSubmit == false) {
2584                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2585                                 hasSubmit = true;
2586                             }
2587                             break;
2588                         default:
2589                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2590                             break;
2591                     }
2592                 }
2593             }
2594             data = data.substr(0, data.length - 1);
2595             return data;
2596         },
2597
2598         headers:{},
2599
2600         hasHeaders:false,
2601
2602         useDefaultHeader:true,
2603
2604         defaultPostHeader:'application/x-www-form-urlencoded',
2605
2606         useDefaultXhrHeader:true,
2607
2608         defaultXhrHeader:'XMLHttpRequest',
2609
2610         hasDefaultHeaders:true,
2611
2612         defaultHeaders:{},
2613
2614         poll:{},
2615
2616         timeout:{},
2617
2618         pollInterval:50,
2619
2620         transactionId:0,
2621
2622         setProgId:function(id)
2623         {
2624             this.activeX.unshift(id);
2625         },
2626
2627         setDefaultPostHeader:function(b)
2628         {
2629             this.useDefaultHeader = b;
2630         },
2631
2632         setDefaultXhrHeader:function(b)
2633         {
2634             this.useDefaultXhrHeader = b;
2635         },
2636
2637         setPollingInterval:function(i)
2638         {
2639             if (typeof i == 'number' && isFinite(i)) {
2640                 this.pollInterval = i;
2641             }
2642         },
2643
2644         createXhrObject:function(transactionId)
2645         {
2646             var obj,http;
2647             try
2648             {
2649
2650                 http = new XMLHttpRequest();
2651
2652                 obj = { conn:http, tId:transactionId };
2653             }
2654             catch(e)
2655             {
2656                 for (var i = 0; i < this.activeX.length; ++i) {
2657                     try
2658                     {
2659
2660                         http = new ActiveXObject(this.activeX[i]);
2661
2662                         obj = { conn:http, tId:transactionId };
2663                         break;
2664                     }
2665                     catch(e) {
2666                     }
2667                 }
2668             }
2669             finally
2670             {
2671                 return obj;
2672             }
2673         },
2674
2675         getConnectionObject:function()
2676         {
2677             var o;
2678             var tId = this.transactionId;
2679
2680             try
2681             {
2682                 o = this.createXhrObject(tId);
2683                 if (o) {
2684                     this.transactionId++;
2685                 }
2686             }
2687             catch(e) {
2688             }
2689             finally
2690             {
2691                 return o;
2692             }
2693         },
2694
2695         asyncRequest:function(method, uri, callback, postData)
2696         {
2697             var o = this.getConnectionObject();
2698
2699             if (!o) {
2700                 return null;
2701             }
2702             else {
2703                 o.conn.open(method, uri, true);
2704
2705                 if (this.useDefaultXhrHeader) {
2706                     if (!this.defaultHeaders['X-Requested-With']) {
2707                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2708                     }
2709                 }
2710
2711                 if(postData && this.useDefaultHeader){
2712                     this.initHeader('Content-Type', this.defaultPostHeader);
2713                 }
2714
2715                  if (this.hasDefaultHeaders || this.hasHeaders) {
2716                     this.setHeader(o);
2717                 }
2718
2719                 this.handleReadyState(o, callback);
2720                 o.conn.send(postData || null);
2721
2722                 return o;
2723             }
2724         },
2725
2726         handleReadyState:function(o, callback)
2727         {
2728             var oConn = this;
2729
2730             if (callback && callback.timeout) {
2731                 
2732                 this.timeout[o.tId] = window.setTimeout(function() {
2733                     oConn.abort(o, callback, true);
2734                 }, callback.timeout);
2735             }
2736
2737             this.poll[o.tId] = window.setInterval(
2738                     function() {
2739                         if (o.conn && o.conn.readyState == 4) {
2740                             window.clearInterval(oConn.poll[o.tId]);
2741                             delete oConn.poll[o.tId];
2742
2743                             if(callback && callback.timeout) {
2744                                 window.clearTimeout(oConn.timeout[o.tId]);
2745                                 delete oConn.timeout[o.tId];
2746                             }
2747
2748                             oConn.handleTransactionResponse(o, callback);
2749                         }
2750                     }
2751                     , this.pollInterval);
2752         },
2753
2754         handleTransactionResponse:function(o, callback, isAbort)
2755         {
2756
2757             if (!callback) {
2758                 this.releaseObject(o);
2759                 return;
2760             }
2761
2762             var httpStatus, responseObject;
2763
2764             try
2765             {
2766                 if (o.conn.status !== undefined && o.conn.status != 0) {
2767                     httpStatus = o.conn.status;
2768                 }
2769                 else {
2770                     httpStatus = 13030;
2771                 }
2772             }
2773             catch(e) {
2774
2775
2776                 httpStatus = 13030;
2777             }
2778
2779             if (httpStatus >= 200 && httpStatus < 300) {
2780                 responseObject = this.createResponseObject(o, callback.argument);
2781                 if (callback.success) {
2782                     if (!callback.scope) {
2783                         callback.success(responseObject);
2784                     }
2785                     else {
2786
2787
2788                         callback.success.apply(callback.scope, [responseObject]);
2789                     }
2790                 }
2791             }
2792             else {
2793                 switch (httpStatus) {
2794
2795                     case 12002:
2796                     case 12029:
2797                     case 12030:
2798                     case 12031:
2799                     case 12152:
2800                     case 13030:
2801                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2802                         if (callback.failure) {
2803                             if (!callback.scope) {
2804                                 callback.failure(responseObject);
2805                             }
2806                             else {
2807                                 callback.failure.apply(callback.scope, [responseObject]);
2808                             }
2809                         }
2810                         break;
2811                     default:
2812                         responseObject = this.createResponseObject(o, callback.argument);
2813                         if (callback.failure) {
2814                             if (!callback.scope) {
2815                                 callback.failure(responseObject);
2816                             }
2817                             else {
2818                                 callback.failure.apply(callback.scope, [responseObject]);
2819                             }
2820                         }
2821                 }
2822             }
2823
2824             this.releaseObject(o);
2825             responseObject = null;
2826         },
2827
2828         createResponseObject:function(o, callbackArg)
2829         {
2830             var obj = {};
2831             var headerObj = {};
2832
2833             try
2834             {
2835                 var headerStr = o.conn.getAllResponseHeaders();
2836                 var header = headerStr.split('\n');
2837                 for (var i = 0; i < header.length; i++) {
2838                     var delimitPos = header[i].indexOf(':');
2839                     if (delimitPos != -1) {
2840                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2841                     }
2842                 }
2843             }
2844             catch(e) {
2845             }
2846
2847             obj.tId = o.tId;
2848             obj.status = o.conn.status;
2849             obj.statusText = o.conn.statusText;
2850             obj.getResponseHeader = headerObj;
2851             obj.getAllResponseHeaders = headerStr;
2852             obj.responseText = o.conn.responseText;
2853             obj.responseXML = o.conn.responseXML;
2854
2855             if (typeof callbackArg !== undefined) {
2856                 obj.argument = callbackArg;
2857             }
2858
2859             return obj;
2860         },
2861
2862         createExceptionObject:function(tId, callbackArg, isAbort)
2863         {
2864             var COMM_CODE = 0;
2865             var COMM_ERROR = 'communication failure';
2866             var ABORT_CODE = -1;
2867             var ABORT_ERROR = 'transaction aborted';
2868
2869             var obj = {};
2870
2871             obj.tId = tId;
2872             if (isAbort) {
2873                 obj.status = ABORT_CODE;
2874                 obj.statusText = ABORT_ERROR;
2875             }
2876             else {
2877                 obj.status = COMM_CODE;
2878                 obj.statusText = COMM_ERROR;
2879             }
2880
2881             if (callbackArg) {
2882                 obj.argument = callbackArg;
2883             }
2884
2885             return obj;
2886         },
2887
2888         initHeader:function(label, value, isDefault)
2889         {
2890             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2891
2892             if (headerObj[label] === undefined) {
2893                 headerObj[label] = value;
2894             }
2895             else {
2896
2897
2898                 headerObj[label] = value + "," + headerObj[label];
2899             }
2900
2901             if (isDefault) {
2902                 this.hasDefaultHeaders = true;
2903             }
2904             else {
2905                 this.hasHeaders = true;
2906             }
2907         },
2908
2909
2910         setHeader:function(o)
2911         {
2912             if (this.hasDefaultHeaders) {
2913                 for (var prop in this.defaultHeaders) {
2914                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2915                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2916                     }
2917                 }
2918             }
2919
2920             if (this.hasHeaders) {
2921                 for (var prop in this.headers) {
2922                     if (this.headers.hasOwnProperty(prop)) {
2923                         o.conn.setRequestHeader(prop, this.headers[prop]);
2924                     }
2925                 }
2926                 this.headers = {};
2927                 this.hasHeaders = false;
2928             }
2929         },
2930
2931         resetDefaultHeaders:function() {
2932             delete this.defaultHeaders;
2933             this.defaultHeaders = {};
2934             this.hasDefaultHeaders = false;
2935         },
2936
2937         abort:function(o, callback, isTimeout)
2938         {
2939             if(this.isCallInProgress(o)) {
2940                 o.conn.abort();
2941                 window.clearInterval(this.poll[o.tId]);
2942                 delete this.poll[o.tId];
2943                 if (isTimeout) {
2944                     delete this.timeout[o.tId];
2945                 }
2946
2947                 this.handleTransactionResponse(o, callback, true);
2948
2949                 return true;
2950             }
2951             else {
2952                 return false;
2953             }
2954         },
2955
2956
2957         isCallInProgress:function(o)
2958         {
2959             if (o && o.conn) {
2960                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2961             }
2962             else {
2963
2964                 return false;
2965             }
2966         },
2967
2968
2969         releaseObject:function(o)
2970         {
2971
2972             o.conn = null;
2973
2974             o = null;
2975         },
2976
2977         activeX:[
2978         'MSXML2.XMLHTTP.3.0',
2979         'MSXML2.XMLHTTP',
2980         'Microsoft.XMLHTTP'
2981         ]
2982
2983
2984     };
2985 })();/*
2986  * Portions of this file are based on pieces of Yahoo User Interface Library
2987  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2988  * YUI licensed under the BSD License:
2989  * http://developer.yahoo.net/yui/license.txt
2990  * <script type="text/javascript">
2991  *
2992  */
2993
2994 Roo.lib.Region = function(t, r, b, l) {
2995     this.top = t;
2996     this[1] = t;
2997     this.right = r;
2998     this.bottom = b;
2999     this.left = l;
3000     this[0] = l;
3001 };
3002
3003
3004 Roo.lib.Region.prototype = {
3005     contains : function(region) {
3006         return ( region.left >= this.left &&
3007                  region.right <= this.right &&
3008                  region.top >= this.top &&
3009                  region.bottom <= this.bottom    );
3010
3011     },
3012
3013     getArea : function() {
3014         return ( (this.bottom - this.top) * (this.right - this.left) );
3015     },
3016
3017     intersect : function(region) {
3018         var t = Math.max(this.top, region.top);
3019         var r = Math.min(this.right, region.right);
3020         var b = Math.min(this.bottom, region.bottom);
3021         var l = Math.max(this.left, region.left);
3022
3023         if (b >= t && r >= l) {
3024             return new Roo.lib.Region(t, r, b, l);
3025         } else {
3026             return null;
3027         }
3028     },
3029     union : function(region) {
3030         var t = Math.min(this.top, region.top);
3031         var r = Math.max(this.right, region.right);
3032         var b = Math.max(this.bottom, region.bottom);
3033         var l = Math.min(this.left, region.left);
3034
3035         return new Roo.lib.Region(t, r, b, l);
3036     },
3037
3038     adjust : function(t, l, b, r) {
3039         this.top += t;
3040         this.left += l;
3041         this.right += r;
3042         this.bottom += b;
3043         return this;
3044     }
3045 };
3046
3047 Roo.lib.Region.getRegion = function(el) {
3048     var p = Roo.lib.Dom.getXY(el);
3049
3050     var t = p[1];
3051     var r = p[0] + el.offsetWidth;
3052     var b = p[1] + el.offsetHeight;
3053     var l = p[0];
3054
3055     return new Roo.lib.Region(t, r, b, l);
3056 };
3057 /*
3058  * Portions of this file are based on pieces of Yahoo User Interface Library
3059  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3060  * YUI licensed under the BSD License:
3061  * http://developer.yahoo.net/yui/license.txt
3062  * <script type="text/javascript">
3063  *
3064  */
3065 //@@dep Roo.lib.Region
3066
3067
3068 Roo.lib.Point = function(x, y) {
3069     if (x instanceof Array) {
3070         y = x[1];
3071         x = x[0];
3072     }
3073     this.x = this.right = this.left = this[0] = x;
3074     this.y = this.top = this.bottom = this[1] = y;
3075 };
3076
3077 Roo.lib.Point.prototype = new Roo.lib.Region();
3078 /*
3079  * Portions of this file are based on pieces of Yahoo User Interface Library
3080  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3081  * YUI licensed under the BSD License:
3082  * http://developer.yahoo.net/yui/license.txt
3083  * <script type="text/javascript">
3084  *
3085  */
3086  
3087 (function() {   
3088
3089     Roo.lib.Anim = {
3090         scroll : function(el, args, duration, easing, cb, scope) {
3091             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3092         },
3093
3094         motion : function(el, args, duration, easing, cb, scope) {
3095             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3096         },
3097
3098         color : function(el, args, duration, easing, cb, scope) {
3099             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3100         },
3101
3102         run : function(el, args, duration, easing, cb, scope, type) {
3103             type = type || Roo.lib.AnimBase;
3104             if (typeof easing == "string") {
3105                 easing = Roo.lib.Easing[easing];
3106             }
3107             var anim = new type(el, args, duration, easing);
3108             anim.animateX(function() {
3109                 Roo.callback(cb, scope);
3110             });
3111             return anim;
3112         }
3113     };
3114 })();/*
3115  * Portions of this file are based on pieces of Yahoo User Interface Library
3116  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3117  * YUI licensed under the BSD License:
3118  * http://developer.yahoo.net/yui/license.txt
3119  * <script type="text/javascript">
3120  *
3121  */
3122
3123 (function() {    
3124     var libFlyweight;
3125     
3126     function fly(el) {
3127         if (!libFlyweight) {
3128             libFlyweight = new Roo.Element.Flyweight();
3129         }
3130         libFlyweight.dom = el;
3131         return libFlyweight;
3132     }
3133
3134     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3135     
3136    
3137     
3138     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3139         if (el) {
3140             this.init(el, attributes, duration, method);
3141         }
3142     };
3143
3144     Roo.lib.AnimBase.fly = fly;
3145     
3146     
3147     
3148     Roo.lib.AnimBase.prototype = {
3149
3150         toString: function() {
3151             var el = this.getEl();
3152             var id = el.id || el.tagName;
3153             return ("Anim " + id);
3154         },
3155
3156         patterns: {
3157             noNegatives:        /width|height|opacity|padding/i,
3158             offsetAttribute:  /^((width|height)|(top|left))$/,
3159             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3160             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3161         },
3162
3163
3164         doMethod: function(attr, start, end) {
3165             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3166         },
3167
3168
3169         setAttribute: function(attr, val, unit) {
3170             if (this.patterns.noNegatives.test(attr)) {
3171                 val = (val > 0) ? val : 0;
3172             }
3173
3174             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3175         },
3176
3177
3178         getAttribute: function(attr) {
3179             var el = this.getEl();
3180             var val = fly(el).getStyle(attr);
3181
3182             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3183                 return parseFloat(val);
3184             }
3185
3186             var a = this.patterns.offsetAttribute.exec(attr) || [];
3187             var pos = !!( a[3] );
3188             var box = !!( a[2] );
3189
3190
3191             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3192                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3193             } else {
3194                 val = 0;
3195             }
3196
3197             return val;
3198         },
3199
3200
3201         getDefaultUnit: function(attr) {
3202             if (this.patterns.defaultUnit.test(attr)) {
3203                 return 'px';
3204             }
3205
3206             return '';
3207         },
3208
3209         animateX : function(callback, scope) {
3210             var f = function() {
3211                 this.onComplete.removeListener(f);
3212                 if (typeof callback == "function") {
3213                     callback.call(scope || this, this);
3214                 }
3215             };
3216             this.onComplete.addListener(f, this);
3217             this.animate();
3218         },
3219
3220
3221         setRuntimeAttribute: function(attr) {
3222             var start;
3223             var end;
3224             var attributes = this.attributes;
3225
3226             this.runtimeAttributes[attr] = {};
3227
3228             var isset = function(prop) {
3229                 return (typeof prop !== 'undefined');
3230             };
3231
3232             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3233                 return false;
3234             }
3235
3236             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3237
3238
3239             if (isset(attributes[attr]['to'])) {
3240                 end = attributes[attr]['to'];
3241             } else if (isset(attributes[attr]['by'])) {
3242                 if (start.constructor == Array) {
3243                     end = [];
3244                     for (var i = 0, len = start.length; i < len; ++i) {
3245                         end[i] = start[i] + attributes[attr]['by'][i];
3246                     }
3247                 } else {
3248                     end = start + attributes[attr]['by'];
3249                 }
3250             }
3251
3252             this.runtimeAttributes[attr].start = start;
3253             this.runtimeAttributes[attr].end = end;
3254
3255
3256             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3257         },
3258
3259
3260         init: function(el, attributes, duration, method) {
3261
3262             var isAnimated = false;
3263
3264
3265             var startTime = null;
3266
3267
3268             var actualFrames = 0;
3269
3270
3271             el = Roo.getDom(el);
3272
3273
3274             this.attributes = attributes || {};
3275
3276
3277             this.duration = duration || 1;
3278
3279
3280             this.method = method || Roo.lib.Easing.easeNone;
3281
3282
3283             this.useSeconds = true;
3284
3285
3286             this.currentFrame = 0;
3287
3288
3289             this.totalFrames = Roo.lib.AnimMgr.fps;
3290
3291
3292             this.getEl = function() {
3293                 return el;
3294             };
3295
3296
3297             this.isAnimated = function() {
3298                 return isAnimated;
3299             };
3300
3301
3302             this.getStartTime = function() {
3303                 return startTime;
3304             };
3305
3306             this.runtimeAttributes = {};
3307
3308
3309             this.animate = function() {
3310                 if (this.isAnimated()) {
3311                     return false;
3312                 }
3313
3314                 this.currentFrame = 0;
3315
3316                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3317
3318                 Roo.lib.AnimMgr.registerElement(this);
3319             };
3320
3321
3322             this.stop = function(finish) {
3323                 if (finish) {
3324                     this.currentFrame = this.totalFrames;
3325                     this._onTween.fire();
3326                 }
3327                 Roo.lib.AnimMgr.stop(this);
3328             };
3329
3330             var onStart = function() {
3331                 this.onStart.fire();
3332
3333                 this.runtimeAttributes = {};
3334                 for (var attr in this.attributes) {
3335                     this.setRuntimeAttribute(attr);
3336                 }
3337
3338                 isAnimated = true;
3339                 actualFrames = 0;
3340                 startTime = new Date();
3341             };
3342
3343
3344             var onTween = function() {
3345                 var data = {
3346                     duration: new Date() - this.getStartTime(),
3347                     currentFrame: this.currentFrame
3348                 };
3349
3350                 data.toString = function() {
3351                     return (
3352                             'duration: ' + data.duration +
3353                             ', currentFrame: ' + data.currentFrame
3354                             );
3355                 };
3356
3357                 this.onTween.fire(data);
3358
3359                 var runtimeAttributes = this.runtimeAttributes;
3360
3361                 for (var attr in runtimeAttributes) {
3362                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3363                 }
3364
3365                 actualFrames += 1;
3366             };
3367
3368             var onComplete = function() {
3369                 var actual_duration = (new Date() - startTime) / 1000 ;
3370
3371                 var data = {
3372                     duration: actual_duration,
3373                     frames: actualFrames,
3374                     fps: actualFrames / actual_duration
3375                 };
3376
3377                 data.toString = function() {
3378                     return (
3379                             'duration: ' + data.duration +
3380                             ', frames: ' + data.frames +
3381                             ', fps: ' + data.fps
3382                             );
3383                 };
3384
3385                 isAnimated = false;
3386                 actualFrames = 0;
3387                 this.onComplete.fire(data);
3388             };
3389
3390
3391             this._onStart = new Roo.util.Event(this);
3392             this.onStart = new Roo.util.Event(this);
3393             this.onTween = new Roo.util.Event(this);
3394             this._onTween = new Roo.util.Event(this);
3395             this.onComplete = new Roo.util.Event(this);
3396             this._onComplete = new Roo.util.Event(this);
3397             this._onStart.addListener(onStart);
3398             this._onTween.addListener(onTween);
3399             this._onComplete.addListener(onComplete);
3400         }
3401     };
3402 })();
3403 /*
3404  * Portions of this file are based on pieces of Yahoo User Interface Library
3405  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3406  * YUI licensed under the BSD License:
3407  * http://developer.yahoo.net/yui/license.txt
3408  * <script type="text/javascript">
3409  *
3410  */
3411
3412 Roo.lib.AnimMgr = new function() {
3413
3414         var thread = null;
3415
3416
3417         var queue = [];
3418
3419
3420         var tweenCount = 0;
3421
3422
3423         this.fps = 1000;
3424
3425
3426         this.delay = 1;
3427
3428
3429         this.registerElement = function(tween) {
3430             queue[queue.length] = tween;
3431             tweenCount += 1;
3432             tween._onStart.fire();
3433             this.start();
3434         };
3435
3436
3437         this.unRegister = function(tween, index) {
3438             tween._onComplete.fire();
3439             index = index || getIndex(tween);
3440             if (index != -1) {
3441                 queue.splice(index, 1);
3442             }
3443
3444             tweenCount -= 1;
3445             if (tweenCount <= 0) {
3446                 this.stop();
3447             }
3448         };
3449
3450
3451         this.start = function() {
3452             if (thread === null) {
3453                 thread = setInterval(this.run, this.delay);
3454             }
3455         };
3456
3457
3458         this.stop = function(tween) {
3459             if (!tween) {
3460                 clearInterval(thread);
3461
3462                 for (var i = 0, len = queue.length; i < len; ++i) {
3463                     if (queue[0].isAnimated()) {
3464                         this.unRegister(queue[0], 0);
3465                     }
3466                 }
3467
3468                 queue = [];
3469                 thread = null;
3470                 tweenCount = 0;
3471             }
3472             else {
3473                 this.unRegister(tween);
3474             }
3475         };
3476
3477
3478         this.run = function() {
3479             for (var i = 0, len = queue.length; i < len; ++i) {
3480                 var tween = queue[i];
3481                 if (!tween || !tween.isAnimated()) {
3482                     continue;
3483                 }
3484
3485                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3486                 {
3487                     tween.currentFrame += 1;
3488
3489                     if (tween.useSeconds) {
3490                         correctFrame(tween);
3491                     }
3492                     tween._onTween.fire();
3493                 }
3494                 else {
3495                     Roo.lib.AnimMgr.stop(tween, i);
3496                 }
3497             }
3498         };
3499
3500         var getIndex = function(anim) {
3501             for (var i = 0, len = queue.length; i < len; ++i) {
3502                 if (queue[i] == anim) {
3503                     return i;
3504                 }
3505             }
3506             return -1;
3507         };
3508
3509
3510         var correctFrame = function(tween) {
3511             var frames = tween.totalFrames;
3512             var frame = tween.currentFrame;
3513             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3514             var elapsed = (new Date() - tween.getStartTime());
3515             var tweak = 0;
3516
3517             if (elapsed < tween.duration * 1000) {
3518                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3519             } else {
3520                 tweak = frames - (frame + 1);
3521             }
3522             if (tweak > 0 && isFinite(tweak)) {
3523                 if (tween.currentFrame + tweak >= frames) {
3524                     tweak = frames - (frame + 1);
3525                 }
3526
3527                 tween.currentFrame += tweak;
3528             }
3529         };
3530     };/*
3531  * Portions of this file are based on pieces of Yahoo User Interface Library
3532  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3533  * YUI licensed under the BSD License:
3534  * http://developer.yahoo.net/yui/license.txt
3535  * <script type="text/javascript">
3536  *
3537  */
3538 Roo.lib.Bezier = new function() {
3539
3540         this.getPosition = function(points, t) {
3541             var n = points.length;
3542             var tmp = [];
3543
3544             for (var i = 0; i < n; ++i) {
3545                 tmp[i] = [points[i][0], points[i][1]];
3546             }
3547
3548             for (var j = 1; j < n; ++j) {
3549                 for (i = 0; i < n - j; ++i) {
3550                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3551                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3552                 }
3553             }
3554
3555             return [ tmp[0][0], tmp[0][1] ];
3556
3557         };
3558     };/*
3559  * Portions of this file are based on pieces of Yahoo User Interface Library
3560  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3561  * YUI licensed under the BSD License:
3562  * http://developer.yahoo.net/yui/license.txt
3563  * <script type="text/javascript">
3564  *
3565  */
3566 (function() {
3567
3568     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3569         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3570     };
3571
3572     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3573
3574     var fly = Roo.lib.AnimBase.fly;
3575     var Y = Roo.lib;
3576     var superclass = Y.ColorAnim.superclass;
3577     var proto = Y.ColorAnim.prototype;
3578
3579     proto.toString = function() {
3580         var el = this.getEl();
3581         var id = el.id || el.tagName;
3582         return ("ColorAnim " + id);
3583     };
3584
3585     proto.patterns.color = /color$/i;
3586     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3587     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3588     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3589     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3590
3591
3592     proto.parseColor = function(s) {
3593         if (s.length == 3) {
3594             return s;
3595         }
3596
3597         var c = this.patterns.hex.exec(s);
3598         if (c && c.length == 4) {
3599             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3600         }
3601
3602         c = this.patterns.rgb.exec(s);
3603         if (c && c.length == 4) {
3604             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3605         }
3606
3607         c = this.patterns.hex3.exec(s);
3608         if (c && c.length == 4) {
3609             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3610         }
3611
3612         return null;
3613     };
3614     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3615     proto.getAttribute = function(attr) {
3616         var el = this.getEl();
3617         if (this.patterns.color.test(attr)) {
3618             var val = fly(el).getStyle(attr);
3619
3620             if (this.patterns.transparent.test(val)) {
3621                 var parent = el.parentNode;
3622                 val = fly(parent).getStyle(attr);
3623
3624                 while (parent && this.patterns.transparent.test(val)) {
3625                     parent = parent.parentNode;
3626                     val = fly(parent).getStyle(attr);
3627                     if (parent.tagName.toUpperCase() == 'HTML') {
3628                         val = '#fff';
3629                     }
3630                 }
3631             }
3632         } else {
3633             val = superclass.getAttribute.call(this, attr);
3634         }
3635
3636         return val;
3637     };
3638     proto.getAttribute = function(attr) {
3639         var el = this.getEl();
3640         if (this.patterns.color.test(attr)) {
3641             var val = fly(el).getStyle(attr);
3642
3643             if (this.patterns.transparent.test(val)) {
3644                 var parent = el.parentNode;
3645                 val = fly(parent).getStyle(attr);
3646
3647                 while (parent && this.patterns.transparent.test(val)) {
3648                     parent = parent.parentNode;
3649                     val = fly(parent).getStyle(attr);
3650                     if (parent.tagName.toUpperCase() == 'HTML') {
3651                         val = '#fff';
3652                     }
3653                 }
3654             }
3655         } else {
3656             val = superclass.getAttribute.call(this, attr);
3657         }
3658
3659         return val;
3660     };
3661
3662     proto.doMethod = function(attr, start, end) {
3663         var val;
3664
3665         if (this.patterns.color.test(attr)) {
3666             val = [];
3667             for (var i = 0, len = start.length; i < len; ++i) {
3668                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3669             }
3670
3671             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3672         }
3673         else {
3674             val = superclass.doMethod.call(this, attr, start, end);
3675         }
3676
3677         return val;
3678     };
3679
3680     proto.setRuntimeAttribute = function(attr) {
3681         superclass.setRuntimeAttribute.call(this, attr);
3682
3683         if (this.patterns.color.test(attr)) {
3684             var attributes = this.attributes;
3685             var start = this.parseColor(this.runtimeAttributes[attr].start);
3686             var end = this.parseColor(this.runtimeAttributes[attr].end);
3687
3688             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3689                 end = this.parseColor(attributes[attr].by);
3690
3691                 for (var i = 0, len = start.length; i < len; ++i) {
3692                     end[i] = start[i] + end[i];
3693                 }
3694             }
3695
3696             this.runtimeAttributes[attr].start = start;
3697             this.runtimeAttributes[attr].end = end;
3698         }
3699     };
3700 })();
3701
3702 /*
3703  * Portions of this file are based on pieces of Yahoo User Interface Library
3704  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3705  * YUI licensed under the BSD License:
3706  * http://developer.yahoo.net/yui/license.txt
3707  * <script type="text/javascript">
3708  *
3709  */
3710 Roo.lib.Easing = {
3711
3712
3713     easeNone: function (t, b, c, d) {
3714         return c * t / d + b;
3715     },
3716
3717
3718     easeIn: function (t, b, c, d) {
3719         return c * (t /= d) * t + b;
3720     },
3721
3722
3723     easeOut: function (t, b, c, d) {
3724         return -c * (t /= d) * (t - 2) + b;
3725     },
3726
3727
3728     easeBoth: function (t, b, c, d) {
3729         if ((t /= d / 2) < 1) {
3730             return c / 2 * t * t + b;
3731         }
3732
3733         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3734     },
3735
3736
3737     easeInStrong: function (t, b, c, d) {
3738         return c * (t /= d) * t * t * t + b;
3739     },
3740
3741
3742     easeOutStrong: function (t, b, c, d) {
3743         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3744     },
3745
3746
3747     easeBothStrong: function (t, b, c, d) {
3748         if ((t /= d / 2) < 1) {
3749             return c / 2 * t * t * t * t + b;
3750         }
3751
3752         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3753     },
3754
3755
3756
3757     elasticIn: function (t, b, c, d, a, p) {
3758         if (t == 0) {
3759             return b;
3760         }
3761         if ((t /= d) == 1) {
3762             return b + c;
3763         }
3764         if (!p) {
3765             p = d * .3;
3766         }
3767
3768         if (!a || a < Math.abs(c)) {
3769             a = c;
3770             var s = p / 4;
3771         }
3772         else {
3773             var s = p / (2 * Math.PI) * Math.asin(c / a);
3774         }
3775
3776         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3777     },
3778
3779
3780     elasticOut: function (t, b, c, d, a, p) {
3781         if (t == 0) {
3782             return b;
3783         }
3784         if ((t /= d) == 1) {
3785             return b + c;
3786         }
3787         if (!p) {
3788             p = d * .3;
3789         }
3790
3791         if (!a || a < Math.abs(c)) {
3792             a = c;
3793             var s = p / 4;
3794         }
3795         else {
3796             var s = p / (2 * Math.PI) * Math.asin(c / a);
3797         }
3798
3799         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3800     },
3801
3802
3803     elasticBoth: function (t, b, c, d, a, p) {
3804         if (t == 0) {
3805             return b;
3806         }
3807
3808         if ((t /= d / 2) == 2) {
3809             return b + c;
3810         }
3811
3812         if (!p) {
3813             p = d * (.3 * 1.5);
3814         }
3815
3816         if (!a || a < Math.abs(c)) {
3817             a = c;
3818             var s = p / 4;
3819         }
3820         else {
3821             var s = p / (2 * Math.PI) * Math.asin(c / a);
3822         }
3823
3824         if (t < 1) {
3825             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3826                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3827         }
3828         return a * Math.pow(2, -10 * (t -= 1)) *
3829                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3830     },
3831
3832
3833
3834     backIn: function (t, b, c, d, s) {
3835         if (typeof s == 'undefined') {
3836             s = 1.70158;
3837         }
3838         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3839     },
3840
3841
3842     backOut: function (t, b, c, d, s) {
3843         if (typeof s == 'undefined') {
3844             s = 1.70158;
3845         }
3846         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3847     },
3848
3849
3850     backBoth: function (t, b, c, d, s) {
3851         if (typeof s == 'undefined') {
3852             s = 1.70158;
3853         }
3854
3855         if ((t /= d / 2 ) < 1) {
3856             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3857         }
3858         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3859     },
3860
3861
3862     bounceIn: function (t, b, c, d) {
3863         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3864     },
3865
3866
3867     bounceOut: function (t, b, c, d) {
3868         if ((t /= d) < (1 / 2.75)) {
3869             return c * (7.5625 * t * t) + b;
3870         } else if (t < (2 / 2.75)) {
3871             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3872         } else if (t < (2.5 / 2.75)) {
3873             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3874         }
3875         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3876     },
3877
3878
3879     bounceBoth: function (t, b, c, d) {
3880         if (t < d / 2) {
3881             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3882         }
3883         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3884     }
3885 };/*
3886  * Portions of this file are based on pieces of Yahoo User Interface Library
3887  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3888  * YUI licensed under the BSD License:
3889  * http://developer.yahoo.net/yui/license.txt
3890  * <script type="text/javascript">
3891  *
3892  */
3893     (function() {
3894         Roo.lib.Motion = function(el, attributes, duration, method) {
3895             if (el) {
3896                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3897             }
3898         };
3899
3900         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3901
3902
3903         var Y = Roo.lib;
3904         var superclass = Y.Motion.superclass;
3905         var proto = Y.Motion.prototype;
3906
3907         proto.toString = function() {
3908             var el = this.getEl();
3909             var id = el.id || el.tagName;
3910             return ("Motion " + id);
3911         };
3912
3913         proto.patterns.points = /^points$/i;
3914
3915         proto.setAttribute = function(attr, val, unit) {
3916             if (this.patterns.points.test(attr)) {
3917                 unit = unit || 'px';
3918                 superclass.setAttribute.call(this, 'left', val[0], unit);
3919                 superclass.setAttribute.call(this, 'top', val[1], unit);
3920             } else {
3921                 superclass.setAttribute.call(this, attr, val, unit);
3922             }
3923         };
3924
3925         proto.getAttribute = function(attr) {
3926             if (this.patterns.points.test(attr)) {
3927                 var val = [
3928                         superclass.getAttribute.call(this, 'left'),
3929                         superclass.getAttribute.call(this, 'top')
3930                         ];
3931             } else {
3932                 val = superclass.getAttribute.call(this, attr);
3933             }
3934
3935             return val;
3936         };
3937
3938         proto.doMethod = function(attr, start, end) {
3939             var val = null;
3940
3941             if (this.patterns.points.test(attr)) {
3942                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3943                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3944             } else {
3945                 val = superclass.doMethod.call(this, attr, start, end);
3946             }
3947             return val;
3948         };
3949
3950         proto.setRuntimeAttribute = function(attr) {
3951             if (this.patterns.points.test(attr)) {
3952                 var el = this.getEl();
3953                 var attributes = this.attributes;
3954                 var start;
3955                 var control = attributes['points']['control'] || [];
3956                 var end;
3957                 var i, len;
3958
3959                 if (control.length > 0 && !(control[0] instanceof Array)) {
3960                     control = [control];
3961                 } else {
3962                     var tmp = [];
3963                     for (i = 0,len = control.length; i < len; ++i) {
3964                         tmp[i] = control[i];
3965                     }
3966                     control = tmp;
3967                 }
3968
3969                 Roo.fly(el).position();
3970
3971                 if (isset(attributes['points']['from'])) {
3972                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3973                 }
3974                 else {
3975                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3976                 }
3977
3978                 start = this.getAttribute('points');
3979
3980
3981                 if (isset(attributes['points']['to'])) {
3982                     end = translateValues.call(this, attributes['points']['to'], start);
3983
3984                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3985                     for (i = 0,len = control.length; i < len; ++i) {
3986                         control[i] = translateValues.call(this, control[i], start);
3987                     }
3988
3989
3990                 } else if (isset(attributes['points']['by'])) {
3991                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3992
3993                     for (i = 0,len = control.length; i < len; ++i) {
3994                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3995                     }
3996                 }
3997
3998                 this.runtimeAttributes[attr] = [start];
3999
4000                 if (control.length > 0) {
4001                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4002                 }
4003
4004                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4005             }
4006             else {
4007                 superclass.setRuntimeAttribute.call(this, attr);
4008             }
4009         };
4010
4011         var translateValues = function(val, start) {
4012             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4013             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4014
4015             return val;
4016         };
4017
4018         var isset = function(prop) {
4019             return (typeof prop !== 'undefined');
4020         };
4021     })();
4022 /*
4023  * Portions of this file are based on pieces of Yahoo User Interface Library
4024  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4025  * YUI licensed under the BSD License:
4026  * http://developer.yahoo.net/yui/license.txt
4027  * <script type="text/javascript">
4028  *
4029  */
4030     (function() {
4031         Roo.lib.Scroll = function(el, attributes, duration, method) {
4032             if (el) {
4033                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4034             }
4035         };
4036
4037         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4038
4039
4040         var Y = Roo.lib;
4041         var superclass = Y.Scroll.superclass;
4042         var proto = Y.Scroll.prototype;
4043
4044         proto.toString = function() {
4045             var el = this.getEl();
4046             var id = el.id || el.tagName;
4047             return ("Scroll " + id);
4048         };
4049
4050         proto.doMethod = function(attr, start, end) {
4051             var val = null;
4052
4053             if (attr == 'scroll') {
4054                 val = [
4055                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4056                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4057                         ];
4058
4059             } else {
4060                 val = superclass.doMethod.call(this, attr, start, end);
4061             }
4062             return val;
4063         };
4064
4065         proto.getAttribute = function(attr) {
4066             var val = null;
4067             var el = this.getEl();
4068
4069             if (attr == 'scroll') {
4070                 val = [ el.scrollLeft, el.scrollTop ];
4071             } else {
4072                 val = superclass.getAttribute.call(this, attr);
4073             }
4074
4075             return val;
4076         };
4077
4078         proto.setAttribute = function(attr, val, unit) {
4079             var el = this.getEl();
4080
4081             if (attr == 'scroll') {
4082                 el.scrollLeft = val[0];
4083                 el.scrollTop = val[1];
4084             } else {
4085                 superclass.setAttribute.call(this, attr, val, unit);
4086             }
4087         };
4088     })();
4089 /*
4090  * Based on:
4091  * Ext JS Library 1.1.1
4092  * Copyright(c) 2006-2007, Ext JS, LLC.
4093  *
4094  * Originally Released Under LGPL - original licence link has changed is not relivant.
4095  *
4096  * Fork - LGPL
4097  * <script type="text/javascript">
4098  */
4099
4100
4101 // nasty IE9 hack - what a pile of crap that is..
4102
4103  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4104     Range.prototype.createContextualFragment = function (html) {
4105         var doc = window.document;
4106         var container = doc.createElement("div");
4107         container.innerHTML = html;
4108         var frag = doc.createDocumentFragment(), n;
4109         while ((n = container.firstChild)) {
4110             frag.appendChild(n);
4111         }
4112         return frag;
4113     };
4114 }
4115
4116 /**
4117  * @class Roo.DomHelper
4118  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4119  * 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>.
4120  * @singleton
4121  */
4122 Roo.DomHelper = function(){
4123     var tempTableEl = null;
4124     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4125     var tableRe = /^table|tbody|tr|td$/i;
4126     var xmlns = {};
4127     // build as innerHTML where available
4128     /** @ignore */
4129     var createHtml = function(o){
4130         if(typeof o == 'string'){
4131             return o;
4132         }
4133         var b = "";
4134         if(!o.tag){
4135             o.tag = "div";
4136         }
4137         b += "<" + o.tag;
4138         for(var attr in o){
4139             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4140             if(attr == "style"){
4141                 var s = o["style"];
4142                 if(typeof s == "function"){
4143                     s = s.call();
4144                 }
4145                 if(typeof s == "string"){
4146                     b += ' style="' + s + '"';
4147                 }else if(typeof s == "object"){
4148                     b += ' style="';
4149                     for(var key in s){
4150                         if(typeof s[key] != "function"){
4151                             b += key + ":" + s[key] + ";";
4152                         }
4153                     }
4154                     b += '"';
4155                 }
4156             }else{
4157                 if(attr == "cls"){
4158                     b += ' class="' + o["cls"] + '"';
4159                 }else if(attr == "htmlFor"){
4160                     b += ' for="' + o["htmlFor"] + '"';
4161                 }else{
4162                     b += " " + attr + '="' + o[attr] + '"';
4163                 }
4164             }
4165         }
4166         if(emptyTags.test(o.tag)){
4167             b += "/>";
4168         }else{
4169             b += ">";
4170             var cn = o.children || o.cn;
4171             if(cn){
4172                 //http://bugs.kde.org/show_bug.cgi?id=71506
4173                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4174                     for(var i = 0, len = cn.length; i < len; i++) {
4175                         b += createHtml(cn[i], b);
4176                     }
4177                 }else{
4178                     b += createHtml(cn, b);
4179                 }
4180             }
4181             if(o.html){
4182                 b += o.html;
4183             }
4184             b += "</" + o.tag + ">";
4185         }
4186         return b;
4187     };
4188
4189     // build as dom
4190     /** @ignore */
4191     var createDom = function(o, parentNode){
4192          
4193         // defininition craeted..
4194         var ns = false;
4195         if (o.ns && o.ns != 'html') {
4196                
4197             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4198                 xmlns[o.ns] = o.xmlns;
4199                 ns = o.xmlns;
4200             }
4201             if (typeof(xmlns[o.ns]) == 'undefined') {
4202                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4203             }
4204             ns = xmlns[o.ns];
4205         }
4206         
4207         
4208         if (typeof(o) == 'string') {
4209             return parentNode.appendChild(document.createTextNode(o));
4210         }
4211         o.tag = o.tag || div;
4212         if (o.ns && Roo.isIE) {
4213             ns = false;
4214             o.tag = o.ns + ':' + o.tag;
4215             
4216         }
4217         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4218         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4219         for(var attr in o){
4220             
4221             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4222                     attr == "style" || typeof o[attr] == "function") continue;
4223                     
4224             if(attr=="cls" && Roo.isIE){
4225                 el.className = o["cls"];
4226             }else{
4227                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4228                 else el[attr] = o[attr];
4229             }
4230         }
4231         Roo.DomHelper.applyStyles(el, o.style);
4232         var cn = o.children || o.cn;
4233         if(cn){
4234             //http://bugs.kde.org/show_bug.cgi?id=71506
4235              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4236                 for(var i = 0, len = cn.length; i < len; i++) {
4237                     createDom(cn[i], el);
4238                 }
4239             }else{
4240                 createDom(cn, el);
4241             }
4242         }
4243         if(o.html){
4244             el.innerHTML = o.html;
4245         }
4246         if(parentNode){
4247            parentNode.appendChild(el);
4248         }
4249         return el;
4250     };
4251
4252     var ieTable = function(depth, s, h, e){
4253         tempTableEl.innerHTML = [s, h, e].join('');
4254         var i = -1, el = tempTableEl;
4255         while(++i < depth){
4256             el = el.firstChild;
4257         }
4258         return el;
4259     };
4260
4261     // kill repeat to save bytes
4262     var ts = '<table>',
4263         te = '</table>',
4264         tbs = ts+'<tbody>',
4265         tbe = '</tbody>'+te,
4266         trs = tbs + '<tr>',
4267         tre = '</tr>'+tbe;
4268
4269     /**
4270      * @ignore
4271      * Nasty code for IE's broken table implementation
4272      */
4273     var insertIntoTable = function(tag, where, el, html){
4274         if(!tempTableEl){
4275             tempTableEl = document.createElement('div');
4276         }
4277         var node;
4278         var before = null;
4279         if(tag == 'td'){
4280             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4281                 return;
4282             }
4283             if(where == 'beforebegin'){
4284                 before = el;
4285                 el = el.parentNode;
4286             } else{
4287                 before = el.nextSibling;
4288                 el = el.parentNode;
4289             }
4290             node = ieTable(4, trs, html, tre);
4291         }
4292         else if(tag == 'tr'){
4293             if(where == 'beforebegin'){
4294                 before = el;
4295                 el = el.parentNode;
4296                 node = ieTable(3, tbs, html, tbe);
4297             } else if(where == 'afterend'){
4298                 before = el.nextSibling;
4299                 el = el.parentNode;
4300                 node = ieTable(3, tbs, html, tbe);
4301             } else{ // INTO a TR
4302                 if(where == 'afterbegin'){
4303                     before = el.firstChild;
4304                 }
4305                 node = ieTable(4, trs, html, tre);
4306             }
4307         } else if(tag == 'tbody'){
4308             if(where == 'beforebegin'){
4309                 before = el;
4310                 el = el.parentNode;
4311                 node = ieTable(2, ts, html, te);
4312             } else if(where == 'afterend'){
4313                 before = el.nextSibling;
4314                 el = el.parentNode;
4315                 node = ieTable(2, ts, html, te);
4316             } else{
4317                 if(where == 'afterbegin'){
4318                     before = el.firstChild;
4319                 }
4320                 node = ieTable(3, tbs, html, tbe);
4321             }
4322         } else{ // TABLE
4323             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4324                 return;
4325             }
4326             if(where == 'afterbegin'){
4327                 before = el.firstChild;
4328             }
4329             node = ieTable(2, ts, html, te);
4330         }
4331         el.insertBefore(node, before);
4332         return node;
4333     };
4334
4335     return {
4336     /** True to force the use of DOM instead of html fragments @type Boolean */
4337     useDom : false,
4338
4339     /**
4340      * Returns the markup for the passed Element(s) config
4341      * @param {Object} o The Dom object spec (and children)
4342      * @return {String}
4343      */
4344     markup : function(o){
4345         return createHtml(o);
4346     },
4347
4348     /**
4349      * Applies a style specification to an element
4350      * @param {String/HTMLElement} el The element to apply styles to
4351      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4352      * a function which returns such a specification.
4353      */
4354     applyStyles : function(el, styles){
4355         if(styles){
4356            el = Roo.fly(el);
4357            if(typeof styles == "string"){
4358                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4359                var matches;
4360                while ((matches = re.exec(styles)) != null){
4361                    el.setStyle(matches[1], matches[2]);
4362                }
4363            }else if (typeof styles == "object"){
4364                for (var style in styles){
4365                   el.setStyle(style, styles[style]);
4366                }
4367            }else if (typeof styles == "function"){
4368                 Roo.DomHelper.applyStyles(el, styles.call());
4369            }
4370         }
4371     },
4372
4373     /**
4374      * Inserts an HTML fragment into the Dom
4375      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4376      * @param {HTMLElement} el The context element
4377      * @param {String} html The HTML fragmenet
4378      * @return {HTMLElement} The new node
4379      */
4380     insertHtml : function(where, el, html){
4381         where = where.toLowerCase();
4382         if(el.insertAdjacentHTML){
4383             if(tableRe.test(el.tagName)){
4384                 var rs;
4385                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4386                     return rs;
4387                 }
4388             }
4389             switch(where){
4390                 case "beforebegin":
4391                     el.insertAdjacentHTML('BeforeBegin', html);
4392                     return el.previousSibling;
4393                 case "afterbegin":
4394                     el.insertAdjacentHTML('AfterBegin', html);
4395                     return el.firstChild;
4396                 case "beforeend":
4397                     el.insertAdjacentHTML('BeforeEnd', html);
4398                     return el.lastChild;
4399                 case "afterend":
4400                     el.insertAdjacentHTML('AfterEnd', html);
4401                     return el.nextSibling;
4402             }
4403             throw 'Illegal insertion point -> "' + where + '"';
4404         }
4405         var range = el.ownerDocument.createRange();
4406         var frag;
4407         switch(where){
4408              case "beforebegin":
4409                 range.setStartBefore(el);
4410                 frag = range.createContextualFragment(html);
4411                 el.parentNode.insertBefore(frag, el);
4412                 return el.previousSibling;
4413              case "afterbegin":
4414                 if(el.firstChild){
4415                     range.setStartBefore(el.firstChild);
4416                     frag = range.createContextualFragment(html);
4417                     el.insertBefore(frag, el.firstChild);
4418                     return el.firstChild;
4419                 }else{
4420                     el.innerHTML = html;
4421                     return el.firstChild;
4422                 }
4423             case "beforeend":
4424                 if(el.lastChild){
4425                     range.setStartAfter(el.lastChild);
4426                     frag = range.createContextualFragment(html);
4427                     el.appendChild(frag);
4428                     return el.lastChild;
4429                 }else{
4430                     el.innerHTML = html;
4431                     return el.lastChild;
4432                 }
4433             case "afterend":
4434                 range.setStartAfter(el);
4435                 frag = range.createContextualFragment(html);
4436                 el.parentNode.insertBefore(frag, el.nextSibling);
4437                 return el.nextSibling;
4438             }
4439             throw 'Illegal insertion point -> "' + where + '"';
4440     },
4441
4442     /**
4443      * Creates new Dom element(s) and inserts them before el
4444      * @param {String/HTMLElement/Element} el The context element
4445      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4446      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4447      * @return {HTMLElement/Roo.Element} The new node
4448      */
4449     insertBefore : function(el, o, returnElement){
4450         return this.doInsert(el, o, returnElement, "beforeBegin");
4451     },
4452
4453     /**
4454      * Creates new Dom element(s) and inserts them after el
4455      * @param {String/HTMLElement/Element} el The context element
4456      * @param {Object} o The Dom object spec (and children)
4457      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4458      * @return {HTMLElement/Roo.Element} The new node
4459      */
4460     insertAfter : function(el, o, returnElement){
4461         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4462     },
4463
4464     /**
4465      * Creates new Dom element(s) and inserts them as the first child of el
4466      * @param {String/HTMLElement/Element} el The context element
4467      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4468      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4469      * @return {HTMLElement/Roo.Element} The new node
4470      */
4471     insertFirst : function(el, o, returnElement){
4472         return this.doInsert(el, o, returnElement, "afterBegin");
4473     },
4474
4475     // private
4476     doInsert : function(el, o, returnElement, pos, sibling){
4477         el = Roo.getDom(el);
4478         var newNode;
4479         if(this.useDom || o.ns){
4480             newNode = createDom(o, null);
4481             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4482         }else{
4483             var html = createHtml(o);
4484             newNode = this.insertHtml(pos, el, html);
4485         }
4486         return returnElement ? Roo.get(newNode, true) : newNode;
4487     },
4488
4489     /**
4490      * Creates new Dom element(s) and appends them to el
4491      * @param {String/HTMLElement/Element} el The context element
4492      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4493      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4494      * @return {HTMLElement/Roo.Element} The new node
4495      */
4496     append : function(el, o, returnElement){
4497         el = Roo.getDom(el);
4498         var newNode;
4499         if(this.useDom || o.ns){
4500             newNode = createDom(o, null);
4501             el.appendChild(newNode);
4502         }else{
4503             var html = createHtml(o);
4504             newNode = this.insertHtml("beforeEnd", el, html);
4505         }
4506         return returnElement ? Roo.get(newNode, true) : newNode;
4507     },
4508
4509     /**
4510      * Creates new Dom element(s) and overwrites the contents of el with them
4511      * @param {String/HTMLElement/Element} el The context element
4512      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4513      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4514      * @return {HTMLElement/Roo.Element} The new node
4515      */
4516     overwrite : function(el, o, returnElement){
4517         el = Roo.getDom(el);
4518         if (o.ns) {
4519           
4520             while (el.childNodes.length) {
4521                 el.removeChild(el.firstChild);
4522             }
4523             createDom(o, el);
4524         } else {
4525             el.innerHTML = createHtml(o);   
4526         }
4527         
4528         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4529     },
4530
4531     /**
4532      * Creates a new Roo.DomHelper.Template from the Dom object spec
4533      * @param {Object} o The Dom object spec (and children)
4534      * @return {Roo.DomHelper.Template} The new template
4535      */
4536     createTemplate : function(o){
4537         var html = createHtml(o);
4538         return new Roo.Template(html);
4539     }
4540     };
4541 }();
4542 /*
4543  * Based on:
4544  * Ext JS Library 1.1.1
4545  * Copyright(c) 2006-2007, Ext JS, LLC.
4546  *
4547  * Originally Released Under LGPL - original licence link has changed is not relivant.
4548  *
4549  * Fork - LGPL
4550  * <script type="text/javascript">
4551  */
4552  
4553 /**
4554 * @class Roo.Template
4555 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4556 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4557 * Usage:
4558 <pre><code>
4559 var t = new Roo.Template({
4560     html :  '&lt;div name="{id}"&gt;' + 
4561         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4562         '&lt;/div&gt;',
4563     myformat: function (value, allValues) {
4564         return 'XX' + value;
4565     }
4566 });
4567 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4568 </code></pre>
4569 * For more information see this blog post with examples:
4570 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4571      - Create Elements using DOM, HTML fragments and Templates</a>. 
4572 * @constructor
4573 * @param {Object} cfg - Configuration object.
4574 */
4575 Roo.Template = function(cfg){
4576     // BC!
4577     if(cfg instanceof Array){
4578         cfg = cfg.join("");
4579     }else if(arguments.length > 1){
4580         cfg = Array.prototype.join.call(arguments, "");
4581     }
4582     
4583     
4584     if (typeof(cfg) == 'object') {
4585         Roo.apply(this,cfg)
4586     } else {
4587         // bc
4588         this.html = cfg;
4589     }
4590     if (this.url) {
4591         this.load();
4592     }
4593     
4594 };
4595 Roo.Template.prototype = {
4596     
4597     /**
4598      * @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..
4599      *                    it should be fixed so that template is observable...
4600      */
4601     url : false,
4602     /**
4603      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4604      */
4605     html : '',
4606     /**
4607      * Returns an HTML fragment of this template with the specified values applied.
4608      * @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'})
4609      * @return {String} The HTML fragment
4610      */
4611     applyTemplate : function(values){
4612         try {
4613            
4614             if(this.compiled){
4615                 return this.compiled(values);
4616             }
4617             var useF = this.disableFormats !== true;
4618             var fm = Roo.util.Format, tpl = this;
4619             var fn = function(m, name, format, args){
4620                 if(format && useF){
4621                     if(format.substr(0, 5) == "this."){
4622                         return tpl.call(format.substr(5), values[name], values);
4623                     }else{
4624                         if(args){
4625                             // quoted values are required for strings in compiled templates, 
4626                             // but for non compiled we need to strip them
4627                             // quoted reversed for jsmin
4628                             var re = /^\s*['"](.*)["']\s*$/;
4629                             args = args.split(',');
4630                             for(var i = 0, len = args.length; i < len; i++){
4631                                 args[i] = args[i].replace(re, "$1");
4632                             }
4633                             args = [values[name]].concat(args);
4634                         }else{
4635                             args = [values[name]];
4636                         }
4637                         return fm[format].apply(fm, args);
4638                     }
4639                 }else{
4640                     return values[name] !== undefined ? values[name] : "";
4641                 }
4642             };
4643             return this.html.replace(this.re, fn);
4644         } catch (e) {
4645             Roo.log(e);
4646             throw e;
4647         }
4648          
4649     },
4650     
4651     loading : false,
4652       
4653     load : function ()
4654     {
4655          
4656         if (this.loading) {
4657             return;
4658         }
4659         var _t = this;
4660         
4661         this.loading = true;
4662         this.compiled = false;
4663         
4664         var cx = new Roo.data.Connection();
4665         cx.request({
4666             url : this.url,
4667             method : 'GET',
4668             success : function (response) {
4669                 _t.loading = false;
4670                 _t.html = response.responseText;
4671                 _t.url = false;
4672                 _t.compile();
4673              },
4674             failure : function(response) {
4675                 Roo.log("Template failed to load from " + _t.url);
4676                 _t.loading = false;
4677             }
4678         });
4679     },
4680
4681     /**
4682      * Sets the HTML used as the template and optionally compiles it.
4683      * @param {String} html
4684      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4685      * @return {Roo.Template} this
4686      */
4687     set : function(html, compile){
4688         this.html = html;
4689         this.compiled = null;
4690         if(compile){
4691             this.compile();
4692         }
4693         return this;
4694     },
4695     
4696     /**
4697      * True to disable format functions (defaults to false)
4698      * @type Boolean
4699      */
4700     disableFormats : false,
4701     
4702     /**
4703     * The regular expression used to match template variables 
4704     * @type RegExp
4705     * @property 
4706     */
4707     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4708     
4709     /**
4710      * Compiles the template into an internal function, eliminating the RegEx overhead.
4711      * @return {Roo.Template} this
4712      */
4713     compile : function(){
4714         var fm = Roo.util.Format;
4715         var useF = this.disableFormats !== true;
4716         var sep = Roo.isGecko ? "+" : ",";
4717         var fn = function(m, name, format, args){
4718             if(format && useF){
4719                 args = args ? ',' + args : "";
4720                 if(format.substr(0, 5) != "this."){
4721                     format = "fm." + format + '(';
4722                 }else{
4723                     format = 'this.call("'+ format.substr(5) + '", ';
4724                     args = ", values";
4725                 }
4726             }else{
4727                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4728             }
4729             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4730         };
4731         var body;
4732         // branched to use + in gecko and [].join() in others
4733         if(Roo.isGecko){
4734             body = "this.compiled = function(values){ return '" +
4735                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4736                     "';};";
4737         }else{
4738             body = ["this.compiled = function(values){ return ['"];
4739             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4740             body.push("'].join('');};");
4741             body = body.join('');
4742         }
4743         /**
4744          * eval:var:values
4745          * eval:var:fm
4746          */
4747         eval(body);
4748         return this;
4749     },
4750     
4751     // private function used to call members
4752     call : function(fnName, value, allValues){
4753         return this[fnName](value, allValues);
4754     },
4755     
4756     /**
4757      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4758      * @param {String/HTMLElement/Roo.Element} el The context element
4759      * @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'})
4760      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4761      * @return {HTMLElement/Roo.Element} The new node or Element
4762      */
4763     insertFirst: function(el, values, returnElement){
4764         return this.doInsert('afterBegin', el, values, returnElement);
4765     },
4766
4767     /**
4768      * Applies the supplied values to the template and inserts the new node(s) before el.
4769      * @param {String/HTMLElement/Roo.Element} el The context element
4770      * @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'})
4771      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4772      * @return {HTMLElement/Roo.Element} The new node or Element
4773      */
4774     insertBefore: function(el, values, returnElement){
4775         return this.doInsert('beforeBegin', el, values, returnElement);
4776     },
4777
4778     /**
4779      * Applies the supplied values to the template and inserts the new node(s) after el.
4780      * @param {String/HTMLElement/Roo.Element} el The context element
4781      * @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'})
4782      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4783      * @return {HTMLElement/Roo.Element} The new node or Element
4784      */
4785     insertAfter : function(el, values, returnElement){
4786         return this.doInsert('afterEnd', el, values, returnElement);
4787     },
4788     
4789     /**
4790      * Applies the supplied values to the template and appends the new node(s) to el.
4791      * @param {String/HTMLElement/Roo.Element} el The context element
4792      * @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'})
4793      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4794      * @return {HTMLElement/Roo.Element} The new node or Element
4795      */
4796     append : function(el, values, returnElement){
4797         return this.doInsert('beforeEnd', el, values, returnElement);
4798     },
4799
4800     doInsert : function(where, el, values, returnEl){
4801         el = Roo.getDom(el);
4802         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4803         return returnEl ? Roo.get(newNode, true) : newNode;
4804     },
4805
4806     /**
4807      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4808      * @param {String/HTMLElement/Roo.Element} el The context element
4809      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4810      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4811      * @return {HTMLElement/Roo.Element} The new node or Element
4812      */
4813     overwrite : function(el, values, returnElement){
4814         el = Roo.getDom(el);
4815         el.innerHTML = this.applyTemplate(values);
4816         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4817     }
4818 };
4819 /**
4820  * Alias for {@link #applyTemplate}
4821  * @method
4822  */
4823 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4824
4825 // backwards compat
4826 Roo.DomHelper.Template = Roo.Template;
4827
4828 /**
4829  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4830  * @param {String/HTMLElement} el A DOM element or its id
4831  * @returns {Roo.Template} The created template
4832  * @static
4833  */
4834 Roo.Template.from = function(el){
4835     el = Roo.getDom(el);
4836     return new Roo.Template(el.value || el.innerHTML);
4837 };/*
4838  * Based on:
4839  * Ext JS Library 1.1.1
4840  * Copyright(c) 2006-2007, Ext JS, LLC.
4841  *
4842  * Originally Released Under LGPL - original licence link has changed is not relivant.
4843  *
4844  * Fork - LGPL
4845  * <script type="text/javascript">
4846  */
4847  
4848
4849 /*
4850  * This is code is also distributed under MIT license for use
4851  * with jQuery and prototype JavaScript libraries.
4852  */
4853 /**
4854  * @class Roo.DomQuery
4855 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).
4856 <p>
4857 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>
4858
4859 <p>
4860 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.
4861 </p>
4862 <h4>Element Selectors:</h4>
4863 <ul class="list">
4864     <li> <b>*</b> any element</li>
4865     <li> <b>E</b> an element with the tag E</li>
4866     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4867     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4868     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4869     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4870 </ul>
4871 <h4>Attribute Selectors:</h4>
4872 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4873 <ul class="list">
4874     <li> <b>E[foo]</b> has an attribute "foo"</li>
4875     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4876     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4877     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4878     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4879     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4880     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4881 </ul>
4882 <h4>Pseudo Classes:</h4>
4883 <ul class="list">
4884     <li> <b>E:first-child</b> E is the first child of its parent</li>
4885     <li> <b>E:last-child</b> E is the last child of its parent</li>
4886     <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>
4887     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4888     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4889     <li> <b>E:only-child</b> E is the only child of its parent</li>
4890     <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>
4891     <li> <b>E:first</b> the first E in the resultset</li>
4892     <li> <b>E:last</b> the last E in the resultset</li>
4893     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4894     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4895     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4896     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4897     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4898     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4899     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4900     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4901     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4902 </ul>
4903 <h4>CSS Value Selectors:</h4>
4904 <ul class="list">
4905     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4906     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4907     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4908     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4909     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4910     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4911 </ul>
4912  * @singleton
4913  */
4914 Roo.DomQuery = function(){
4915     var cache = {}, simpleCache = {}, valueCache = {};
4916     var nonSpace = /\S/;
4917     var trimRe = /^\s+|\s+$/g;
4918     var tplRe = /\{(\d+)\}/g;
4919     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4920     var tagTokenRe = /^(#)?([\w-\*]+)/;
4921     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4922
4923     function child(p, index){
4924         var i = 0;
4925         var n = p.firstChild;
4926         while(n){
4927             if(n.nodeType == 1){
4928                if(++i == index){
4929                    return n;
4930                }
4931             }
4932             n = n.nextSibling;
4933         }
4934         return null;
4935     };
4936
4937     function next(n){
4938         while((n = n.nextSibling) && n.nodeType != 1);
4939         return n;
4940     };
4941
4942     function prev(n){
4943         while((n = n.previousSibling) && n.nodeType != 1);
4944         return n;
4945     };
4946
4947     function children(d){
4948         var n = d.firstChild, ni = -1;
4949             while(n){
4950                 var nx = n.nextSibling;
4951                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4952                     d.removeChild(n);
4953                 }else{
4954                     n.nodeIndex = ++ni;
4955                 }
4956                 n = nx;
4957             }
4958             return this;
4959         };
4960
4961     function byClassName(c, a, v){
4962         if(!v){
4963             return c;
4964         }
4965         var r = [], ri = -1, cn;
4966         for(var i = 0, ci; ci = c[i]; i++){
4967             if((' '+ci.className+' ').indexOf(v) != -1){
4968                 r[++ri] = ci;
4969             }
4970         }
4971         return r;
4972     };
4973
4974     function attrValue(n, attr){
4975         if(!n.tagName && typeof n.length != "undefined"){
4976             n = n[0];
4977         }
4978         if(!n){
4979             return null;
4980         }
4981         if(attr == "for"){
4982             return n.htmlFor;
4983         }
4984         if(attr == "class" || attr == "className"){
4985             return n.className;
4986         }
4987         return n.getAttribute(attr) || n[attr];
4988
4989     };
4990
4991     function getNodes(ns, mode, tagName){
4992         var result = [], ri = -1, cs;
4993         if(!ns){
4994             return result;
4995         }
4996         tagName = tagName || "*";
4997         if(typeof ns.getElementsByTagName != "undefined"){
4998             ns = [ns];
4999         }
5000         if(!mode){
5001             for(var i = 0, ni; ni = ns[i]; i++){
5002                 cs = ni.getElementsByTagName(tagName);
5003                 for(var j = 0, ci; ci = cs[j]; j++){
5004                     result[++ri] = ci;
5005                 }
5006             }
5007         }else if(mode == "/" || mode == ">"){
5008             var utag = tagName.toUpperCase();
5009             for(var i = 0, ni, cn; ni = ns[i]; i++){
5010                 cn = ni.children || ni.childNodes;
5011                 for(var j = 0, cj; cj = cn[j]; j++){
5012                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5013                         result[++ri] = cj;
5014                     }
5015                 }
5016             }
5017         }else if(mode == "+"){
5018             var utag = tagName.toUpperCase();
5019             for(var i = 0, n; n = ns[i]; i++){
5020                 while((n = n.nextSibling) && n.nodeType != 1);
5021                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5022                     result[++ri] = n;
5023                 }
5024             }
5025         }else if(mode == "~"){
5026             for(var i = 0, n; n = ns[i]; i++){
5027                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5028                 if(n){
5029                     result[++ri] = n;
5030                 }
5031             }
5032         }
5033         return result;
5034     };
5035
5036     function concat(a, b){
5037         if(b.slice){
5038             return a.concat(b);
5039         }
5040         for(var i = 0, l = b.length; i < l; i++){
5041             a[a.length] = b[i];
5042         }
5043         return a;
5044     }
5045
5046     function byTag(cs, tagName){
5047         if(cs.tagName || cs == document){
5048             cs = [cs];
5049         }
5050         if(!tagName){
5051             return cs;
5052         }
5053         var r = [], ri = -1;
5054         tagName = tagName.toLowerCase();
5055         for(var i = 0, ci; ci = cs[i]; i++){
5056             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5057                 r[++ri] = ci;
5058             }
5059         }
5060         return r;
5061     };
5062
5063     function byId(cs, attr, id){
5064         if(cs.tagName || cs == document){
5065             cs = [cs];
5066         }
5067         if(!id){
5068             return cs;
5069         }
5070         var r = [], ri = -1;
5071         for(var i = 0,ci; ci = cs[i]; i++){
5072             if(ci && ci.id == id){
5073                 r[++ri] = ci;
5074                 return r;
5075             }
5076         }
5077         return r;
5078     };
5079
5080     function byAttribute(cs, attr, value, op, custom){
5081         var r = [], ri = -1, st = custom=="{";
5082         var f = Roo.DomQuery.operators[op];
5083         for(var i = 0, ci; ci = cs[i]; i++){
5084             var a;
5085             if(st){
5086                 a = Roo.DomQuery.getStyle(ci, attr);
5087             }
5088             else if(attr == "class" || attr == "className"){
5089                 a = ci.className;
5090             }else if(attr == "for"){
5091                 a = ci.htmlFor;
5092             }else if(attr == "href"){
5093                 a = ci.getAttribute("href", 2);
5094             }else{
5095                 a = ci.getAttribute(attr);
5096             }
5097             if((f && f(a, value)) || (!f && a)){
5098                 r[++ri] = ci;
5099             }
5100         }
5101         return r;
5102     };
5103
5104     function byPseudo(cs, name, value){
5105         return Roo.DomQuery.pseudos[name](cs, value);
5106     };
5107
5108     // This is for IE MSXML which does not support expandos.
5109     // IE runs the same speed using setAttribute, however FF slows way down
5110     // and Safari completely fails so they need to continue to use expandos.
5111     var isIE = window.ActiveXObject ? true : false;
5112
5113     // this eval is stop the compressor from
5114     // renaming the variable to something shorter
5115     
5116     /** eval:var:batch */
5117     var batch = 30803; 
5118
5119     var key = 30803;
5120
5121     function nodupIEXml(cs){
5122         var d = ++key;
5123         cs[0].setAttribute("_nodup", d);
5124         var r = [cs[0]];
5125         for(var i = 1, len = cs.length; i < len; i++){
5126             var c = cs[i];
5127             if(!c.getAttribute("_nodup") != d){
5128                 c.setAttribute("_nodup", d);
5129                 r[r.length] = c;
5130             }
5131         }
5132         for(var i = 0, len = cs.length; i < len; i++){
5133             cs[i].removeAttribute("_nodup");
5134         }
5135         return r;
5136     }
5137
5138     function nodup(cs){
5139         if(!cs){
5140             return [];
5141         }
5142         var len = cs.length, c, i, r = cs, cj, ri = -1;
5143         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5144             return cs;
5145         }
5146         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5147             return nodupIEXml(cs);
5148         }
5149         var d = ++key;
5150         cs[0]._nodup = d;
5151         for(i = 1; c = cs[i]; i++){
5152             if(c._nodup != d){
5153                 c._nodup = d;
5154             }else{
5155                 r = [];
5156                 for(var j = 0; j < i; j++){
5157                     r[++ri] = cs[j];
5158                 }
5159                 for(j = i+1; cj = cs[j]; j++){
5160                     if(cj._nodup != d){
5161                         cj._nodup = d;
5162                         r[++ri] = cj;
5163                     }
5164                 }
5165                 return r;
5166             }
5167         }
5168         return r;
5169     }
5170
5171     function quickDiffIEXml(c1, c2){
5172         var d = ++key;
5173         for(var i = 0, len = c1.length; i < len; i++){
5174             c1[i].setAttribute("_qdiff", d);
5175         }
5176         var r = [];
5177         for(var i = 0, len = c2.length; i < len; i++){
5178             if(c2[i].getAttribute("_qdiff") != d){
5179                 r[r.length] = c2[i];
5180             }
5181         }
5182         for(var i = 0, len = c1.length; i < len; i++){
5183            c1[i].removeAttribute("_qdiff");
5184         }
5185         return r;
5186     }
5187
5188     function quickDiff(c1, c2){
5189         var len1 = c1.length;
5190         if(!len1){
5191             return c2;
5192         }
5193         if(isIE && c1[0].selectSingleNode){
5194             return quickDiffIEXml(c1, c2);
5195         }
5196         var d = ++key;
5197         for(var i = 0; i < len1; i++){
5198             c1[i]._qdiff = d;
5199         }
5200         var r = [];
5201         for(var i = 0, len = c2.length; i < len; i++){
5202             if(c2[i]._qdiff != d){
5203                 r[r.length] = c2[i];
5204             }
5205         }
5206         return r;
5207     }
5208
5209     function quickId(ns, mode, root, id){
5210         if(ns == root){
5211            var d = root.ownerDocument || root;
5212            return d.getElementById(id);
5213         }
5214         ns = getNodes(ns, mode, "*");
5215         return byId(ns, null, id);
5216     }
5217
5218     return {
5219         getStyle : function(el, name){
5220             return Roo.fly(el).getStyle(name);
5221         },
5222         /**
5223          * Compiles a selector/xpath query into a reusable function. The returned function
5224          * takes one parameter "root" (optional), which is the context node from where the query should start.
5225          * @param {String} selector The selector/xpath query
5226          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5227          * @return {Function}
5228          */
5229         compile : function(path, type){
5230             type = type || "select";
5231             
5232             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5233             var q = path, mode, lq;
5234             var tk = Roo.DomQuery.matchers;
5235             var tklen = tk.length;
5236             var mm;
5237
5238             // accept leading mode switch
5239             var lmode = q.match(modeRe);
5240             if(lmode && lmode[1]){
5241                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5242                 q = q.replace(lmode[1], "");
5243             }
5244             // strip leading slashes
5245             while(path.substr(0, 1)=="/"){
5246                 path = path.substr(1);
5247             }
5248
5249             while(q && lq != q){
5250                 lq = q;
5251                 var tm = q.match(tagTokenRe);
5252                 if(type == "select"){
5253                     if(tm){
5254                         if(tm[1] == "#"){
5255                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5256                         }else{
5257                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5258                         }
5259                         q = q.replace(tm[0], "");
5260                     }else if(q.substr(0, 1) != '@'){
5261                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5262                     }
5263                 }else{
5264                     if(tm){
5265                         if(tm[1] == "#"){
5266                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5267                         }else{
5268                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5269                         }
5270                         q = q.replace(tm[0], "");
5271                     }
5272                 }
5273                 while(!(mm = q.match(modeRe))){
5274                     var matched = false;
5275                     for(var j = 0; j < tklen; j++){
5276                         var t = tk[j];
5277                         var m = q.match(t.re);
5278                         if(m){
5279                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5280                                                     return m[i];
5281                                                 });
5282                             q = q.replace(m[0], "");
5283                             matched = true;
5284                             break;
5285                         }
5286                     }
5287                     // prevent infinite loop on bad selector
5288                     if(!matched){
5289                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5290                     }
5291                 }
5292                 if(mm[1]){
5293                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5294                     q = q.replace(mm[1], "");
5295                 }
5296             }
5297             fn[fn.length] = "return nodup(n);\n}";
5298             
5299              /** 
5300               * list of variables that need from compression as they are used by eval.
5301              *  eval:var:batch 
5302              *  eval:var:nodup
5303              *  eval:var:byTag
5304              *  eval:var:ById
5305              *  eval:var:getNodes
5306              *  eval:var:quickId
5307              *  eval:var:mode
5308              *  eval:var:root
5309              *  eval:var:n
5310              *  eval:var:byClassName
5311              *  eval:var:byPseudo
5312              *  eval:var:byAttribute
5313              *  eval:var:attrValue
5314              * 
5315              **/ 
5316             eval(fn.join(""));
5317             return f;
5318         },
5319
5320         /**
5321          * Selects a group of elements.
5322          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5323          * @param {Node} root (optional) The start of the query (defaults to document).
5324          * @return {Array}
5325          */
5326         select : function(path, root, type){
5327             if(!root || root == document){
5328                 root = document;
5329             }
5330             if(typeof root == "string"){
5331                 root = document.getElementById(root);
5332             }
5333             var paths = path.split(",");
5334             var results = [];
5335             for(var i = 0, len = paths.length; i < len; i++){
5336                 var p = paths[i].replace(trimRe, "");
5337                 if(!cache[p]){
5338                     cache[p] = Roo.DomQuery.compile(p);
5339                     if(!cache[p]){
5340                         throw p + " is not a valid selector";
5341                     }
5342                 }
5343                 var result = cache[p](root);
5344                 if(result && result != document){
5345                     results = results.concat(result);
5346                 }
5347             }
5348             if(paths.length > 1){
5349                 return nodup(results);
5350             }
5351             return results;
5352         },
5353
5354         /**
5355          * Selects a single element.
5356          * @param {String} selector The selector/xpath query
5357          * @param {Node} root (optional) The start of the query (defaults to document).
5358          * @return {Element}
5359          */
5360         selectNode : function(path, root){
5361             return Roo.DomQuery.select(path, root)[0];
5362         },
5363
5364         /**
5365          * Selects the value of a node, optionally replacing null with the defaultValue.
5366          * @param {String} selector The selector/xpath query
5367          * @param {Node} root (optional) The start of the query (defaults to document).
5368          * @param {String} defaultValue
5369          */
5370         selectValue : function(path, root, defaultValue){
5371             path = path.replace(trimRe, "");
5372             if(!valueCache[path]){
5373                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5374             }
5375             var n = valueCache[path](root);
5376             n = n[0] ? n[0] : n;
5377             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5378             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5379         },
5380
5381         /**
5382          * Selects the value of a node, parsing integers and floats.
5383          * @param {String} selector The selector/xpath query
5384          * @param {Node} root (optional) The start of the query (defaults to document).
5385          * @param {Number} defaultValue
5386          * @return {Number}
5387          */
5388         selectNumber : function(path, root, defaultValue){
5389             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5390             return parseFloat(v);
5391         },
5392
5393         /**
5394          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5395          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5396          * @param {String} selector The simple selector to test
5397          * @return {Boolean}
5398          */
5399         is : function(el, ss){
5400             if(typeof el == "string"){
5401                 el = document.getElementById(el);
5402             }
5403             var isArray = (el instanceof Array);
5404             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5405             return isArray ? (result.length == el.length) : (result.length > 0);
5406         },
5407
5408         /**
5409          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5410          * @param {Array} el An array of elements to filter
5411          * @param {String} selector The simple selector to test
5412          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5413          * the selector instead of the ones that match
5414          * @return {Array}
5415          */
5416         filter : function(els, ss, nonMatches){
5417             ss = ss.replace(trimRe, "");
5418             if(!simpleCache[ss]){
5419                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5420             }
5421             var result = simpleCache[ss](els);
5422             return nonMatches ? quickDiff(result, els) : result;
5423         },
5424
5425         /**
5426          * Collection of matching regular expressions and code snippets.
5427          */
5428         matchers : [{
5429                 re: /^\.([\w-]+)/,
5430                 select: 'n = byClassName(n, null, " {1} ");'
5431             }, {
5432                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5433                 select: 'n = byPseudo(n, "{1}", "{2}");'
5434             },{
5435                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5436                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5437             }, {
5438                 re: /^#([\w-]+)/,
5439                 select: 'n = byId(n, null, "{1}");'
5440             },{
5441                 re: /^@([\w-]+)/,
5442                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5443             }
5444         ],
5445
5446         /**
5447          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5448          * 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;.
5449          */
5450         operators : {
5451             "=" : function(a, v){
5452                 return a == v;
5453             },
5454             "!=" : function(a, v){
5455                 return a != v;
5456             },
5457             "^=" : function(a, v){
5458                 return a && a.substr(0, v.length) == v;
5459             },
5460             "$=" : function(a, v){
5461                 return a && a.substr(a.length-v.length) == v;
5462             },
5463             "*=" : function(a, v){
5464                 return a && a.indexOf(v) !== -1;
5465             },
5466             "%=" : function(a, v){
5467                 return (a % v) == 0;
5468             },
5469             "|=" : function(a, v){
5470                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5471             },
5472             "~=" : function(a, v){
5473                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5474             }
5475         },
5476
5477         /**
5478          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5479          * and the argument (if any) supplied in the selector.
5480          */
5481         pseudos : {
5482             "first-child" : function(c){
5483                 var r = [], ri = -1, n;
5484                 for(var i = 0, ci; ci = n = c[i]; i++){
5485                     while((n = n.previousSibling) && n.nodeType != 1);
5486                     if(!n){
5487                         r[++ri] = ci;
5488                     }
5489                 }
5490                 return r;
5491             },
5492
5493             "last-child" : function(c){
5494                 var r = [], ri = -1, n;
5495                 for(var i = 0, ci; ci = n = c[i]; i++){
5496                     while((n = n.nextSibling) && n.nodeType != 1);
5497                     if(!n){
5498                         r[++ri] = ci;
5499                     }
5500                 }
5501                 return r;
5502             },
5503
5504             "nth-child" : function(c, a) {
5505                 var r = [], ri = -1;
5506                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5507                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5508                 for(var i = 0, n; n = c[i]; i++){
5509                     var pn = n.parentNode;
5510                     if (batch != pn._batch) {
5511                         var j = 0;
5512                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5513                             if(cn.nodeType == 1){
5514                                cn.nodeIndex = ++j;
5515                             }
5516                         }
5517                         pn._batch = batch;
5518                     }
5519                     if (f == 1) {
5520                         if (l == 0 || n.nodeIndex == l){
5521                             r[++ri] = n;
5522                         }
5523                     } else if ((n.nodeIndex + l) % f == 0){
5524                         r[++ri] = n;
5525                     }
5526                 }
5527
5528                 return r;
5529             },
5530
5531             "only-child" : function(c){
5532                 var r = [], ri = -1;;
5533                 for(var i = 0, ci; ci = c[i]; i++){
5534                     if(!prev(ci) && !next(ci)){
5535                         r[++ri] = ci;
5536                     }
5537                 }
5538                 return r;
5539             },
5540
5541             "empty" : function(c){
5542                 var r = [], ri = -1;
5543                 for(var i = 0, ci; ci = c[i]; i++){
5544                     var cns = ci.childNodes, j = 0, cn, empty = true;
5545                     while(cn = cns[j]){
5546                         ++j;
5547                         if(cn.nodeType == 1 || cn.nodeType == 3){
5548                             empty = false;
5549                             break;
5550                         }
5551                     }
5552                     if(empty){
5553                         r[++ri] = ci;
5554                     }
5555                 }
5556                 return r;
5557             },
5558
5559             "contains" : function(c, v){
5560                 var r = [], ri = -1;
5561                 for(var i = 0, ci; ci = c[i]; i++){
5562                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5563                         r[++ri] = ci;
5564                     }
5565                 }
5566                 return r;
5567             },
5568
5569             "nodeValue" : function(c, v){
5570                 var r = [], ri = -1;
5571                 for(var i = 0, ci; ci = c[i]; i++){
5572                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5573                         r[++ri] = ci;
5574                     }
5575                 }
5576                 return r;
5577             },
5578
5579             "checked" : function(c){
5580                 var r = [], ri = -1;
5581                 for(var i = 0, ci; ci = c[i]; i++){
5582                     if(ci.checked == true){
5583                         r[++ri] = ci;
5584                     }
5585                 }
5586                 return r;
5587             },
5588
5589             "not" : function(c, ss){
5590                 return Roo.DomQuery.filter(c, ss, true);
5591             },
5592
5593             "odd" : function(c){
5594                 return this["nth-child"](c, "odd");
5595             },
5596
5597             "even" : function(c){
5598                 return this["nth-child"](c, "even");
5599             },
5600
5601             "nth" : function(c, a){
5602                 return c[a-1] || [];
5603             },
5604
5605             "first" : function(c){
5606                 return c[0] || [];
5607             },
5608
5609             "last" : function(c){
5610                 return c[c.length-1] || [];
5611             },
5612
5613             "has" : function(c, ss){
5614                 var s = Roo.DomQuery.select;
5615                 var r = [], ri = -1;
5616                 for(var i = 0, ci; ci = c[i]; i++){
5617                     if(s(ss, ci).length > 0){
5618                         r[++ri] = ci;
5619                     }
5620                 }
5621                 return r;
5622             },
5623
5624             "next" : function(c, ss){
5625                 var is = Roo.DomQuery.is;
5626                 var r = [], ri = -1;
5627                 for(var i = 0, ci; ci = c[i]; i++){
5628                     var n = next(ci);
5629                     if(n && is(n, ss)){
5630                         r[++ri] = ci;
5631                     }
5632                 }
5633                 return r;
5634             },
5635
5636             "prev" : function(c, ss){
5637                 var is = Roo.DomQuery.is;
5638                 var r = [], ri = -1;
5639                 for(var i = 0, ci; ci = c[i]; i++){
5640                     var n = prev(ci);
5641                     if(n && is(n, ss)){
5642                         r[++ri] = ci;
5643                     }
5644                 }
5645                 return r;
5646             }
5647         }
5648     };
5649 }();
5650
5651 /**
5652  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5653  * @param {String} path The selector/xpath query
5654  * @param {Node} root (optional) The start of the query (defaults to document).
5655  * @return {Array}
5656  * @member Roo
5657  * @method query
5658  */
5659 Roo.query = Roo.DomQuery.select;
5660 /*
5661  * Based on:
5662  * Ext JS Library 1.1.1
5663  * Copyright(c) 2006-2007, Ext JS, LLC.
5664  *
5665  * Originally Released Under LGPL - original licence link has changed is not relivant.
5666  *
5667  * Fork - LGPL
5668  * <script type="text/javascript">
5669  */
5670
5671 /**
5672  * @class Roo.util.Observable
5673  * Base class that provides a common interface for publishing events. Subclasses are expected to
5674  * to have a property "events" with all the events defined.<br>
5675  * For example:
5676  * <pre><code>
5677  Employee = function(name){
5678     this.name = name;
5679     this.addEvents({
5680         "fired" : true,
5681         "quit" : true
5682     });
5683  }
5684  Roo.extend(Employee, Roo.util.Observable);
5685 </code></pre>
5686  * @param {Object} config properties to use (incuding events / listeners)
5687  */
5688
5689 Roo.util.Observable = function(cfg){
5690     
5691     cfg = cfg|| {};
5692     this.addEvents(cfg.events || {});
5693     if (cfg.events) {
5694         delete cfg.events; // make sure
5695     }
5696      
5697     Roo.apply(this, cfg);
5698     
5699     if(this.listeners){
5700         this.on(this.listeners);
5701         delete this.listeners;
5702     }
5703 };
5704 Roo.util.Observable.prototype = {
5705     /** 
5706  * @cfg {Object} listeners  list of events and functions to call for this object, 
5707  * For example :
5708  * <pre><code>
5709     listeners :  { 
5710        'click' : function(e) {
5711            ..... 
5712         } ,
5713         .... 
5714     } 
5715   </code></pre>
5716  */
5717     
5718     
5719     /**
5720      * Fires the specified event with the passed parameters (minus the event name).
5721      * @param {String} eventName
5722      * @param {Object...} args Variable number of parameters are passed to handlers
5723      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5724      */
5725     fireEvent : function(){
5726         var ce = this.events[arguments[0].toLowerCase()];
5727         if(typeof ce == "object"){
5728             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5729         }else{
5730             return true;
5731         }
5732     },
5733
5734     // private
5735     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5736
5737     /**
5738      * Appends an event handler to this component
5739      * @param {String}   eventName The type of event to listen for
5740      * @param {Function} handler The method the event invokes
5741      * @param {Object}   scope (optional) The scope in which to execute the handler
5742      * function. The handler function's "this" context.
5743      * @param {Object}   options (optional) An object containing handler configuration
5744      * properties. This may contain any of the following properties:<ul>
5745      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5746      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5747      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5748      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5749      * by the specified number of milliseconds. If the event fires again within that time, the original
5750      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5751      * </ul><br>
5752      * <p>
5753      * <b>Combining Options</b><br>
5754      * Using the options argument, it is possible to combine different types of listeners:<br>
5755      * <br>
5756      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5757                 <pre><code>
5758                 el.on('click', this.onClick, this, {
5759                         single: true,
5760                 delay: 100,
5761                 forumId: 4
5762                 });
5763                 </code></pre>
5764      * <p>
5765      * <b>Attaching multiple handlers in 1 call</b><br>
5766      * The method also allows for a single argument to be passed which is a config object containing properties
5767      * which specify multiple handlers.
5768      * <pre><code>
5769                 el.on({
5770                         'click': {
5771                         fn: this.onClick,
5772                         scope: this,
5773                         delay: 100
5774                 }, 
5775                 'mouseover': {
5776                         fn: this.onMouseOver,
5777                         scope: this
5778                 },
5779                 'mouseout': {
5780                         fn: this.onMouseOut,
5781                         scope: this
5782                 }
5783                 });
5784                 </code></pre>
5785      * <p>
5786      * Or a shorthand syntax which passes the same scope object to all handlers:
5787         <pre><code>
5788                 el.on({
5789                         'click': this.onClick,
5790                 'mouseover': this.onMouseOver,
5791                 'mouseout': this.onMouseOut,
5792                 scope: this
5793                 });
5794                 </code></pre>
5795      */
5796     addListener : function(eventName, fn, scope, o){
5797         if(typeof eventName == "object"){
5798             o = eventName;
5799             for(var e in o){
5800                 if(this.filterOptRe.test(e)){
5801                     continue;
5802                 }
5803                 if(typeof o[e] == "function"){
5804                     // shared options
5805                     this.addListener(e, o[e], o.scope,  o);
5806                 }else{
5807                     // individual options
5808                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5809                 }
5810             }
5811             return;
5812         }
5813         o = (!o || typeof o == "boolean") ? {} : o;
5814         eventName = eventName.toLowerCase();
5815         var ce = this.events[eventName] || true;
5816         if(typeof ce == "boolean"){
5817             ce = new Roo.util.Event(this, eventName);
5818             this.events[eventName] = ce;
5819         }
5820         ce.addListener(fn, scope, o);
5821     },
5822
5823     /**
5824      * Removes a listener
5825      * @param {String}   eventName     The type of event to listen for
5826      * @param {Function} handler        The handler to remove
5827      * @param {Object}   scope  (optional) The scope (this object) for the handler
5828      */
5829     removeListener : function(eventName, fn, scope){
5830         var ce = this.events[eventName.toLowerCase()];
5831         if(typeof ce == "object"){
5832             ce.removeListener(fn, scope);
5833         }
5834     },
5835
5836     /**
5837      * Removes all listeners for this object
5838      */
5839     purgeListeners : function(){
5840         for(var evt in this.events){
5841             if(typeof this.events[evt] == "object"){
5842                  this.events[evt].clearListeners();
5843             }
5844         }
5845     },
5846
5847     relayEvents : function(o, events){
5848         var createHandler = function(ename){
5849             return function(){
5850                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5851             };
5852         };
5853         for(var i = 0, len = events.length; i < len; i++){
5854             var ename = events[i];
5855             if(!this.events[ename]){ this.events[ename] = true; };
5856             o.on(ename, createHandler(ename), this);
5857         }
5858     },
5859
5860     /**
5861      * Used to define events on this Observable
5862      * @param {Object} object The object with the events defined
5863      */
5864     addEvents : function(o){
5865         if(!this.events){
5866             this.events = {};
5867         }
5868         Roo.applyIf(this.events, o);
5869     },
5870
5871     /**
5872      * Checks to see if this object has any listeners for a specified event
5873      * @param {String} eventName The name of the event to check for
5874      * @return {Boolean} True if the event is being listened for, else false
5875      */
5876     hasListener : function(eventName){
5877         var e = this.events[eventName];
5878         return typeof e == "object" && e.listeners.length > 0;
5879     }
5880 };
5881 /**
5882  * Appends an event handler to this element (shorthand for addListener)
5883  * @param {String}   eventName     The type of event to listen for
5884  * @param {Function} handler        The method the event invokes
5885  * @param {Object}   scope (optional) The scope in which to execute the handler
5886  * function. The handler function's "this" context.
5887  * @param {Object}   options  (optional)
5888  * @method
5889  */
5890 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5891 /**
5892  * Removes a listener (shorthand for removeListener)
5893  * @param {String}   eventName     The type of event to listen for
5894  * @param {Function} handler        The handler to remove
5895  * @param {Object}   scope  (optional) The scope (this object) for the handler
5896  * @method
5897  */
5898 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5899
5900 /**
5901  * Starts capture on the specified Observable. All events will be passed
5902  * to the supplied function with the event name + standard signature of the event
5903  * <b>before</b> the event is fired. If the supplied function returns false,
5904  * the event will not fire.
5905  * @param {Observable} o The Observable to capture
5906  * @param {Function} fn The function to call
5907  * @param {Object} scope (optional) The scope (this object) for the fn
5908  * @static
5909  */
5910 Roo.util.Observable.capture = function(o, fn, scope){
5911     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5912 };
5913
5914 /**
5915  * Removes <b>all</b> added captures from the Observable.
5916  * @param {Observable} o The Observable to release
5917  * @static
5918  */
5919 Roo.util.Observable.releaseCapture = function(o){
5920     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5921 };
5922
5923 (function(){
5924
5925     var createBuffered = function(h, o, scope){
5926         var task = new Roo.util.DelayedTask();
5927         return function(){
5928             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5929         };
5930     };
5931
5932     var createSingle = function(h, e, fn, scope){
5933         return function(){
5934             e.removeListener(fn, scope);
5935             return h.apply(scope, arguments);
5936         };
5937     };
5938
5939     var createDelayed = function(h, o, scope){
5940         return function(){
5941             var args = Array.prototype.slice.call(arguments, 0);
5942             setTimeout(function(){
5943                 h.apply(scope, args);
5944             }, o.delay || 10);
5945         };
5946     };
5947
5948     Roo.util.Event = function(obj, name){
5949         this.name = name;
5950         this.obj = obj;
5951         this.listeners = [];
5952     };
5953
5954     Roo.util.Event.prototype = {
5955         addListener : function(fn, scope, options){
5956             var o = options || {};
5957             scope = scope || this.obj;
5958             if(!this.isListening(fn, scope)){
5959                 var l = {fn: fn, scope: scope, options: o};
5960                 var h = fn;
5961                 if(o.delay){
5962                     h = createDelayed(h, o, scope);
5963                 }
5964                 if(o.single){
5965                     h = createSingle(h, this, fn, scope);
5966                 }
5967                 if(o.buffer){
5968                     h = createBuffered(h, o, scope);
5969                 }
5970                 l.fireFn = h;
5971                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5972                     this.listeners.push(l);
5973                 }else{
5974                     this.listeners = this.listeners.slice(0);
5975                     this.listeners.push(l);
5976                 }
5977             }
5978         },
5979
5980         findListener : function(fn, scope){
5981             scope = scope || this.obj;
5982             var ls = this.listeners;
5983             for(var i = 0, len = ls.length; i < len; i++){
5984                 var l = ls[i];
5985                 if(l.fn == fn && l.scope == scope){
5986                     return i;
5987                 }
5988             }
5989             return -1;
5990         },
5991
5992         isListening : function(fn, scope){
5993             return this.findListener(fn, scope) != -1;
5994         },
5995
5996         removeListener : function(fn, scope){
5997             var index;
5998             if((index = this.findListener(fn, scope)) != -1){
5999                 if(!this.firing){
6000                     this.listeners.splice(index, 1);
6001                 }else{
6002                     this.listeners = this.listeners.slice(0);
6003                     this.listeners.splice(index, 1);
6004                 }
6005                 return true;
6006             }
6007             return false;
6008         },
6009
6010         clearListeners : function(){
6011             this.listeners = [];
6012         },
6013
6014         fire : function(){
6015             var ls = this.listeners, scope, len = ls.length;
6016             if(len > 0){
6017                 this.firing = true;
6018                 var args = Array.prototype.slice.call(arguments, 0);
6019                 for(var i = 0; i < len; i++){
6020                     var l = ls[i];
6021                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6022                         this.firing = false;
6023                         return false;
6024                     }
6025                 }
6026                 this.firing = false;
6027             }
6028             return true;
6029         }
6030     };
6031 })();/*
6032  * Based on:
6033  * Ext JS Library 1.1.1
6034  * Copyright(c) 2006-2007, Ext JS, LLC.
6035  *
6036  * Originally Released Under LGPL - original licence link has changed is not relivant.
6037  *
6038  * Fork - LGPL
6039  * <script type="text/javascript">
6040  */
6041
6042 /**
6043  * @class Roo.EventManager
6044  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6045  * several useful events directly.
6046  * See {@link Roo.EventObject} for more details on normalized event objects.
6047  * @singleton
6048  */
6049 Roo.EventManager = function(){
6050     var docReadyEvent, docReadyProcId, docReadyState = false;
6051     var resizeEvent, resizeTask, textEvent, textSize;
6052     var E = Roo.lib.Event;
6053     var D = Roo.lib.Dom;
6054
6055
6056     var fireDocReady = function(){
6057         if(!docReadyState){
6058             docReadyState = true;
6059             Roo.isReady = true;
6060             if(docReadyProcId){
6061                 clearInterval(docReadyProcId);
6062             }
6063             if(Roo.isGecko || Roo.isOpera) {
6064                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6065             }
6066             if(Roo.isIE){
6067                 var defer = document.getElementById("ie-deferred-loader");
6068                 if(defer){
6069                     defer.onreadystatechange = null;
6070                     defer.parentNode.removeChild(defer);
6071                 }
6072             }
6073             if(docReadyEvent){
6074                 docReadyEvent.fire();
6075                 docReadyEvent.clearListeners();
6076             }
6077         }
6078     };
6079     
6080     var initDocReady = function(){
6081         docReadyEvent = new Roo.util.Event();
6082         if(Roo.isGecko || Roo.isOpera) {
6083             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6084         }else if(Roo.isIE){
6085             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6086             var defer = document.getElementById("ie-deferred-loader");
6087             defer.onreadystatechange = function(){
6088                 if(this.readyState == "complete"){
6089                     fireDocReady();
6090                 }
6091             };
6092         }else if(Roo.isSafari){ 
6093             docReadyProcId = setInterval(function(){
6094                 var rs = document.readyState;
6095                 if(rs == "complete") {
6096                     fireDocReady();     
6097                  }
6098             }, 10);
6099         }
6100         // no matter what, make sure it fires on load
6101         E.on(window, "load", fireDocReady);
6102     };
6103
6104     var createBuffered = function(h, o){
6105         var task = new Roo.util.DelayedTask(h);
6106         return function(e){
6107             // create new event object impl so new events don't wipe out properties
6108             e = new Roo.EventObjectImpl(e);
6109             task.delay(o.buffer, h, null, [e]);
6110         };
6111     };
6112
6113     var createSingle = function(h, el, ename, fn){
6114         return function(e){
6115             Roo.EventManager.removeListener(el, ename, fn);
6116             h(e);
6117         };
6118     };
6119
6120     var createDelayed = function(h, o){
6121         return function(e){
6122             // create new event object impl so new events don't wipe out properties
6123             e = new Roo.EventObjectImpl(e);
6124             setTimeout(function(){
6125                 h(e);
6126             }, o.delay || 10);
6127         };
6128     };
6129
6130     var listen = function(element, ename, opt, fn, scope){
6131         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6132         fn = fn || o.fn; scope = scope || o.scope;
6133         var el = Roo.getDom(element);
6134         if(!el){
6135             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6136         }
6137         var h = function(e){
6138             e = Roo.EventObject.setEvent(e);
6139             var t;
6140             if(o.delegate){
6141                 t = e.getTarget(o.delegate, el);
6142                 if(!t){
6143                     return;
6144                 }
6145             }else{
6146                 t = e.target;
6147             }
6148             if(o.stopEvent === true){
6149                 e.stopEvent();
6150             }
6151             if(o.preventDefault === true){
6152                e.preventDefault();
6153             }
6154             if(o.stopPropagation === true){
6155                 e.stopPropagation();
6156             }
6157
6158             if(o.normalized === false){
6159                 e = e.browserEvent;
6160             }
6161
6162             fn.call(scope || el, e, t, o);
6163         };
6164         if(o.delay){
6165             h = createDelayed(h, o);
6166         }
6167         if(o.single){
6168             h = createSingle(h, el, ename, fn);
6169         }
6170         if(o.buffer){
6171             h = createBuffered(h, o);
6172         }
6173         fn._handlers = fn._handlers || [];
6174         fn._handlers.push([Roo.id(el), ename, h]);
6175
6176         E.on(el, ename, h);
6177         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6178             el.addEventListener("DOMMouseScroll", h, false);
6179             E.on(window, 'unload', function(){
6180                 el.removeEventListener("DOMMouseScroll", h, false);
6181             });
6182         }
6183         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6184             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6185         }
6186         return h;
6187     };
6188
6189     var stopListening = function(el, ename, fn){
6190         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6191         if(hds){
6192             for(var i = 0, len = hds.length; i < len; i++){
6193                 var h = hds[i];
6194                 if(h[0] == id && h[1] == ename){
6195                     hd = h[2];
6196                     hds.splice(i, 1);
6197                     break;
6198                 }
6199             }
6200         }
6201         E.un(el, ename, hd);
6202         el = Roo.getDom(el);
6203         if(ename == "mousewheel" && el.addEventListener){
6204             el.removeEventListener("DOMMouseScroll", hd, false);
6205         }
6206         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6207             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6208         }
6209     };
6210
6211     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6212     
6213     var pub = {
6214         
6215         
6216         /** 
6217          * Fix for doc tools
6218          * @scope Roo.EventManager
6219          */
6220         
6221         
6222         /** 
6223          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6224          * object with a Roo.EventObject
6225          * @param {Function} fn        The method the event invokes
6226          * @param {Object}   scope    An object that becomes the scope of the handler
6227          * @param {boolean}  override If true, the obj passed in becomes
6228          *                             the execution scope of the listener
6229          * @return {Function} The wrapped function
6230          * @deprecated
6231          */
6232         wrap : function(fn, scope, override){
6233             return function(e){
6234                 Roo.EventObject.setEvent(e);
6235                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6236             };
6237         },
6238         
6239         /**
6240      * Appends an event handler to an element (shorthand for addListener)
6241      * @param {String/HTMLElement}   element        The html element or id to assign the
6242      * @param {String}   eventName The type of event to listen for
6243      * @param {Function} handler The method the event invokes
6244      * @param {Object}   scope (optional) The scope in which to execute the handler
6245      * function. The handler function's "this" context.
6246      * @param {Object}   options (optional) An object containing handler configuration
6247      * properties. This may contain any of the following properties:<ul>
6248      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6249      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6250      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6251      * <li>preventDefault {Boolean} True to prevent the default action</li>
6252      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6253      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6254      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6255      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6256      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6257      * by the specified number of milliseconds. If the event fires again within that time, the original
6258      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6259      * </ul><br>
6260      * <p>
6261      * <b>Combining Options</b><br>
6262      * Using the options argument, it is possible to combine different types of listeners:<br>
6263      * <br>
6264      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6265      * Code:<pre><code>
6266 el.on('click', this.onClick, this, {
6267     single: true,
6268     delay: 100,
6269     stopEvent : true,
6270     forumId: 4
6271 });</code></pre>
6272      * <p>
6273      * <b>Attaching multiple handlers in 1 call</b><br>
6274       * The method also allows for a single argument to be passed which is a config object containing properties
6275      * which specify multiple handlers.
6276      * <p>
6277      * Code:<pre><code>
6278 el.on({
6279     'click' : {
6280         fn: this.onClick
6281         scope: this,
6282         delay: 100
6283     },
6284     'mouseover' : {
6285         fn: this.onMouseOver
6286         scope: this
6287     },
6288     'mouseout' : {
6289         fn: this.onMouseOut
6290         scope: this
6291     }
6292 });</code></pre>
6293      * <p>
6294      * Or a shorthand syntax:<br>
6295      * Code:<pre><code>
6296 el.on({
6297     'click' : this.onClick,
6298     'mouseover' : this.onMouseOver,
6299     'mouseout' : this.onMouseOut
6300     scope: this
6301 });</code></pre>
6302      */
6303         addListener : function(element, eventName, fn, scope, options){
6304             if(typeof eventName == "object"){
6305                 var o = eventName;
6306                 for(var e in o){
6307                     if(propRe.test(e)){
6308                         continue;
6309                     }
6310                     if(typeof o[e] == "function"){
6311                         // shared options
6312                         listen(element, e, o, o[e], o.scope);
6313                     }else{
6314                         // individual options
6315                         listen(element, e, o[e]);
6316                     }
6317                 }
6318                 return;
6319             }
6320             return listen(element, eventName, options, fn, scope);
6321         },
6322         
6323         /**
6324          * Removes an event handler
6325          *
6326          * @param {String/HTMLElement}   element        The id or html element to remove the 
6327          *                             event from
6328          * @param {String}   eventName     The type of event
6329          * @param {Function} fn
6330          * @return {Boolean} True if a listener was actually removed
6331          */
6332         removeListener : function(element, eventName, fn){
6333             return stopListening(element, eventName, fn);
6334         },
6335         
6336         /**
6337          * Fires when the document is ready (before onload and before images are loaded). Can be 
6338          * accessed shorthanded Roo.onReady().
6339          * @param {Function} fn        The method the event invokes
6340          * @param {Object}   scope    An  object that becomes the scope of the handler
6341          * @param {boolean}  options
6342          */
6343         onDocumentReady : function(fn, scope, options){
6344             if(docReadyState){ // if it already fired
6345                 docReadyEvent.addListener(fn, scope, options);
6346                 docReadyEvent.fire();
6347                 docReadyEvent.clearListeners();
6348                 return;
6349             }
6350             if(!docReadyEvent){
6351                 initDocReady();
6352             }
6353             docReadyEvent.addListener(fn, scope, options);
6354         },
6355         
6356         /**
6357          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6358          * @param {Function} fn        The method the event invokes
6359          * @param {Object}   scope    An object that becomes the scope of the handler
6360          * @param {boolean}  options
6361          */
6362         onWindowResize : function(fn, scope, options){
6363             if(!resizeEvent){
6364                 resizeEvent = new Roo.util.Event();
6365                 resizeTask = new Roo.util.DelayedTask(function(){
6366                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6367                 });
6368                 E.on(window, "resize", function(){
6369                     if(Roo.isIE){
6370                         resizeTask.delay(50);
6371                     }else{
6372                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6373                     }
6374                 });
6375             }
6376             resizeEvent.addListener(fn, scope, options);
6377         },
6378
6379         /**
6380          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6381          * @param {Function} fn        The method the event invokes
6382          * @param {Object}   scope    An object that becomes the scope of the handler
6383          * @param {boolean}  options
6384          */
6385         onTextResize : function(fn, scope, options){
6386             if(!textEvent){
6387                 textEvent = new Roo.util.Event();
6388                 var textEl = new Roo.Element(document.createElement('div'));
6389                 textEl.dom.className = 'x-text-resize';
6390                 textEl.dom.innerHTML = 'X';
6391                 textEl.appendTo(document.body);
6392                 textSize = textEl.dom.offsetHeight;
6393                 setInterval(function(){
6394                     if(textEl.dom.offsetHeight != textSize){
6395                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6396                     }
6397                 }, this.textResizeInterval);
6398             }
6399             textEvent.addListener(fn, scope, options);
6400         },
6401
6402         /**
6403          * Removes the passed window resize listener.
6404          * @param {Function} fn        The method the event invokes
6405          * @param {Object}   scope    The scope of handler
6406          */
6407         removeResizeListener : function(fn, scope){
6408             if(resizeEvent){
6409                 resizeEvent.removeListener(fn, scope);
6410             }
6411         },
6412
6413         // private
6414         fireResize : function(){
6415             if(resizeEvent){
6416                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6417             }   
6418         },
6419         /**
6420          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6421          */
6422         ieDeferSrc : false,
6423         /**
6424          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6425          */
6426         textResizeInterval : 50
6427     };
6428     
6429     /**
6430      * Fix for doc tools
6431      * @scopeAlias pub=Roo.EventManager
6432      */
6433     
6434      /**
6435      * Appends an event handler to an element (shorthand for addListener)
6436      * @param {String/HTMLElement}   element        The html element or id to assign the
6437      * @param {String}   eventName The type of event to listen for
6438      * @param {Function} handler The method the event invokes
6439      * @param {Object}   scope (optional) The scope in which to execute the handler
6440      * function. The handler function's "this" context.
6441      * @param {Object}   options (optional) An object containing handler configuration
6442      * properties. This may contain any of the following properties:<ul>
6443      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6444      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6445      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6446      * <li>preventDefault {Boolean} True to prevent the default action</li>
6447      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6448      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6449      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6450      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6451      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6452      * by the specified number of milliseconds. If the event fires again within that time, the original
6453      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6454      * </ul><br>
6455      * <p>
6456      * <b>Combining Options</b><br>
6457      * Using the options argument, it is possible to combine different types of listeners:<br>
6458      * <br>
6459      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6460      * Code:<pre><code>
6461 el.on('click', this.onClick, this, {
6462     single: true,
6463     delay: 100,
6464     stopEvent : true,
6465     forumId: 4
6466 });</code></pre>
6467      * <p>
6468      * <b>Attaching multiple handlers in 1 call</b><br>
6469       * The method also allows for a single argument to be passed which is a config object containing properties
6470      * which specify multiple handlers.
6471      * <p>
6472      * Code:<pre><code>
6473 el.on({
6474     'click' : {
6475         fn: this.onClick
6476         scope: this,
6477         delay: 100
6478     },
6479     'mouseover' : {
6480         fn: this.onMouseOver
6481         scope: this
6482     },
6483     'mouseout' : {
6484         fn: this.onMouseOut
6485         scope: this
6486     }
6487 });</code></pre>
6488      * <p>
6489      * Or a shorthand syntax:<br>
6490      * Code:<pre><code>
6491 el.on({
6492     'click' : this.onClick,
6493     'mouseover' : this.onMouseOver,
6494     'mouseout' : this.onMouseOut
6495     scope: this
6496 });</code></pre>
6497      */
6498     pub.on = pub.addListener;
6499     pub.un = pub.removeListener;
6500
6501     pub.stoppedMouseDownEvent = new Roo.util.Event();
6502     return pub;
6503 }();
6504 /**
6505   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6506   * @param {Function} fn        The method the event invokes
6507   * @param {Object}   scope    An  object that becomes the scope of the handler
6508   * @param {boolean}  override If true, the obj passed in becomes
6509   *                             the execution scope of the listener
6510   * @member Roo
6511   * @method onReady
6512  */
6513 Roo.onReady = Roo.EventManager.onDocumentReady;
6514
6515 Roo.onReady(function(){
6516     var bd = Roo.get(document.body);
6517     if(!bd){ return; }
6518
6519     var cls = [
6520             Roo.isIE ? "roo-ie"
6521             : Roo.isGecko ? "roo-gecko"
6522             : Roo.isOpera ? "roo-opera"
6523             : Roo.isSafari ? "roo-safari" : ""];
6524
6525     if(Roo.isMac){
6526         cls.push("roo-mac");
6527     }
6528     if(Roo.isLinux){
6529         cls.push("roo-linux");
6530     }
6531     if(Roo.isBorderBox){
6532         cls.push('roo-border-box');
6533     }
6534     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6535         var p = bd.dom.parentNode;
6536         if(p){
6537             p.className += ' roo-strict';
6538         }
6539     }
6540     bd.addClass(cls.join(' '));
6541 });
6542
6543 /**
6544  * @class Roo.EventObject
6545  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6546  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6547  * Example:
6548  * <pre><code>
6549  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6550     e.preventDefault();
6551     var target = e.getTarget();
6552     ...
6553  }
6554  var myDiv = Roo.get("myDiv");
6555  myDiv.on("click", handleClick);
6556  //or
6557  Roo.EventManager.on("myDiv", 'click', handleClick);
6558  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6559  </code></pre>
6560  * @singleton
6561  */
6562 Roo.EventObject = function(){
6563     
6564     var E = Roo.lib.Event;
6565     
6566     // safari keypress events for special keys return bad keycodes
6567     var safariKeys = {
6568         63234 : 37, // left
6569         63235 : 39, // right
6570         63232 : 38, // up
6571         63233 : 40, // down
6572         63276 : 33, // page up
6573         63277 : 34, // page down
6574         63272 : 46, // delete
6575         63273 : 36, // home
6576         63275 : 35  // end
6577     };
6578
6579     // normalize button clicks
6580     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6581                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6582
6583     Roo.EventObjectImpl = function(e){
6584         if(e){
6585             this.setEvent(e.browserEvent || e);
6586         }
6587     };
6588     Roo.EventObjectImpl.prototype = {
6589         /**
6590          * Used to fix doc tools.
6591          * @scope Roo.EventObject.prototype
6592          */
6593             
6594
6595         
6596         
6597         /** The normal browser event */
6598         browserEvent : null,
6599         /** The button pressed in a mouse event */
6600         button : -1,
6601         /** True if the shift key was down during the event */
6602         shiftKey : false,
6603         /** True if the control key was down during the event */
6604         ctrlKey : false,
6605         /** True if the alt key was down during the event */
6606         altKey : false,
6607
6608         /** Key constant 
6609         * @type Number */
6610         BACKSPACE : 8,
6611         /** Key constant 
6612         * @type Number */
6613         TAB : 9,
6614         /** Key constant 
6615         * @type Number */
6616         RETURN : 13,
6617         /** Key constant 
6618         * @type Number */
6619         ENTER : 13,
6620         /** Key constant 
6621         * @type Number */
6622         SHIFT : 16,
6623         /** Key constant 
6624         * @type Number */
6625         CONTROL : 17,
6626         /** Key constant 
6627         * @type Number */
6628         ESC : 27,
6629         /** Key constant 
6630         * @type Number */
6631         SPACE : 32,
6632         /** Key constant 
6633         * @type Number */
6634         PAGEUP : 33,
6635         /** Key constant 
6636         * @type Number */
6637         PAGEDOWN : 34,
6638         /** Key constant 
6639         * @type Number */
6640         END : 35,
6641         /** Key constant 
6642         * @type Number */
6643         HOME : 36,
6644         /** Key constant 
6645         * @type Number */
6646         LEFT : 37,
6647         /** Key constant 
6648         * @type Number */
6649         UP : 38,
6650         /** Key constant 
6651         * @type Number */
6652         RIGHT : 39,
6653         /** Key constant 
6654         * @type Number */
6655         DOWN : 40,
6656         /** Key constant 
6657         * @type Number */
6658         DELETE : 46,
6659         /** Key constant 
6660         * @type Number */
6661         F5 : 116,
6662
6663            /** @private */
6664         setEvent : function(e){
6665             if(e == this || (e && e.browserEvent)){ // already wrapped
6666                 return e;
6667             }
6668             this.browserEvent = e;
6669             if(e){
6670                 // normalize buttons
6671                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6672                 if(e.type == 'click' && this.button == -1){
6673                     this.button = 0;
6674                 }
6675                 this.type = e.type;
6676                 this.shiftKey = e.shiftKey;
6677                 // mac metaKey behaves like ctrlKey
6678                 this.ctrlKey = e.ctrlKey || e.metaKey;
6679                 this.altKey = e.altKey;
6680                 // in getKey these will be normalized for the mac
6681                 this.keyCode = e.keyCode;
6682                 // keyup warnings on firefox.
6683                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6684                 // cache the target for the delayed and or buffered events
6685                 this.target = E.getTarget(e);
6686                 // same for XY
6687                 this.xy = E.getXY(e);
6688             }else{
6689                 this.button = -1;
6690                 this.shiftKey = false;
6691                 this.ctrlKey = false;
6692                 this.altKey = false;
6693                 this.keyCode = 0;
6694                 this.charCode =0;
6695                 this.target = null;
6696                 this.xy = [0, 0];
6697             }
6698             return this;
6699         },
6700
6701         /**
6702          * Stop the event (preventDefault and stopPropagation)
6703          */
6704         stopEvent : function(){
6705             if(this.browserEvent){
6706                 if(this.browserEvent.type == 'mousedown'){
6707                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6708                 }
6709                 E.stopEvent(this.browserEvent);
6710             }
6711         },
6712
6713         /**
6714          * Prevents the browsers default handling of the event.
6715          */
6716         preventDefault : function(){
6717             if(this.browserEvent){
6718                 E.preventDefault(this.browserEvent);
6719             }
6720         },
6721
6722         /** @private */
6723         isNavKeyPress : function(){
6724             var k = this.keyCode;
6725             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6726             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6727         },
6728
6729         isSpecialKey : function(){
6730             var k = this.keyCode;
6731             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6732             (k == 16) || (k == 17) ||
6733             (k >= 18 && k <= 20) ||
6734             (k >= 33 && k <= 35) ||
6735             (k >= 36 && k <= 39) ||
6736             (k >= 44 && k <= 45);
6737         },
6738         /**
6739          * Cancels bubbling of the event.
6740          */
6741         stopPropagation : function(){
6742             if(this.browserEvent){
6743                 if(this.type == 'mousedown'){
6744                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6745                 }
6746                 E.stopPropagation(this.browserEvent);
6747             }
6748         },
6749
6750         /**
6751          * Gets the key code for the event.
6752          * @return {Number}
6753          */
6754         getCharCode : function(){
6755             return this.charCode || this.keyCode;
6756         },
6757
6758         /**
6759          * Returns a normalized keyCode for the event.
6760          * @return {Number} The key code
6761          */
6762         getKey : function(){
6763             var k = this.keyCode || this.charCode;
6764             return Roo.isSafari ? (safariKeys[k] || k) : k;
6765         },
6766
6767         /**
6768          * Gets the x coordinate of the event.
6769          * @return {Number}
6770          */
6771         getPageX : function(){
6772             return this.xy[0];
6773         },
6774
6775         /**
6776          * Gets the y coordinate of the event.
6777          * @return {Number}
6778          */
6779         getPageY : function(){
6780             return this.xy[1];
6781         },
6782
6783         /**
6784          * Gets the time of the event.
6785          * @return {Number}
6786          */
6787         getTime : function(){
6788             if(this.browserEvent){
6789                 return E.getTime(this.browserEvent);
6790             }
6791             return null;
6792         },
6793
6794         /**
6795          * Gets the page coordinates of the event.
6796          * @return {Array} The xy values like [x, y]
6797          */
6798         getXY : function(){
6799             return this.xy;
6800         },
6801
6802         /**
6803          * Gets the target for the event.
6804          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6805          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6806                 search as a number or element (defaults to 10 || document.body)
6807          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6808          * @return {HTMLelement}
6809          */
6810         getTarget : function(selector, maxDepth, returnEl){
6811             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6812         },
6813         /**
6814          * Gets the related target.
6815          * @return {HTMLElement}
6816          */
6817         getRelatedTarget : function(){
6818             if(this.browserEvent){
6819                 return E.getRelatedTarget(this.browserEvent);
6820             }
6821             return null;
6822         },
6823
6824         /**
6825          * Normalizes mouse wheel delta across browsers
6826          * @return {Number} The delta
6827          */
6828         getWheelDelta : function(){
6829             var e = this.browserEvent;
6830             var delta = 0;
6831             if(e.wheelDelta){ /* IE/Opera. */
6832                 delta = e.wheelDelta/120;
6833             }else if(e.detail){ /* Mozilla case. */
6834                 delta = -e.detail/3;
6835             }
6836             return delta;
6837         },
6838
6839         /**
6840          * Returns true if the control, meta, shift or alt key was pressed during this event.
6841          * @return {Boolean}
6842          */
6843         hasModifier : function(){
6844             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6845         },
6846
6847         /**
6848          * Returns true if the target of this event equals el or is a child of el
6849          * @param {String/HTMLElement/Element} el
6850          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6851          * @return {Boolean}
6852          */
6853         within : function(el, related){
6854             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6855             return t && Roo.fly(el).contains(t);
6856         },
6857
6858         getPoint : function(){
6859             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6860         }
6861     };
6862
6863     return new Roo.EventObjectImpl();
6864 }();
6865             
6866     /*
6867  * Based on:
6868  * Ext JS Library 1.1.1
6869  * Copyright(c) 2006-2007, Ext JS, LLC.
6870  *
6871  * Originally Released Under LGPL - original licence link has changed is not relivant.
6872  *
6873  * Fork - LGPL
6874  * <script type="text/javascript">
6875  */
6876
6877  
6878 // was in Composite Element!??!?!
6879  
6880 (function(){
6881     var D = Roo.lib.Dom;
6882     var E = Roo.lib.Event;
6883     var A = Roo.lib.Anim;
6884
6885     // local style camelizing for speed
6886     var propCache = {};
6887     var camelRe = /(-[a-z])/gi;
6888     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6889     var view = document.defaultView;
6890
6891 /**
6892  * @class Roo.Element
6893  * Represents an Element in the DOM.<br><br>
6894  * Usage:<br>
6895 <pre><code>
6896 var el = Roo.get("my-div");
6897
6898 // or with getEl
6899 var el = getEl("my-div");
6900
6901 // or with a DOM element
6902 var el = Roo.get(myDivElement);
6903 </code></pre>
6904  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6905  * each call instead of constructing a new one.<br><br>
6906  * <b>Animations</b><br />
6907  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6908  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6909 <pre>
6910 Option    Default   Description
6911 --------- --------  ---------------------------------------------
6912 duration  .35       The duration of the animation in seconds
6913 easing    easeOut   The YUI easing method
6914 callback  none      A function to execute when the anim completes
6915 scope     this      The scope (this) of the callback function
6916 </pre>
6917 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6918 * manipulate the animation. Here's an example:
6919 <pre><code>
6920 var el = Roo.get("my-div");
6921
6922 // no animation
6923 el.setWidth(100);
6924
6925 // default animation
6926 el.setWidth(100, true);
6927
6928 // animation with some options set
6929 el.setWidth(100, {
6930     duration: 1,
6931     callback: this.foo,
6932     scope: this
6933 });
6934
6935 // using the "anim" property to get the Anim object
6936 var opt = {
6937     duration: 1,
6938     callback: this.foo,
6939     scope: this
6940 };
6941 el.setWidth(100, opt);
6942 ...
6943 if(opt.anim.isAnimated()){
6944     opt.anim.stop();
6945 }
6946 </code></pre>
6947 * <b> Composite (Collections of) Elements</b><br />
6948  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6949  * @constructor Create a new Element directly.
6950  * @param {String/HTMLElement} element
6951  * @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).
6952  */
6953     Roo.Element = function(element, forceNew){
6954         var dom = typeof element == "string" ?
6955                 document.getElementById(element) : element;
6956         if(!dom){ // invalid id/element
6957             return null;
6958         }
6959         var id = dom.id;
6960         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6961             return Roo.Element.cache[id];
6962         }
6963
6964         /**
6965          * The DOM element
6966          * @type HTMLElement
6967          */
6968         this.dom = dom;
6969
6970         /**
6971          * The DOM element ID
6972          * @type String
6973          */
6974         this.id = id || Roo.id(dom);
6975     };
6976
6977     var El = Roo.Element;
6978
6979     El.prototype = {
6980         /**
6981          * The element's default display mode  (defaults to "")
6982          * @type String
6983          */
6984         originalDisplay : "",
6985
6986         visibilityMode : 1,
6987         /**
6988          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6989          * @type String
6990          */
6991         defaultUnit : "px",
6992         /**
6993          * Sets the element's visibility mode. When setVisible() is called it
6994          * will use this to determine whether to set the visibility or the display property.
6995          * @param visMode Element.VISIBILITY or Element.DISPLAY
6996          * @return {Roo.Element} this
6997          */
6998         setVisibilityMode : function(visMode){
6999             this.visibilityMode = visMode;
7000             return this;
7001         },
7002         /**
7003          * Convenience method for setVisibilityMode(Element.DISPLAY)
7004          * @param {String} display (optional) What to set display to when visible
7005          * @return {Roo.Element} this
7006          */
7007         enableDisplayMode : function(display){
7008             this.setVisibilityMode(El.DISPLAY);
7009             if(typeof display != "undefined") this.originalDisplay = display;
7010             return this;
7011         },
7012
7013         /**
7014          * 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)
7015          * @param {String} selector The simple selector to test
7016          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7017                 search as a number or element (defaults to 10 || document.body)
7018          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7019          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7020          */
7021         findParent : function(simpleSelector, maxDepth, returnEl){
7022             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7023             maxDepth = maxDepth || 50;
7024             if(typeof maxDepth != "number"){
7025                 stopEl = Roo.getDom(maxDepth);
7026                 maxDepth = 10;
7027             }
7028             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7029                 if(dq.is(p, simpleSelector)){
7030                     return returnEl ? Roo.get(p) : p;
7031                 }
7032                 depth++;
7033                 p = p.parentNode;
7034             }
7035             return null;
7036         },
7037
7038
7039         /**
7040          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7041          * @param {String} selector The simple selector to test
7042          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7043                 search as a number or element (defaults to 10 || document.body)
7044          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7045          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7046          */
7047         findParentNode : function(simpleSelector, maxDepth, returnEl){
7048             var p = Roo.fly(this.dom.parentNode, '_internal');
7049             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7050         },
7051
7052         /**
7053          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7054          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7055          * @param {String} selector The simple selector to test
7056          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7057                 search as a number or element (defaults to 10 || document.body)
7058          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7059          */
7060         up : function(simpleSelector, maxDepth){
7061             return this.findParentNode(simpleSelector, maxDepth, true);
7062         },
7063
7064
7065
7066         /**
7067          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7068          * @param {String} selector The simple selector to test
7069          * @return {Boolean} True if this element matches the selector, else false
7070          */
7071         is : function(simpleSelector){
7072             return Roo.DomQuery.is(this.dom, simpleSelector);
7073         },
7074
7075         /**
7076          * Perform animation on this element.
7077          * @param {Object} args The YUI animation control args
7078          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7079          * @param {Function} onComplete (optional) Function to call when animation completes
7080          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7081          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7082          * @return {Roo.Element} this
7083          */
7084         animate : function(args, duration, onComplete, easing, animType){
7085             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7086             return this;
7087         },
7088
7089         /*
7090          * @private Internal animation call
7091          */
7092         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7093             animType = animType || 'run';
7094             opt = opt || {};
7095             var anim = Roo.lib.Anim[animType](
7096                 this.dom, args,
7097                 (opt.duration || defaultDur) || .35,
7098                 (opt.easing || defaultEase) || 'easeOut',
7099                 function(){
7100                     Roo.callback(cb, this);
7101                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7102                 },
7103                 this
7104             );
7105             opt.anim = anim;
7106             return anim;
7107         },
7108
7109         // private legacy anim prep
7110         preanim : function(a, i){
7111             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7112         },
7113
7114         /**
7115          * Removes worthless text nodes
7116          * @param {Boolean} forceReclean (optional) By default the element
7117          * keeps track if it has been cleaned already so
7118          * you can call this over and over. However, if you update the element and
7119          * need to force a reclean, you can pass true.
7120          */
7121         clean : function(forceReclean){
7122             if(this.isCleaned && forceReclean !== true){
7123                 return this;
7124             }
7125             var ns = /\S/;
7126             var d = this.dom, n = d.firstChild, ni = -1;
7127             while(n){
7128                 var nx = n.nextSibling;
7129                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7130                     d.removeChild(n);
7131                 }else{
7132                     n.nodeIndex = ++ni;
7133                 }
7134                 n = nx;
7135             }
7136             this.isCleaned = true;
7137             return this;
7138         },
7139
7140         // private
7141         calcOffsetsTo : function(el){
7142             el = Roo.get(el);
7143             var d = el.dom;
7144             var restorePos = false;
7145             if(el.getStyle('position') == 'static'){
7146                 el.position('relative');
7147                 restorePos = true;
7148             }
7149             var x = 0, y =0;
7150             var op = this.dom;
7151             while(op && op != d && op.tagName != 'HTML'){
7152                 x+= op.offsetLeft;
7153                 y+= op.offsetTop;
7154                 op = op.offsetParent;
7155             }
7156             if(restorePos){
7157                 el.position('static');
7158             }
7159             return [x, y];
7160         },
7161
7162         /**
7163          * Scrolls this element into view within the passed container.
7164          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7165          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7166          * @return {Roo.Element} this
7167          */
7168         scrollIntoView : function(container, hscroll){
7169             var c = Roo.getDom(container) || document.body;
7170             var el = this.dom;
7171
7172             var o = this.calcOffsetsTo(c),
7173                 l = o[0],
7174                 t = o[1],
7175                 b = t+el.offsetHeight,
7176                 r = l+el.offsetWidth;
7177
7178             var ch = c.clientHeight;
7179             var ct = parseInt(c.scrollTop, 10);
7180             var cl = parseInt(c.scrollLeft, 10);
7181             var cb = ct + ch;
7182             var cr = cl + c.clientWidth;
7183
7184             if(t < ct){
7185                 c.scrollTop = t;
7186             }else if(b > cb){
7187                 c.scrollTop = b-ch;
7188             }
7189
7190             if(hscroll !== false){
7191                 if(l < cl){
7192                     c.scrollLeft = l;
7193                 }else if(r > cr){
7194                     c.scrollLeft = r-c.clientWidth;
7195                 }
7196             }
7197             return this;
7198         },
7199
7200         // private
7201         scrollChildIntoView : function(child, hscroll){
7202             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7203         },
7204
7205         /**
7206          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7207          * the new height may not be available immediately.
7208          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7209          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7210          * @param {Function} onComplete (optional) Function to call when animation completes
7211          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7212          * @return {Roo.Element} this
7213          */
7214         autoHeight : function(animate, duration, onComplete, easing){
7215             var oldHeight = this.getHeight();
7216             this.clip();
7217             this.setHeight(1); // force clipping
7218             setTimeout(function(){
7219                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7220                 if(!animate){
7221                     this.setHeight(height);
7222                     this.unclip();
7223                     if(typeof onComplete == "function"){
7224                         onComplete();
7225                     }
7226                 }else{
7227                     this.setHeight(oldHeight); // restore original height
7228                     this.setHeight(height, animate, duration, function(){
7229                         this.unclip();
7230                         if(typeof onComplete == "function") onComplete();
7231                     }.createDelegate(this), easing);
7232                 }
7233             }.createDelegate(this), 0);
7234             return this;
7235         },
7236
7237         /**
7238          * Returns true if this element is an ancestor of the passed element
7239          * @param {HTMLElement/String} el The element to check
7240          * @return {Boolean} True if this element is an ancestor of el, else false
7241          */
7242         contains : function(el){
7243             if(!el){return false;}
7244             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7245         },
7246
7247         /**
7248          * Checks whether the element is currently visible using both visibility and display properties.
7249          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7250          * @return {Boolean} True if the element is currently visible, else false
7251          */
7252         isVisible : function(deep) {
7253             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7254             if(deep !== true || !vis){
7255                 return vis;
7256             }
7257             var p = this.dom.parentNode;
7258             while(p && p.tagName.toLowerCase() != "body"){
7259                 if(!Roo.fly(p, '_isVisible').isVisible()){
7260                     return false;
7261                 }
7262                 p = p.parentNode;
7263             }
7264             return true;
7265         },
7266
7267         /**
7268          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7269          * @param {String} selector The CSS selector
7270          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7271          * @return {CompositeElement/CompositeElementLite} The composite element
7272          */
7273         select : function(selector, unique){
7274             return El.select(selector, unique, this.dom);
7275         },
7276
7277         /**
7278          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7279          * @param {String} selector The CSS selector
7280          * @return {Array} An array of the matched nodes
7281          */
7282         query : function(selector, unique){
7283             return Roo.DomQuery.select(selector, this.dom);
7284         },
7285
7286         /**
7287          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7288          * @param {String} selector The CSS selector
7289          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7290          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7291          */
7292         child : function(selector, returnDom){
7293             var n = Roo.DomQuery.selectNode(selector, this.dom);
7294             return returnDom ? n : Roo.get(n);
7295         },
7296
7297         /**
7298          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7299          * @param {String} selector The CSS selector
7300          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7301          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7302          */
7303         down : function(selector, returnDom){
7304             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7305             return returnDom ? n : Roo.get(n);
7306         },
7307
7308         /**
7309          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7310          * @param {String} group The group the DD object is member of
7311          * @param {Object} config The DD config object
7312          * @param {Object} overrides An object containing methods to override/implement on the DD object
7313          * @return {Roo.dd.DD} The DD object
7314          */
7315         initDD : function(group, config, overrides){
7316             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7317             return Roo.apply(dd, overrides);
7318         },
7319
7320         /**
7321          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7322          * @param {String} group The group the DDProxy object is member of
7323          * @param {Object} config The DDProxy config object
7324          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7325          * @return {Roo.dd.DDProxy} The DDProxy object
7326          */
7327         initDDProxy : function(group, config, overrides){
7328             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7329             return Roo.apply(dd, overrides);
7330         },
7331
7332         /**
7333          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7334          * @param {String} group The group the DDTarget object is member of
7335          * @param {Object} config The DDTarget config object
7336          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7337          * @return {Roo.dd.DDTarget} The DDTarget object
7338          */
7339         initDDTarget : function(group, config, overrides){
7340             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7341             return Roo.apply(dd, overrides);
7342         },
7343
7344         /**
7345          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7346          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7347          * @param {Boolean} visible Whether the element is visible
7348          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7349          * @return {Roo.Element} this
7350          */
7351          setVisible : function(visible, animate){
7352             if(!animate || !A){
7353                 if(this.visibilityMode == El.DISPLAY){
7354                     this.setDisplayed(visible);
7355                 }else{
7356                     this.fixDisplay();
7357                     this.dom.style.visibility = visible ? "visible" : "hidden";
7358                 }
7359             }else{
7360                 // closure for composites
7361                 var dom = this.dom;
7362                 var visMode = this.visibilityMode;
7363                 if(visible){
7364                     this.setOpacity(.01);
7365                     this.setVisible(true);
7366                 }
7367                 this.anim({opacity: { to: (visible?1:0) }},
7368                       this.preanim(arguments, 1),
7369                       null, .35, 'easeIn', function(){
7370                          if(!visible){
7371                              if(visMode == El.DISPLAY){
7372                                  dom.style.display = "none";
7373                              }else{
7374                                  dom.style.visibility = "hidden";
7375                              }
7376                              Roo.get(dom).setOpacity(1);
7377                          }
7378                      });
7379             }
7380             return this;
7381         },
7382
7383         /**
7384          * Returns true if display is not "none"
7385          * @return {Boolean}
7386          */
7387         isDisplayed : function() {
7388             return this.getStyle("display") != "none";
7389         },
7390
7391         /**
7392          * Toggles the element's visibility or display, depending on visibility mode.
7393          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7394          * @return {Roo.Element} this
7395          */
7396         toggle : function(animate){
7397             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7398             return this;
7399         },
7400
7401         /**
7402          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7403          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7404          * @return {Roo.Element} this
7405          */
7406         setDisplayed : function(value) {
7407             if(typeof value == "boolean"){
7408                value = value ? this.originalDisplay : "none";
7409             }
7410             this.setStyle("display", value);
7411             return this;
7412         },
7413
7414         /**
7415          * Tries to focus the element. Any exceptions are caught and ignored.
7416          * @return {Roo.Element} this
7417          */
7418         focus : function() {
7419             try{
7420                 this.dom.focus();
7421             }catch(e){}
7422             return this;
7423         },
7424
7425         /**
7426          * Tries to blur the element. Any exceptions are caught and ignored.
7427          * @return {Roo.Element} this
7428          */
7429         blur : function() {
7430             try{
7431                 this.dom.blur();
7432             }catch(e){}
7433             return this;
7434         },
7435
7436         /**
7437          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7438          * @param {String/Array} className The CSS class to add, or an array of classes
7439          * @return {Roo.Element} this
7440          */
7441         addClass : function(className){
7442             if(className instanceof Array){
7443                 for(var i = 0, len = className.length; i < len; i++) {
7444                     this.addClass(className[i]);
7445                 }
7446             }else{
7447                 if(className && !this.hasClass(className)){
7448                     this.dom.className = this.dom.className + " " + className;
7449                 }
7450             }
7451             return this;
7452         },
7453
7454         /**
7455          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7456          * @param {String/Array} className The CSS class to add, or an array of classes
7457          * @return {Roo.Element} this
7458          */
7459         radioClass : function(className){
7460             var siblings = this.dom.parentNode.childNodes;
7461             for(var i = 0; i < siblings.length; i++) {
7462                 var s = siblings[i];
7463                 if(s.nodeType == 1){
7464                     Roo.get(s).removeClass(className);
7465                 }
7466             }
7467             this.addClass(className);
7468             return this;
7469         },
7470
7471         /**
7472          * Removes one or more CSS classes from the element.
7473          * @param {String/Array} className The CSS class to remove, or an array of classes
7474          * @return {Roo.Element} this
7475          */
7476         removeClass : function(className){
7477             if(!className || !this.dom.className){
7478                 return this;
7479             }
7480             if(className instanceof Array){
7481                 for(var i = 0, len = className.length; i < len; i++) {
7482                     this.removeClass(className[i]);
7483                 }
7484             }else{
7485                 if(this.hasClass(className)){
7486                     var re = this.classReCache[className];
7487                     if (!re) {
7488                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7489                        this.classReCache[className] = re;
7490                     }
7491                     this.dom.className =
7492                         this.dom.className.replace(re, " ");
7493                 }
7494             }
7495             return this;
7496         },
7497
7498         // private
7499         classReCache: {},
7500
7501         /**
7502          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7503          * @param {String} className The CSS class to toggle
7504          * @return {Roo.Element} this
7505          */
7506         toggleClass : function(className){
7507             if(this.hasClass(className)){
7508                 this.removeClass(className);
7509             }else{
7510                 this.addClass(className);
7511             }
7512             return this;
7513         },
7514
7515         /**
7516          * Checks if the specified CSS class exists on this element's DOM node.
7517          * @param {String} className The CSS class to check for
7518          * @return {Boolean} True if the class exists, else false
7519          */
7520         hasClass : function(className){
7521             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7522         },
7523
7524         /**
7525          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7526          * @param {String} oldClassName The CSS class to replace
7527          * @param {String} newClassName The replacement CSS class
7528          * @return {Roo.Element} this
7529          */
7530         replaceClass : function(oldClassName, newClassName){
7531             this.removeClass(oldClassName);
7532             this.addClass(newClassName);
7533             return this;
7534         },
7535
7536         /**
7537          * Returns an object with properties matching the styles requested.
7538          * For example, el.getStyles('color', 'font-size', 'width') might return
7539          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7540          * @param {String} style1 A style name
7541          * @param {String} style2 A style name
7542          * @param {String} etc.
7543          * @return {Object} The style object
7544          */
7545         getStyles : function(){
7546             var a = arguments, len = a.length, r = {};
7547             for(var i = 0; i < len; i++){
7548                 r[a[i]] = this.getStyle(a[i]);
7549             }
7550             return r;
7551         },
7552
7553         /**
7554          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7555          * @param {String} property The style property whose value is returned.
7556          * @return {String} The current value of the style property for this element.
7557          */
7558         getStyle : function(){
7559             return view && view.getComputedStyle ?
7560                 function(prop){
7561                     var el = this.dom, v, cs, camel;
7562                     if(prop == 'float'){
7563                         prop = "cssFloat";
7564                     }
7565                     if(el.style && (v = el.style[prop])){
7566                         return v;
7567                     }
7568                     if(cs = view.getComputedStyle(el, "")){
7569                         if(!(camel = propCache[prop])){
7570                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7571                         }
7572                         return cs[camel];
7573                     }
7574                     return null;
7575                 } :
7576                 function(prop){
7577                     var el = this.dom, v, cs, camel;
7578                     if(prop == 'opacity'){
7579                         if(typeof el.style.filter == 'string'){
7580                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7581                             if(m){
7582                                 var fv = parseFloat(m[1]);
7583                                 if(!isNaN(fv)){
7584                                     return fv ? fv / 100 : 0;
7585                                 }
7586                             }
7587                         }
7588                         return 1;
7589                     }else if(prop == 'float'){
7590                         prop = "styleFloat";
7591                     }
7592                     if(!(camel = propCache[prop])){
7593                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7594                     }
7595                     if(v = el.style[camel]){
7596                         return v;
7597                     }
7598                     if(cs = el.currentStyle){
7599                         return cs[camel];
7600                     }
7601                     return null;
7602                 };
7603         }(),
7604
7605         /**
7606          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7607          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7608          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7609          * @return {Roo.Element} this
7610          */
7611         setStyle : function(prop, value){
7612             if(typeof prop == "string"){
7613                 
7614                 if (prop == 'float') {
7615                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7616                     return this;
7617                 }
7618                 
7619                 var camel;
7620                 if(!(camel = propCache[prop])){
7621                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7622                 }
7623                 
7624                 if(camel == 'opacity') {
7625                     this.setOpacity(value);
7626                 }else{
7627                     this.dom.style[camel] = value;
7628                 }
7629             }else{
7630                 for(var style in prop){
7631                     if(typeof prop[style] != "function"){
7632                        this.setStyle(style, prop[style]);
7633                     }
7634                 }
7635             }
7636             return this;
7637         },
7638
7639         /**
7640          * More flexible version of {@link #setStyle} for setting style properties.
7641          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7642          * a function which returns such a specification.
7643          * @return {Roo.Element} this
7644          */
7645         applyStyles : function(style){
7646             Roo.DomHelper.applyStyles(this.dom, style);
7647             return this;
7648         },
7649
7650         /**
7651           * 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).
7652           * @return {Number} The X position of the element
7653           */
7654         getX : function(){
7655             return D.getX(this.dom);
7656         },
7657
7658         /**
7659           * 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).
7660           * @return {Number} The Y position of the element
7661           */
7662         getY : function(){
7663             return D.getY(this.dom);
7664         },
7665
7666         /**
7667           * 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).
7668           * @return {Array} The XY position of the element
7669           */
7670         getXY : function(){
7671             return D.getXY(this.dom);
7672         },
7673
7674         /**
7675          * 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).
7676          * @param {Number} The X position of the element
7677          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7678          * @return {Roo.Element} this
7679          */
7680         setX : function(x, animate){
7681             if(!animate || !A){
7682                 D.setX(this.dom, x);
7683             }else{
7684                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7685             }
7686             return this;
7687         },
7688
7689         /**
7690          * 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).
7691          * @param {Number} The Y position of the element
7692          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7693          * @return {Roo.Element} this
7694          */
7695         setY : function(y, animate){
7696             if(!animate || !A){
7697                 D.setY(this.dom, y);
7698             }else{
7699                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7700             }
7701             return this;
7702         },
7703
7704         /**
7705          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7706          * @param {String} left The left CSS property value
7707          * @return {Roo.Element} this
7708          */
7709         setLeft : function(left){
7710             this.setStyle("left", this.addUnits(left));
7711             return this;
7712         },
7713
7714         /**
7715          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7716          * @param {String} top The top CSS property value
7717          * @return {Roo.Element} this
7718          */
7719         setTop : function(top){
7720             this.setStyle("top", this.addUnits(top));
7721             return this;
7722         },
7723
7724         /**
7725          * Sets the element's CSS right style.
7726          * @param {String} right The right CSS property value
7727          * @return {Roo.Element} this
7728          */
7729         setRight : function(right){
7730             this.setStyle("right", this.addUnits(right));
7731             return this;
7732         },
7733
7734         /**
7735          * Sets the element's CSS bottom style.
7736          * @param {String} bottom The bottom CSS property value
7737          * @return {Roo.Element} this
7738          */
7739         setBottom : function(bottom){
7740             this.setStyle("bottom", this.addUnits(bottom));
7741             return this;
7742         },
7743
7744         /**
7745          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7746          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7747          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7748          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7749          * @return {Roo.Element} this
7750          */
7751         setXY : function(pos, animate){
7752             if(!animate || !A){
7753                 D.setXY(this.dom, pos);
7754             }else{
7755                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7756             }
7757             return this;
7758         },
7759
7760         /**
7761          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7762          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7763          * @param {Number} x X value for new position (coordinates are page-based)
7764          * @param {Number} y Y value for new position (coordinates are page-based)
7765          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7766          * @return {Roo.Element} this
7767          */
7768         setLocation : function(x, y, animate){
7769             this.setXY([x, y], this.preanim(arguments, 2));
7770             return this;
7771         },
7772
7773         /**
7774          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7775          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7776          * @param {Number} x X value for new position (coordinates are page-based)
7777          * @param {Number} y Y value for new position (coordinates are page-based)
7778          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7779          * @return {Roo.Element} this
7780          */
7781         moveTo : function(x, y, animate){
7782             this.setXY([x, y], this.preanim(arguments, 2));
7783             return this;
7784         },
7785
7786         /**
7787          * Returns the region of the given element.
7788          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7789          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7790          */
7791         getRegion : function(){
7792             return D.getRegion(this.dom);
7793         },
7794
7795         /**
7796          * Returns the offset height of the element
7797          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7798          * @return {Number} The element's height
7799          */
7800         getHeight : function(contentHeight){
7801             var h = this.dom.offsetHeight || 0;
7802             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7803         },
7804
7805         /**
7806          * Returns the offset width of the element
7807          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7808          * @return {Number} The element's width
7809          */
7810         getWidth : function(contentWidth){
7811             var w = this.dom.offsetWidth || 0;
7812             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7813         },
7814
7815         /**
7816          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7817          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7818          * if a height has not been set using CSS.
7819          * @return {Number}
7820          */
7821         getComputedHeight : function(){
7822             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7823             if(!h){
7824                 h = parseInt(this.getStyle('height'), 10) || 0;
7825                 if(!this.isBorderBox()){
7826                     h += this.getFrameWidth('tb');
7827                 }
7828             }
7829             return h;
7830         },
7831
7832         /**
7833          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7834          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7835          * if a width has not been set using CSS.
7836          * @return {Number}
7837          */
7838         getComputedWidth : function(){
7839             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7840             if(!w){
7841                 w = parseInt(this.getStyle('width'), 10) || 0;
7842                 if(!this.isBorderBox()){
7843                     w += this.getFrameWidth('lr');
7844                 }
7845             }
7846             return w;
7847         },
7848
7849         /**
7850          * Returns the size of the element.
7851          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7852          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7853          */
7854         getSize : function(contentSize){
7855             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7856         },
7857
7858         /**
7859          * Returns the width and height of the viewport.
7860          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7861          */
7862         getViewSize : function(){
7863             var d = this.dom, doc = document, aw = 0, ah = 0;
7864             if(d == doc || d == doc.body){
7865                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7866             }else{
7867                 return {
7868                     width : d.clientWidth,
7869                     height: d.clientHeight
7870                 };
7871             }
7872         },
7873
7874         /**
7875          * Returns the value of the "value" attribute
7876          * @param {Boolean} asNumber true to parse the value as a number
7877          * @return {String/Number}
7878          */
7879         getValue : function(asNumber){
7880             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7881         },
7882
7883         // private
7884         adjustWidth : function(width){
7885             if(typeof width == "number"){
7886                 if(this.autoBoxAdjust && !this.isBorderBox()){
7887                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7888                 }
7889                 if(width < 0){
7890                     width = 0;
7891                 }
7892             }
7893             return width;
7894         },
7895
7896         // private
7897         adjustHeight : function(height){
7898             if(typeof height == "number"){
7899                if(this.autoBoxAdjust && !this.isBorderBox()){
7900                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7901                }
7902                if(height < 0){
7903                    height = 0;
7904                }
7905             }
7906             return height;
7907         },
7908
7909         /**
7910          * Set the width of the element
7911          * @param {Number} width The new width
7912          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7913          * @return {Roo.Element} this
7914          */
7915         setWidth : function(width, animate){
7916             width = this.adjustWidth(width);
7917             if(!animate || !A){
7918                 this.dom.style.width = this.addUnits(width);
7919             }else{
7920                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7921             }
7922             return this;
7923         },
7924
7925         /**
7926          * Set the height of the element
7927          * @param {Number} height The new height
7928          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7929          * @return {Roo.Element} this
7930          */
7931          setHeight : function(height, animate){
7932             height = this.adjustHeight(height);
7933             if(!animate || !A){
7934                 this.dom.style.height = this.addUnits(height);
7935             }else{
7936                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7937             }
7938             return this;
7939         },
7940
7941         /**
7942          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7943          * @param {Number} width The new width
7944          * @param {Number} height The new height
7945          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7946          * @return {Roo.Element} this
7947          */
7948          setSize : function(width, height, animate){
7949             if(typeof width == "object"){ // in case of object from getSize()
7950                 height = width.height; width = width.width;
7951             }
7952             width = this.adjustWidth(width); height = this.adjustHeight(height);
7953             if(!animate || !A){
7954                 this.dom.style.width = this.addUnits(width);
7955                 this.dom.style.height = this.addUnits(height);
7956             }else{
7957                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7958             }
7959             return this;
7960         },
7961
7962         /**
7963          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7964          * @param {Number} x X value for new position (coordinates are page-based)
7965          * @param {Number} y Y value for new position (coordinates are page-based)
7966          * @param {Number} width The new width
7967          * @param {Number} height The new height
7968          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7969          * @return {Roo.Element} this
7970          */
7971         setBounds : function(x, y, width, height, animate){
7972             if(!animate || !A){
7973                 this.setSize(width, height);
7974                 this.setLocation(x, y);
7975             }else{
7976                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7977                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7978                               this.preanim(arguments, 4), 'motion');
7979             }
7980             return this;
7981         },
7982
7983         /**
7984          * 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.
7985          * @param {Roo.lib.Region} region The region to fill
7986          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7987          * @return {Roo.Element} this
7988          */
7989         setRegion : function(region, animate){
7990             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7991             return this;
7992         },
7993
7994         /**
7995          * Appends an event handler
7996          *
7997          * @param {String}   eventName     The type of event to append
7998          * @param {Function} fn        The method the event invokes
7999          * @param {Object} scope       (optional) The scope (this object) of the fn
8000          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8001          */
8002         addListener : function(eventName, fn, scope, options){
8003             if (this.dom) {
8004                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8005             }
8006         },
8007
8008         /**
8009          * Removes an event handler from this element
8010          * @param {String} eventName the type of event to remove
8011          * @param {Function} fn the method the event invokes
8012          * @return {Roo.Element} this
8013          */
8014         removeListener : function(eventName, fn){
8015             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8016             return this;
8017         },
8018
8019         /**
8020          * Removes all previous added listeners from this element
8021          * @return {Roo.Element} this
8022          */
8023         removeAllListeners : function(){
8024             E.purgeElement(this.dom);
8025             return this;
8026         },
8027
8028         relayEvent : function(eventName, observable){
8029             this.on(eventName, function(e){
8030                 observable.fireEvent(eventName, e);
8031             });
8032         },
8033
8034         /**
8035          * Set the opacity of the element
8036          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8037          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8038          * @return {Roo.Element} this
8039          */
8040          setOpacity : function(opacity, animate){
8041             if(!animate || !A){
8042                 var s = this.dom.style;
8043                 if(Roo.isIE){
8044                     s.zoom = 1;
8045                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8046                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8047                 }else{
8048                     s.opacity = opacity;
8049                 }
8050             }else{
8051                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8052             }
8053             return this;
8054         },
8055
8056         /**
8057          * Gets the left X coordinate
8058          * @param {Boolean} local True to get the local css position instead of page coordinate
8059          * @return {Number}
8060          */
8061         getLeft : function(local){
8062             if(!local){
8063                 return this.getX();
8064             }else{
8065                 return parseInt(this.getStyle("left"), 10) || 0;
8066             }
8067         },
8068
8069         /**
8070          * Gets the right X coordinate of the element (element X position + element width)
8071          * @param {Boolean} local True to get the local css position instead of page coordinate
8072          * @return {Number}
8073          */
8074         getRight : function(local){
8075             if(!local){
8076                 return this.getX() + this.getWidth();
8077             }else{
8078                 return (this.getLeft(true) + this.getWidth()) || 0;
8079             }
8080         },
8081
8082         /**
8083          * Gets the top Y coordinate
8084          * @param {Boolean} local True to get the local css position instead of page coordinate
8085          * @return {Number}
8086          */
8087         getTop : function(local) {
8088             if(!local){
8089                 return this.getY();
8090             }else{
8091                 return parseInt(this.getStyle("top"), 10) || 0;
8092             }
8093         },
8094
8095         /**
8096          * Gets the bottom Y coordinate of the element (element Y position + element height)
8097          * @param {Boolean} local True to get the local css position instead of page coordinate
8098          * @return {Number}
8099          */
8100         getBottom : function(local){
8101             if(!local){
8102                 return this.getY() + this.getHeight();
8103             }else{
8104                 return (this.getTop(true) + this.getHeight()) || 0;
8105             }
8106         },
8107
8108         /**
8109         * Initializes positioning on this element. If a desired position is not passed, it will make the
8110         * the element positioned relative IF it is not already positioned.
8111         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8112         * @param {Number} zIndex (optional) The zIndex to apply
8113         * @param {Number} x (optional) Set the page X position
8114         * @param {Number} y (optional) Set the page Y position
8115         */
8116         position : function(pos, zIndex, x, y){
8117             if(!pos){
8118                if(this.getStyle('position') == 'static'){
8119                    this.setStyle('position', 'relative');
8120                }
8121             }else{
8122                 this.setStyle("position", pos);
8123             }
8124             if(zIndex){
8125                 this.setStyle("z-index", zIndex);
8126             }
8127             if(x !== undefined && y !== undefined){
8128                 this.setXY([x, y]);
8129             }else if(x !== undefined){
8130                 this.setX(x);
8131             }else if(y !== undefined){
8132                 this.setY(y);
8133             }
8134         },
8135
8136         /**
8137         * Clear positioning back to the default when the document was loaded
8138         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8139         * @return {Roo.Element} this
8140          */
8141         clearPositioning : function(value){
8142             value = value ||'';
8143             this.setStyle({
8144                 "left": value,
8145                 "right": value,
8146                 "top": value,
8147                 "bottom": value,
8148                 "z-index": "",
8149                 "position" : "static"
8150             });
8151             return this;
8152         },
8153
8154         /**
8155         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8156         * snapshot before performing an update and then restoring the element.
8157         * @return {Object}
8158         */
8159         getPositioning : function(){
8160             var l = this.getStyle("left");
8161             var t = this.getStyle("top");
8162             return {
8163                 "position" : this.getStyle("position"),
8164                 "left" : l,
8165                 "right" : l ? "" : this.getStyle("right"),
8166                 "top" : t,
8167                 "bottom" : t ? "" : this.getStyle("bottom"),
8168                 "z-index" : this.getStyle("z-index")
8169             };
8170         },
8171
8172         /**
8173          * Gets the width of the border(s) for the specified side(s)
8174          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8175          * passing lr would get the border (l)eft width + the border (r)ight width.
8176          * @return {Number} The width of the sides passed added together
8177          */
8178         getBorderWidth : function(side){
8179             return this.addStyles(side, El.borders);
8180         },
8181
8182         /**
8183          * Gets the width of the padding(s) for the specified side(s)
8184          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8185          * passing lr would get the padding (l)eft + the padding (r)ight.
8186          * @return {Number} The padding of the sides passed added together
8187          */
8188         getPadding : function(side){
8189             return this.addStyles(side, El.paddings);
8190         },
8191
8192         /**
8193         * Set positioning with an object returned by getPositioning().
8194         * @param {Object} posCfg
8195         * @return {Roo.Element} this
8196          */
8197         setPositioning : function(pc){
8198             this.applyStyles(pc);
8199             if(pc.right == "auto"){
8200                 this.dom.style.right = "";
8201             }
8202             if(pc.bottom == "auto"){
8203                 this.dom.style.bottom = "";
8204             }
8205             return this;
8206         },
8207
8208         // private
8209         fixDisplay : function(){
8210             if(this.getStyle("display") == "none"){
8211                 this.setStyle("visibility", "hidden");
8212                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8213                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8214                     this.setStyle("display", "block");
8215                 }
8216             }
8217         },
8218
8219         /**
8220          * Quick set left and top adding default units
8221          * @param {String} left The left CSS property value
8222          * @param {String} top The top CSS property value
8223          * @return {Roo.Element} this
8224          */
8225          setLeftTop : function(left, top){
8226             this.dom.style.left = this.addUnits(left);
8227             this.dom.style.top = this.addUnits(top);
8228             return this;
8229         },
8230
8231         /**
8232          * Move this element relative to its current position.
8233          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8234          * @param {Number} distance How far to move the element in pixels
8235          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8236          * @return {Roo.Element} this
8237          */
8238          move : function(direction, distance, animate){
8239             var xy = this.getXY();
8240             direction = direction.toLowerCase();
8241             switch(direction){
8242                 case "l":
8243                 case "left":
8244                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8245                     break;
8246                case "r":
8247                case "right":
8248                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8249                     break;
8250                case "t":
8251                case "top":
8252                case "up":
8253                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8254                     break;
8255                case "b":
8256                case "bottom":
8257                case "down":
8258                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8259                     break;
8260             }
8261             return this;
8262         },
8263
8264         /**
8265          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8266          * @return {Roo.Element} this
8267          */
8268         clip : function(){
8269             if(!this.isClipped){
8270                this.isClipped = true;
8271                this.originalClip = {
8272                    "o": this.getStyle("overflow"),
8273                    "x": this.getStyle("overflow-x"),
8274                    "y": this.getStyle("overflow-y")
8275                };
8276                this.setStyle("overflow", "hidden");
8277                this.setStyle("overflow-x", "hidden");
8278                this.setStyle("overflow-y", "hidden");
8279             }
8280             return this;
8281         },
8282
8283         /**
8284          *  Return clipping (overflow) to original clipping before clip() was called
8285          * @return {Roo.Element} this
8286          */
8287         unclip : function(){
8288             if(this.isClipped){
8289                 this.isClipped = false;
8290                 var o = this.originalClip;
8291                 if(o.o){this.setStyle("overflow", o.o);}
8292                 if(o.x){this.setStyle("overflow-x", o.x);}
8293                 if(o.y){this.setStyle("overflow-y", o.y);}
8294             }
8295             return this;
8296         },
8297
8298
8299         /**
8300          * Gets the x,y coordinates specified by the anchor position on the element.
8301          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8302          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8303          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8304          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8305          * @return {Array} [x, y] An array containing the element's x and y coordinates
8306          */
8307         getAnchorXY : function(anchor, local, s){
8308             //Passing a different size is useful for pre-calculating anchors,
8309             //especially for anchored animations that change the el size.
8310
8311             var w, h, vp = false;
8312             if(!s){
8313                 var d = this.dom;
8314                 if(d == document.body || d == document){
8315                     vp = true;
8316                     w = D.getViewWidth(); h = D.getViewHeight();
8317                 }else{
8318                     w = this.getWidth(); h = this.getHeight();
8319                 }
8320             }else{
8321                 w = s.width;  h = s.height;
8322             }
8323             var x = 0, y = 0, r = Math.round;
8324             switch((anchor || "tl").toLowerCase()){
8325                 case "c":
8326                     x = r(w*.5);
8327                     y = r(h*.5);
8328                 break;
8329                 case "t":
8330                     x = r(w*.5);
8331                     y = 0;
8332                 break;
8333                 case "l":
8334                     x = 0;
8335                     y = r(h*.5);
8336                 break;
8337                 case "r":
8338                     x = w;
8339                     y = r(h*.5);
8340                 break;
8341                 case "b":
8342                     x = r(w*.5);
8343                     y = h;
8344                 break;
8345                 case "tl":
8346                     x = 0;
8347                     y = 0;
8348                 break;
8349                 case "bl":
8350                     x = 0;
8351                     y = h;
8352                 break;
8353                 case "br":
8354                     x = w;
8355                     y = h;
8356                 break;
8357                 case "tr":
8358                     x = w;
8359                     y = 0;
8360                 break;
8361             }
8362             if(local === true){
8363                 return [x, y];
8364             }
8365             if(vp){
8366                 var sc = this.getScroll();
8367                 return [x + sc.left, y + sc.top];
8368             }
8369             //Add the element's offset xy
8370             var o = this.getXY();
8371             return [x+o[0], y+o[1]];
8372         },
8373
8374         /**
8375          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8376          * supported position values.
8377          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8378          * @param {String} position The position to align to.
8379          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8380          * @return {Array} [x, y]
8381          */
8382         getAlignToXY : function(el, p, o){
8383             el = Roo.get(el);
8384             var d = this.dom;
8385             if(!el.dom){
8386                 throw "Element.alignTo with an element that doesn't exist";
8387             }
8388             var c = false; //constrain to viewport
8389             var p1 = "", p2 = "";
8390             o = o || [0,0];
8391
8392             if(!p){
8393                 p = "tl-bl";
8394             }else if(p == "?"){
8395                 p = "tl-bl?";
8396             }else if(p.indexOf("-") == -1){
8397                 p = "tl-" + p;
8398             }
8399             p = p.toLowerCase();
8400             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8401             if(!m){
8402                throw "Element.alignTo with an invalid alignment " + p;
8403             }
8404             p1 = m[1]; p2 = m[2]; c = !!m[3];
8405
8406             //Subtract the aligned el's internal xy from the target's offset xy
8407             //plus custom offset to get the aligned el's new offset xy
8408             var a1 = this.getAnchorXY(p1, true);
8409             var a2 = el.getAnchorXY(p2, false);
8410             var x = a2[0] - a1[0] + o[0];
8411             var y = a2[1] - a1[1] + o[1];
8412             if(c){
8413                 //constrain the aligned el to viewport if necessary
8414                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8415                 // 5px of margin for ie
8416                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8417
8418                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8419                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8420                 //otherwise swap the aligned el to the opposite border of the target.
8421                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8422                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8423                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8424                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8425
8426                var doc = document;
8427                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8428                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8429
8430                if((x+w) > dw + scrollX){
8431                     x = swapX ? r.left-w : dw+scrollX-w;
8432                 }
8433                if(x < scrollX){
8434                    x = swapX ? r.right : scrollX;
8435                }
8436                if((y+h) > dh + scrollY){
8437                     y = swapY ? r.top-h : dh+scrollY-h;
8438                 }
8439                if (y < scrollY){
8440                    y = swapY ? r.bottom : scrollY;
8441                }
8442             }
8443             return [x,y];
8444         },
8445
8446         // private
8447         getConstrainToXY : function(){
8448             var os = {top:0, left:0, bottom:0, right: 0};
8449
8450             return function(el, local, offsets, proposedXY){
8451                 el = Roo.get(el);
8452                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8453
8454                 var vw, vh, vx = 0, vy = 0;
8455                 if(el.dom == document.body || el.dom == document){
8456                     vw = Roo.lib.Dom.getViewWidth();
8457                     vh = Roo.lib.Dom.getViewHeight();
8458                 }else{
8459                     vw = el.dom.clientWidth;
8460                     vh = el.dom.clientHeight;
8461                     if(!local){
8462                         var vxy = el.getXY();
8463                         vx = vxy[0];
8464                         vy = vxy[1];
8465                     }
8466                 }
8467
8468                 var s = el.getScroll();
8469
8470                 vx += offsets.left + s.left;
8471                 vy += offsets.top + s.top;
8472
8473                 vw -= offsets.right;
8474                 vh -= offsets.bottom;
8475
8476                 var vr = vx+vw;
8477                 var vb = vy+vh;
8478
8479                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8480                 var x = xy[0], y = xy[1];
8481                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8482
8483                 // only move it if it needs it
8484                 var moved = false;
8485
8486                 // first validate right/bottom
8487                 if((x + w) > vr){
8488                     x = vr - w;
8489                     moved = true;
8490                 }
8491                 if((y + h) > vb){
8492                     y = vb - h;
8493                     moved = true;
8494                 }
8495                 // then make sure top/left isn't negative
8496                 if(x < vx){
8497                     x = vx;
8498                     moved = true;
8499                 }
8500                 if(y < vy){
8501                     y = vy;
8502                     moved = true;
8503                 }
8504                 return moved ? [x, y] : false;
8505             };
8506         }(),
8507
8508         // private
8509         adjustForConstraints : function(xy, parent, offsets){
8510             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8511         },
8512
8513         /**
8514          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8515          * document it aligns it to the viewport.
8516          * The position parameter is optional, and can be specified in any one of the following formats:
8517          * <ul>
8518          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8519          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8520          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8521          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8522          *   <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
8523          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8524          * </ul>
8525          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8526          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8527          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8528          * that specified in order to enforce the viewport constraints.
8529          * Following are all of the supported anchor positions:
8530     <pre>
8531     Value  Description
8532     -----  -----------------------------
8533     tl     The top left corner (default)
8534     t      The center of the top edge
8535     tr     The top right corner
8536     l      The center of the left edge
8537     c      In the center of the element
8538     r      The center of the right edge
8539     bl     The bottom left corner
8540     b      The center of the bottom edge
8541     br     The bottom right corner
8542     </pre>
8543     Example Usage:
8544     <pre><code>
8545     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8546     el.alignTo("other-el");
8547
8548     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8549     el.alignTo("other-el", "tr?");
8550
8551     // align the bottom right corner of el with the center left edge of other-el
8552     el.alignTo("other-el", "br-l?");
8553
8554     // align the center of el with the bottom left corner of other-el and
8555     // adjust the x position by -6 pixels (and the y position by 0)
8556     el.alignTo("other-el", "c-bl", [-6, 0]);
8557     </code></pre>
8558          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8559          * @param {String} position The position to align to.
8560          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8561          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8562          * @return {Roo.Element} this
8563          */
8564         alignTo : function(element, position, offsets, animate){
8565             var xy = this.getAlignToXY(element, position, offsets);
8566             this.setXY(xy, this.preanim(arguments, 3));
8567             return this;
8568         },
8569
8570         /**
8571          * Anchors an element to another element and realigns it when the window is resized.
8572          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8573          * @param {String} position The position to align to.
8574          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8575          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8576          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8577          * is a number, it is used as the buffer delay (defaults to 50ms).
8578          * @param {Function} callback The function to call after the animation finishes
8579          * @return {Roo.Element} this
8580          */
8581         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8582             var action = function(){
8583                 this.alignTo(el, alignment, offsets, animate);
8584                 Roo.callback(callback, this);
8585             };
8586             Roo.EventManager.onWindowResize(action, this);
8587             var tm = typeof monitorScroll;
8588             if(tm != 'undefined'){
8589                 Roo.EventManager.on(window, 'scroll', action, this,
8590                     {buffer: tm == 'number' ? monitorScroll : 50});
8591             }
8592             action.call(this); // align immediately
8593             return this;
8594         },
8595         /**
8596          * Clears any opacity settings from this element. Required in some cases for IE.
8597          * @return {Roo.Element} this
8598          */
8599         clearOpacity : function(){
8600             if (window.ActiveXObject) {
8601                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8602                     this.dom.style.filter = "";
8603                 }
8604             } else {
8605                 this.dom.style.opacity = "";
8606                 this.dom.style["-moz-opacity"] = "";
8607                 this.dom.style["-khtml-opacity"] = "";
8608             }
8609             return this;
8610         },
8611
8612         /**
8613          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8614          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8615          * @return {Roo.Element} this
8616          */
8617         hide : function(animate){
8618             this.setVisible(false, this.preanim(arguments, 0));
8619             return this;
8620         },
8621
8622         /**
8623         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8624         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8625          * @return {Roo.Element} this
8626          */
8627         show : function(animate){
8628             this.setVisible(true, this.preanim(arguments, 0));
8629             return this;
8630         },
8631
8632         /**
8633          * @private Test if size has a unit, otherwise appends the default
8634          */
8635         addUnits : function(size){
8636             return Roo.Element.addUnits(size, this.defaultUnit);
8637         },
8638
8639         /**
8640          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8641          * @return {Roo.Element} this
8642          */
8643         beginMeasure : function(){
8644             var el = this.dom;
8645             if(el.offsetWidth || el.offsetHeight){
8646                 return this; // offsets work already
8647             }
8648             var changed = [];
8649             var p = this.dom, b = document.body; // start with this element
8650             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8651                 var pe = Roo.get(p);
8652                 if(pe.getStyle('display') == 'none'){
8653                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8654                     p.style.visibility = "hidden";
8655                     p.style.display = "block";
8656                 }
8657                 p = p.parentNode;
8658             }
8659             this._measureChanged = changed;
8660             return this;
8661
8662         },
8663
8664         /**
8665          * Restores displays to before beginMeasure was called
8666          * @return {Roo.Element} this
8667          */
8668         endMeasure : function(){
8669             var changed = this._measureChanged;
8670             if(changed){
8671                 for(var i = 0, len = changed.length; i < len; i++) {
8672                     var r = changed[i];
8673                     r.el.style.visibility = r.visibility;
8674                     r.el.style.display = "none";
8675                 }
8676                 this._measureChanged = null;
8677             }
8678             return this;
8679         },
8680
8681         /**
8682         * Update the innerHTML of this element, optionally searching for and processing scripts
8683         * @param {String} html The new HTML
8684         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8685         * @param {Function} callback For async script loading you can be noticed when the update completes
8686         * @return {Roo.Element} this
8687          */
8688         update : function(html, loadScripts, callback){
8689             if(typeof html == "undefined"){
8690                 html = "";
8691             }
8692             if(loadScripts !== true){
8693                 this.dom.innerHTML = html;
8694                 if(typeof callback == "function"){
8695                     callback();
8696                 }
8697                 return this;
8698             }
8699             var id = Roo.id();
8700             var dom = this.dom;
8701
8702             html += '<span id="' + id + '"></span>';
8703
8704             E.onAvailable(id, function(){
8705                 var hd = document.getElementsByTagName("head")[0];
8706                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8707                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8708                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8709
8710                 var match;
8711                 while(match = re.exec(html)){
8712                     var attrs = match[1];
8713                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8714                     if(srcMatch && srcMatch[2]){
8715                        var s = document.createElement("script");
8716                        s.src = srcMatch[2];
8717                        var typeMatch = attrs.match(typeRe);
8718                        if(typeMatch && typeMatch[2]){
8719                            s.type = typeMatch[2];
8720                        }
8721                        hd.appendChild(s);
8722                     }else if(match[2] && match[2].length > 0){
8723                         if(window.execScript) {
8724                            window.execScript(match[2]);
8725                         } else {
8726                             /**
8727                              * eval:var:id
8728                              * eval:var:dom
8729                              * eval:var:html
8730                              * 
8731                              */
8732                            window.eval(match[2]);
8733                         }
8734                     }
8735                 }
8736                 var el = document.getElementById(id);
8737                 if(el){el.parentNode.removeChild(el);}
8738                 if(typeof callback == "function"){
8739                     callback();
8740                 }
8741             });
8742             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8743             return this;
8744         },
8745
8746         /**
8747          * Direct access to the UpdateManager update() method (takes the same parameters).
8748          * @param {String/Function} url The url for this request or a function to call to get the url
8749          * @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}
8750          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8751          * @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.
8752          * @return {Roo.Element} this
8753          */
8754         load : function(){
8755             var um = this.getUpdateManager();
8756             um.update.apply(um, arguments);
8757             return this;
8758         },
8759
8760         /**
8761         * Gets this element's UpdateManager
8762         * @return {Roo.UpdateManager} The UpdateManager
8763         */
8764         getUpdateManager : function(){
8765             if(!this.updateManager){
8766                 this.updateManager = new Roo.UpdateManager(this);
8767             }
8768             return this.updateManager;
8769         },
8770
8771         /**
8772          * Disables text selection for this element (normalized across browsers)
8773          * @return {Roo.Element} this
8774          */
8775         unselectable : function(){
8776             this.dom.unselectable = "on";
8777             this.swallowEvent("selectstart", true);
8778             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8779             this.addClass("x-unselectable");
8780             return this;
8781         },
8782
8783         /**
8784         * Calculates the x, y to center this element on the screen
8785         * @return {Array} The x, y values [x, y]
8786         */
8787         getCenterXY : function(){
8788             return this.getAlignToXY(document, 'c-c');
8789         },
8790
8791         /**
8792         * Centers the Element in either the viewport, or another Element.
8793         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8794         */
8795         center : function(centerIn){
8796             this.alignTo(centerIn || document, 'c-c');
8797             return this;
8798         },
8799
8800         /**
8801          * Tests various css rules/browsers to determine if this element uses a border box
8802          * @return {Boolean}
8803          */
8804         isBorderBox : function(){
8805             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8806         },
8807
8808         /**
8809          * Return a box {x, y, width, height} that can be used to set another elements
8810          * size/location to match this element.
8811          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8812          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8813          * @return {Object} box An object in the format {x, y, width, height}
8814          */
8815         getBox : function(contentBox, local){
8816             var xy;
8817             if(!local){
8818                 xy = this.getXY();
8819             }else{
8820                 var left = parseInt(this.getStyle("left"), 10) || 0;
8821                 var top = parseInt(this.getStyle("top"), 10) || 0;
8822                 xy = [left, top];
8823             }
8824             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8825             if(!contentBox){
8826                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8827             }else{
8828                 var l = this.getBorderWidth("l")+this.getPadding("l");
8829                 var r = this.getBorderWidth("r")+this.getPadding("r");
8830                 var t = this.getBorderWidth("t")+this.getPadding("t");
8831                 var b = this.getBorderWidth("b")+this.getPadding("b");
8832                 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)};
8833             }
8834             bx.right = bx.x + bx.width;
8835             bx.bottom = bx.y + bx.height;
8836             return bx;
8837         },
8838
8839         /**
8840          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8841          for more information about the sides.
8842          * @param {String} sides
8843          * @return {Number}
8844          */
8845         getFrameWidth : function(sides, onlyContentBox){
8846             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8847         },
8848
8849         /**
8850          * 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.
8851          * @param {Object} box The box to fill {x, y, width, height}
8852          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8853          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8854          * @return {Roo.Element} this
8855          */
8856         setBox : function(box, adjust, animate){
8857             var w = box.width, h = box.height;
8858             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8859                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8860                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8861             }
8862             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8863             return this;
8864         },
8865
8866         /**
8867          * Forces the browser to repaint this element
8868          * @return {Roo.Element} this
8869          */
8870          repaint : function(){
8871             var dom = this.dom;
8872             this.addClass("x-repaint");
8873             setTimeout(function(){
8874                 Roo.get(dom).removeClass("x-repaint");
8875             }, 1);
8876             return this;
8877         },
8878
8879         /**
8880          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8881          * then it returns the calculated width of the sides (see getPadding)
8882          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8883          * @return {Object/Number}
8884          */
8885         getMargins : function(side){
8886             if(!side){
8887                 return {
8888                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8889                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8890                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8891                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8892                 };
8893             }else{
8894                 return this.addStyles(side, El.margins);
8895              }
8896         },
8897
8898         // private
8899         addStyles : function(sides, styles){
8900             var val = 0, v, w;
8901             for(var i = 0, len = sides.length; i < len; i++){
8902                 v = this.getStyle(styles[sides.charAt(i)]);
8903                 if(v){
8904                      w = parseInt(v, 10);
8905                      if(w){ val += w; }
8906                 }
8907             }
8908             return val;
8909         },
8910
8911         /**
8912          * Creates a proxy element of this element
8913          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8914          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8915          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8916          * @return {Roo.Element} The new proxy element
8917          */
8918         createProxy : function(config, renderTo, matchBox){
8919             if(renderTo){
8920                 renderTo = Roo.getDom(renderTo);
8921             }else{
8922                 renderTo = document.body;
8923             }
8924             config = typeof config == "object" ?
8925                 config : {tag : "div", cls: config};
8926             var proxy = Roo.DomHelper.append(renderTo, config, true);
8927             if(matchBox){
8928                proxy.setBox(this.getBox());
8929             }
8930             return proxy;
8931         },
8932
8933         /**
8934          * Puts a mask over this element to disable user interaction. Requires core.css.
8935          * This method can only be applied to elements which accept child nodes.
8936          * @param {String} msg (optional) A message to display in the mask
8937          * @param {String} msgCls (optional) A css class to apply to the msg element
8938          * @return {Element} The mask  element
8939          */
8940         mask : function(msg, msgCls)
8941         {
8942             if(this.getStyle("position") == "static"){
8943                 this.setStyle("position", "relative");
8944             }
8945             if(!this._mask){
8946                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8947             }
8948             this.addClass("x-masked");
8949             this._mask.setDisplayed(true);
8950             
8951             // we wander
8952             var z = 0;
8953             var dom = this.dom
8954             while (dom && dom.style) {
8955                 if (!isNaN(parseInt(dom.style.zIndex))) {
8956                     z = Math.max(z, parseInt(dom.style.zIndex));
8957                 }
8958                 dom = dom.parentNode;
8959             }
8960             // if we are masking the body - then it hides everything..
8961             if (this.dom == document.body) {
8962                 z = 1000000;
8963                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8964                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8965             }
8966            
8967             if(typeof msg == 'string'){
8968                 if(!this._maskMsg){
8969                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8970                 }
8971                 var mm = this._maskMsg;
8972                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8973                 mm.dom.firstChild.innerHTML = msg;
8974                 mm.setDisplayed(true);
8975                 mm.center(this);
8976                 mm.setStyle('z-index', z + 102);
8977             }
8978             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8979                 this._mask.setHeight(this.getHeight());
8980             }
8981             this._mask.setStyle('z-index', z + 100);
8982             
8983             return this._mask;
8984         },
8985
8986         /**
8987          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8988          * it is cached for reuse.
8989          */
8990         unmask : function(removeEl){
8991             if(this._mask){
8992                 if(removeEl === true){
8993                     this._mask.remove();
8994                     delete this._mask;
8995                     if(this._maskMsg){
8996                         this._maskMsg.remove();
8997                         delete this._maskMsg;
8998                     }
8999                 }else{
9000                     this._mask.setDisplayed(false);
9001                     if(this._maskMsg){
9002                         this._maskMsg.setDisplayed(false);
9003                     }
9004                 }
9005             }
9006             this.removeClass("x-masked");
9007         },
9008
9009         /**
9010          * Returns true if this element is masked
9011          * @return {Boolean}
9012          */
9013         isMasked : function(){
9014             return this._mask && this._mask.isVisible();
9015         },
9016
9017         /**
9018          * Creates an iframe shim for this element to keep selects and other windowed objects from
9019          * showing through.
9020          * @return {Roo.Element} The new shim element
9021          */
9022         createShim : function(){
9023             var el = document.createElement('iframe');
9024             el.frameBorder = 'no';
9025             el.className = 'roo-shim';
9026             if(Roo.isIE && Roo.isSecure){
9027                 el.src = Roo.SSL_SECURE_URL;
9028             }
9029             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9030             shim.autoBoxAdjust = false;
9031             return shim;
9032         },
9033
9034         /**
9035          * Removes this element from the DOM and deletes it from the cache
9036          */
9037         remove : function(){
9038             if(this.dom.parentNode){
9039                 this.dom.parentNode.removeChild(this.dom);
9040             }
9041             delete El.cache[this.dom.id];
9042         },
9043
9044         /**
9045          * Sets up event handlers to add and remove a css class when the mouse is over this element
9046          * @param {String} className
9047          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9048          * mouseout events for children elements
9049          * @return {Roo.Element} this
9050          */
9051         addClassOnOver : function(className, preventFlicker){
9052             this.on("mouseover", function(){
9053                 Roo.fly(this, '_internal').addClass(className);
9054             }, this.dom);
9055             var removeFn = function(e){
9056                 if(preventFlicker !== true || !e.within(this, true)){
9057                     Roo.fly(this, '_internal').removeClass(className);
9058                 }
9059             };
9060             this.on("mouseout", removeFn, this.dom);
9061             return this;
9062         },
9063
9064         /**
9065          * Sets up event handlers to add and remove a css class when this element has the focus
9066          * @param {String} className
9067          * @return {Roo.Element} this
9068          */
9069         addClassOnFocus : function(className){
9070             this.on("focus", function(){
9071                 Roo.fly(this, '_internal').addClass(className);
9072             }, this.dom);
9073             this.on("blur", function(){
9074                 Roo.fly(this, '_internal').removeClass(className);
9075             }, this.dom);
9076             return this;
9077         },
9078         /**
9079          * 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)
9080          * @param {String} className
9081          * @return {Roo.Element} this
9082          */
9083         addClassOnClick : function(className){
9084             var dom = this.dom;
9085             this.on("mousedown", function(){
9086                 Roo.fly(dom, '_internal').addClass(className);
9087                 var d = Roo.get(document);
9088                 var fn = function(){
9089                     Roo.fly(dom, '_internal').removeClass(className);
9090                     d.removeListener("mouseup", fn);
9091                 };
9092                 d.on("mouseup", fn);
9093             });
9094             return this;
9095         },
9096
9097         /**
9098          * Stops the specified event from bubbling and optionally prevents the default action
9099          * @param {String} eventName
9100          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9101          * @return {Roo.Element} this
9102          */
9103         swallowEvent : function(eventName, preventDefault){
9104             var fn = function(e){
9105                 e.stopPropagation();
9106                 if(preventDefault){
9107                     e.preventDefault();
9108                 }
9109             };
9110             if(eventName instanceof Array){
9111                 for(var i = 0, len = eventName.length; i < len; i++){
9112                      this.on(eventName[i], fn);
9113                 }
9114                 return this;
9115             }
9116             this.on(eventName, fn);
9117             return this;
9118         },
9119
9120         /**
9121          * @private
9122          */
9123       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9124
9125         /**
9126          * Sizes this element to its parent element's dimensions performing
9127          * neccessary box adjustments.
9128          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9129          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9130          * @return {Roo.Element} this
9131          */
9132         fitToParent : function(monitorResize, targetParent) {
9133           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9134           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9135           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9136             return;
9137           }
9138           var p = Roo.get(targetParent || this.dom.parentNode);
9139           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9140           if (monitorResize === true) {
9141             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9142             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9143           }
9144           return this;
9145         },
9146
9147         /**
9148          * Gets the next sibling, skipping text nodes
9149          * @return {HTMLElement} The next sibling or null
9150          */
9151         getNextSibling : function(){
9152             var n = this.dom.nextSibling;
9153             while(n && n.nodeType != 1){
9154                 n = n.nextSibling;
9155             }
9156             return n;
9157         },
9158
9159         /**
9160          * Gets the previous sibling, skipping text nodes
9161          * @return {HTMLElement} The previous sibling or null
9162          */
9163         getPrevSibling : function(){
9164             var n = this.dom.previousSibling;
9165             while(n && n.nodeType != 1){
9166                 n = n.previousSibling;
9167             }
9168             return n;
9169         },
9170
9171
9172         /**
9173          * Appends the passed element(s) to this element
9174          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9175          * @return {Roo.Element} this
9176          */
9177         appendChild: function(el){
9178             el = Roo.get(el);
9179             el.appendTo(this);
9180             return this;
9181         },
9182
9183         /**
9184          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9185          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9186          * automatically generated with the specified attributes.
9187          * @param {HTMLElement} insertBefore (optional) a child element of this element
9188          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9189          * @return {Roo.Element} The new child element
9190          */
9191         createChild: function(config, insertBefore, returnDom){
9192             config = config || {tag:'div'};
9193             if(insertBefore){
9194                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9195             }
9196             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9197         },
9198
9199         /**
9200          * Appends this element to the passed element
9201          * @param {String/HTMLElement/Element} el The new parent element
9202          * @return {Roo.Element} this
9203          */
9204         appendTo: function(el){
9205             el = Roo.getDom(el);
9206             el.appendChild(this.dom);
9207             return this;
9208         },
9209
9210         /**
9211          * Inserts this element before the passed element in the DOM
9212          * @param {String/HTMLElement/Element} el The element to insert before
9213          * @return {Roo.Element} this
9214          */
9215         insertBefore: function(el){
9216             el = Roo.getDom(el);
9217             el.parentNode.insertBefore(this.dom, el);
9218             return this;
9219         },
9220
9221         /**
9222          * Inserts this element after the passed element in the DOM
9223          * @param {String/HTMLElement/Element} el The element to insert after
9224          * @return {Roo.Element} this
9225          */
9226         insertAfter: function(el){
9227             el = Roo.getDom(el);
9228             el.parentNode.insertBefore(this.dom, el.nextSibling);
9229             return this;
9230         },
9231
9232         /**
9233          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9234          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9235          * @return {Roo.Element} The new child
9236          */
9237         insertFirst: function(el, returnDom){
9238             el = el || {};
9239             if(typeof el == 'object' && !el.nodeType){ // dh config
9240                 return this.createChild(el, this.dom.firstChild, returnDom);
9241             }else{
9242                 el = Roo.getDom(el);
9243                 this.dom.insertBefore(el, this.dom.firstChild);
9244                 return !returnDom ? Roo.get(el) : el;
9245             }
9246         },
9247
9248         /**
9249          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9250          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9251          * @param {String} where (optional) 'before' or 'after' defaults to before
9252          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9253          * @return {Roo.Element} the inserted Element
9254          */
9255         insertSibling: function(el, where, returnDom){
9256             where = where ? where.toLowerCase() : 'before';
9257             el = el || {};
9258             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9259
9260             if(typeof el == 'object' && !el.nodeType){ // dh config
9261                 if(where == 'after' && !this.dom.nextSibling){
9262                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9263                 }else{
9264                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9265                 }
9266
9267             }else{
9268                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9269                             where == 'before' ? this.dom : this.dom.nextSibling);
9270                 if(!returnDom){
9271                     rt = Roo.get(rt);
9272                 }
9273             }
9274             return rt;
9275         },
9276
9277         /**
9278          * Creates and wraps this element with another element
9279          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9280          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9281          * @return {HTMLElement/Element} The newly created wrapper element
9282          */
9283         wrap: function(config, returnDom){
9284             if(!config){
9285                 config = {tag: "div"};
9286             }
9287             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9288             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9289             return newEl;
9290         },
9291
9292         /**
9293          * Replaces the passed element with this element
9294          * @param {String/HTMLElement/Element} el The element to replace
9295          * @return {Roo.Element} this
9296          */
9297         replace: function(el){
9298             el = Roo.get(el);
9299             this.insertBefore(el);
9300             el.remove();
9301             return this;
9302         },
9303
9304         /**
9305          * Inserts an html fragment into this element
9306          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9307          * @param {String} html The HTML fragment
9308          * @param {Boolean} returnEl True to return an Roo.Element
9309          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9310          */
9311         insertHtml : function(where, html, returnEl){
9312             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9313             return returnEl ? Roo.get(el) : el;
9314         },
9315
9316         /**
9317          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9318          * @param {Object} o The object with the attributes
9319          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9320          * @return {Roo.Element} this
9321          */
9322         set : function(o, useSet){
9323             var el = this.dom;
9324             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9325             for(var attr in o){
9326                 if(attr == "style" || typeof o[attr] == "function") continue;
9327                 if(attr=="cls"){
9328                     el.className = o["cls"];
9329                 }else{
9330                     if(useSet) el.setAttribute(attr, o[attr]);
9331                     else el[attr] = o[attr];
9332                 }
9333             }
9334             if(o.style){
9335                 Roo.DomHelper.applyStyles(el, o.style);
9336             }
9337             return this;
9338         },
9339
9340         /**
9341          * Convenience method for constructing a KeyMap
9342          * @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:
9343          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9344          * @param {Function} fn The function to call
9345          * @param {Object} scope (optional) The scope of the function
9346          * @return {Roo.KeyMap} The KeyMap created
9347          */
9348         addKeyListener : function(key, fn, scope){
9349             var config;
9350             if(typeof key != "object" || key instanceof Array){
9351                 config = {
9352                     key: key,
9353                     fn: fn,
9354                     scope: scope
9355                 };
9356             }else{
9357                 config = {
9358                     key : key.key,
9359                     shift : key.shift,
9360                     ctrl : key.ctrl,
9361                     alt : key.alt,
9362                     fn: fn,
9363                     scope: scope
9364                 };
9365             }
9366             return new Roo.KeyMap(this, config);
9367         },
9368
9369         /**
9370          * Creates a KeyMap for this element
9371          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9372          * @return {Roo.KeyMap} The KeyMap created
9373          */
9374         addKeyMap : function(config){
9375             return new Roo.KeyMap(this, config);
9376         },
9377
9378         /**
9379          * Returns true if this element is scrollable.
9380          * @return {Boolean}
9381          */
9382          isScrollable : function(){
9383             var dom = this.dom;
9384             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9385         },
9386
9387         /**
9388          * 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().
9389          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9390          * @param {Number} value The new scroll value
9391          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9392          * @return {Element} this
9393          */
9394
9395         scrollTo : function(side, value, animate){
9396             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9397             if(!animate || !A){
9398                 this.dom[prop] = value;
9399             }else{
9400                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9401                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9402             }
9403             return this;
9404         },
9405
9406         /**
9407          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9408          * within this element's scrollable range.
9409          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9410          * @param {Number} distance How far to scroll the element in pixels
9411          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9412          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9413          * was scrolled as far as it could go.
9414          */
9415          scroll : function(direction, distance, animate){
9416              if(!this.isScrollable()){
9417                  return;
9418              }
9419              var el = this.dom;
9420              var l = el.scrollLeft, t = el.scrollTop;
9421              var w = el.scrollWidth, h = el.scrollHeight;
9422              var cw = el.clientWidth, ch = el.clientHeight;
9423              direction = direction.toLowerCase();
9424              var scrolled = false;
9425              var a = this.preanim(arguments, 2);
9426              switch(direction){
9427                  case "l":
9428                  case "left":
9429                      if(w - l > cw){
9430                          var v = Math.min(l + distance, w-cw);
9431                          this.scrollTo("left", v, a);
9432                          scrolled = true;
9433                      }
9434                      break;
9435                 case "r":
9436                 case "right":
9437                      if(l > 0){
9438                          var v = Math.max(l - distance, 0);
9439                          this.scrollTo("left", v, a);
9440                          scrolled = true;
9441                      }
9442                      break;
9443                 case "t":
9444                 case "top":
9445                 case "up":
9446                      if(t > 0){
9447                          var v = Math.max(t - distance, 0);
9448                          this.scrollTo("top", v, a);
9449                          scrolled = true;
9450                      }
9451                      break;
9452                 case "b":
9453                 case "bottom":
9454                 case "down":
9455                      if(h - t > ch){
9456                          var v = Math.min(t + distance, h-ch);
9457                          this.scrollTo("top", v, a);
9458                          scrolled = true;
9459                      }
9460                      break;
9461              }
9462              return scrolled;
9463         },
9464
9465         /**
9466          * Translates the passed page coordinates into left/top css values for this element
9467          * @param {Number/Array} x The page x or an array containing [x, y]
9468          * @param {Number} y The page y
9469          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9470          */
9471         translatePoints : function(x, y){
9472             if(typeof x == 'object' || x instanceof Array){
9473                 y = x[1]; x = x[0];
9474             }
9475             var p = this.getStyle('position');
9476             var o = this.getXY();
9477
9478             var l = parseInt(this.getStyle('left'), 10);
9479             var t = parseInt(this.getStyle('top'), 10);
9480
9481             if(isNaN(l)){
9482                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9483             }
9484             if(isNaN(t)){
9485                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9486             }
9487
9488             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9489         },
9490
9491         /**
9492          * Returns the current scroll position of the element.
9493          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9494          */
9495         getScroll : function(){
9496             var d = this.dom, doc = document;
9497             if(d == doc || d == doc.body){
9498                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9499                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9500                 return {left: l, top: t};
9501             }else{
9502                 return {left: d.scrollLeft, top: d.scrollTop};
9503             }
9504         },
9505
9506         /**
9507          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9508          * are convert to standard 6 digit hex color.
9509          * @param {String} attr The css attribute
9510          * @param {String} defaultValue The default value to use when a valid color isn't found
9511          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9512          * YUI color anims.
9513          */
9514         getColor : function(attr, defaultValue, prefix){
9515             var v = this.getStyle(attr);
9516             if(!v || v == "transparent" || v == "inherit") {
9517                 return defaultValue;
9518             }
9519             var color = typeof prefix == "undefined" ? "#" : prefix;
9520             if(v.substr(0, 4) == "rgb("){
9521                 var rvs = v.slice(4, v.length -1).split(",");
9522                 for(var i = 0; i < 3; i++){
9523                     var h = parseInt(rvs[i]).toString(16);
9524                     if(h < 16){
9525                         h = "0" + h;
9526                     }
9527                     color += h;
9528                 }
9529             } else {
9530                 if(v.substr(0, 1) == "#"){
9531                     if(v.length == 4) {
9532                         for(var i = 1; i < 4; i++){
9533                             var c = v.charAt(i);
9534                             color +=  c + c;
9535                         }
9536                     }else if(v.length == 7){
9537                         color += v.substr(1);
9538                     }
9539                 }
9540             }
9541             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9542         },
9543
9544         /**
9545          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9546          * gradient background, rounded corners and a 4-way shadow.
9547          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9548          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9549          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9550          * @return {Roo.Element} this
9551          */
9552         boxWrap : function(cls){
9553             cls = cls || 'x-box';
9554             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9555             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9556             return el;
9557         },
9558
9559         /**
9560          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9561          * @param {String} namespace The namespace in which to look for the attribute
9562          * @param {String} name The attribute name
9563          * @return {String} The attribute value
9564          */
9565         getAttributeNS : Roo.isIE ? function(ns, name){
9566             var d = this.dom;
9567             var type = typeof d[ns+":"+name];
9568             if(type != 'undefined' && type != 'unknown'){
9569                 return d[ns+":"+name];
9570             }
9571             return d[name];
9572         } : function(ns, name){
9573             var d = this.dom;
9574             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9575         },
9576         
9577         
9578         /**
9579          * Sets or Returns the value the dom attribute value
9580          * @param {String} name The attribute name
9581          * @param {String} value (optional) The value to set the attribute to
9582          * @return {String} The attribute value
9583          */
9584         attr : function(name){
9585             if (arguments.length > 1) {
9586                 this.dom.setAttribute(name, arguments[1]);
9587                 return arguments[1];
9588             }
9589             if (!this.dom.hasAttribute(name)) {
9590                 return undefined;
9591             }
9592             return this.dom.getAttribute(name);
9593         }
9594         
9595         
9596         
9597     };
9598
9599     var ep = El.prototype;
9600
9601     /**
9602      * Appends an event handler (Shorthand for addListener)
9603      * @param {String}   eventName     The type of event to append
9604      * @param {Function} fn        The method the event invokes
9605      * @param {Object} scope       (optional) The scope (this object) of the fn
9606      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9607      * @method
9608      */
9609     ep.on = ep.addListener;
9610         // backwards compat
9611     ep.mon = ep.addListener;
9612
9613     /**
9614      * Removes an event handler from this element (shorthand for removeListener)
9615      * @param {String} eventName the type of event to remove
9616      * @param {Function} fn the method the event invokes
9617      * @return {Roo.Element} this
9618      * @method
9619      */
9620     ep.un = ep.removeListener;
9621
9622     /**
9623      * true to automatically adjust width and height settings for box-model issues (default to true)
9624      */
9625     ep.autoBoxAdjust = true;
9626
9627     // private
9628     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9629
9630     // private
9631     El.addUnits = function(v, defaultUnit){
9632         if(v === "" || v == "auto"){
9633             return v;
9634         }
9635         if(v === undefined){
9636             return '';
9637         }
9638         if(typeof v == "number" || !El.unitPattern.test(v)){
9639             return v + (defaultUnit || 'px');
9640         }
9641         return v;
9642     };
9643
9644     // special markup used throughout Roo when box wrapping elements
9645     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>';
9646     /**
9647      * Visibility mode constant - Use visibility to hide element
9648      * @static
9649      * @type Number
9650      */
9651     El.VISIBILITY = 1;
9652     /**
9653      * Visibility mode constant - Use display to hide element
9654      * @static
9655      * @type Number
9656      */
9657     El.DISPLAY = 2;
9658
9659     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9660     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9661     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9662
9663
9664
9665     /**
9666      * @private
9667      */
9668     El.cache = {};
9669
9670     var docEl;
9671
9672     /**
9673      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9674      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9675      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9676      * @return {Element} The Element object
9677      * @static
9678      */
9679     El.get = function(el){
9680         var ex, elm, id;
9681         if(!el){ return null; }
9682         if(typeof el == "string"){ // element id
9683             if(!(elm = document.getElementById(el))){
9684                 return null;
9685             }
9686             if(ex = El.cache[el]){
9687                 ex.dom = elm;
9688             }else{
9689                 ex = El.cache[el] = new El(elm);
9690             }
9691             return ex;
9692         }else if(el.tagName){ // dom element
9693             if(!(id = el.id)){
9694                 id = Roo.id(el);
9695             }
9696             if(ex = El.cache[id]){
9697                 ex.dom = el;
9698             }else{
9699                 ex = El.cache[id] = new El(el);
9700             }
9701             return ex;
9702         }else if(el instanceof El){
9703             if(el != docEl){
9704                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9705                                                               // catch case where it hasn't been appended
9706                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9707             }
9708             return el;
9709         }else if(el.isComposite){
9710             return el;
9711         }else if(el instanceof Array){
9712             return El.select(el);
9713         }else if(el == document){
9714             // create a bogus element object representing the document object
9715             if(!docEl){
9716                 var f = function(){};
9717                 f.prototype = El.prototype;
9718                 docEl = new f();
9719                 docEl.dom = document;
9720             }
9721             return docEl;
9722         }
9723         return null;
9724     };
9725
9726     // private
9727     El.uncache = function(el){
9728         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9729             if(a[i]){
9730                 delete El.cache[a[i].id || a[i]];
9731             }
9732         }
9733     };
9734
9735     // private
9736     // Garbage collection - uncache elements/purge listeners on orphaned elements
9737     // so we don't hold a reference and cause the browser to retain them
9738     El.garbageCollect = function(){
9739         if(!Roo.enableGarbageCollector){
9740             clearInterval(El.collectorThread);
9741             return;
9742         }
9743         for(var eid in El.cache){
9744             var el = El.cache[eid], d = el.dom;
9745             // -------------------------------------------------------
9746             // Determining what is garbage:
9747             // -------------------------------------------------------
9748             // !d
9749             // dom node is null, definitely garbage
9750             // -------------------------------------------------------
9751             // !d.parentNode
9752             // no parentNode == direct orphan, definitely garbage
9753             // -------------------------------------------------------
9754             // !d.offsetParent && !document.getElementById(eid)
9755             // display none elements have no offsetParent so we will
9756             // also try to look it up by it's id. However, check
9757             // offsetParent first so we don't do unneeded lookups.
9758             // This enables collection of elements that are not orphans
9759             // directly, but somewhere up the line they have an orphan
9760             // parent.
9761             // -------------------------------------------------------
9762             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9763                 delete El.cache[eid];
9764                 if(d && Roo.enableListenerCollection){
9765                     E.purgeElement(d);
9766                 }
9767             }
9768         }
9769     }
9770     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9771
9772
9773     // dom is optional
9774     El.Flyweight = function(dom){
9775         this.dom = dom;
9776     };
9777     El.Flyweight.prototype = El.prototype;
9778
9779     El._flyweights = {};
9780     /**
9781      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9782      * the dom node can be overwritten by other code.
9783      * @param {String/HTMLElement} el The dom node or id
9784      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9785      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9786      * @static
9787      * @return {Element} The shared Element object
9788      */
9789     El.fly = function(el, named){
9790         named = named || '_global';
9791         el = Roo.getDom(el);
9792         if(!el){
9793             return null;
9794         }
9795         if(!El._flyweights[named]){
9796             El._flyweights[named] = new El.Flyweight();
9797         }
9798         El._flyweights[named].dom = el;
9799         return El._flyweights[named];
9800     };
9801
9802     /**
9803      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9804      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9805      * Shorthand of {@link Roo.Element#get}
9806      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9807      * @return {Element} The Element object
9808      * @member Roo
9809      * @method get
9810      */
9811     Roo.get = El.get;
9812     /**
9813      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9814      * the dom node can be overwritten by other code.
9815      * Shorthand of {@link Roo.Element#fly}
9816      * @param {String/HTMLElement} el The dom node or id
9817      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9818      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9819      * @static
9820      * @return {Element} The shared Element object
9821      * @member Roo
9822      * @method fly
9823      */
9824     Roo.fly = El.fly;
9825
9826     // speedy lookup for elements never to box adjust
9827     var noBoxAdjust = Roo.isStrict ? {
9828         select:1
9829     } : {
9830         input:1, select:1, textarea:1
9831     };
9832     if(Roo.isIE || Roo.isGecko){
9833         noBoxAdjust['button'] = 1;
9834     }
9835
9836
9837     Roo.EventManager.on(window, 'unload', function(){
9838         delete El.cache;
9839         delete El._flyweights;
9840     });
9841 })();
9842
9843
9844
9845
9846 if(Roo.DomQuery){
9847     Roo.Element.selectorFunction = Roo.DomQuery.select;
9848 }
9849
9850 Roo.Element.select = function(selector, unique, root){
9851     var els;
9852     if(typeof selector == "string"){
9853         els = Roo.Element.selectorFunction(selector, root);
9854     }else if(selector.length !== undefined){
9855         els = selector;
9856     }else{
9857         throw "Invalid selector";
9858     }
9859     if(unique === true){
9860         return new Roo.CompositeElement(els);
9861     }else{
9862         return new Roo.CompositeElementLite(els);
9863     }
9864 };
9865 /**
9866  * Selects elements based on the passed CSS selector to enable working on them as 1.
9867  * @param {String/Array} selector The CSS selector or an array of elements
9868  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9869  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9870  * @return {CompositeElementLite/CompositeElement}
9871  * @member Roo
9872  * @method select
9873  */
9874 Roo.select = Roo.Element.select;
9875
9876
9877
9878
9879
9880
9881
9882
9883
9884
9885
9886
9887
9888
9889 /*
9890  * Based on:
9891  * Ext JS Library 1.1.1
9892  * Copyright(c) 2006-2007, Ext JS, LLC.
9893  *
9894  * Originally Released Under LGPL - original licence link has changed is not relivant.
9895  *
9896  * Fork - LGPL
9897  * <script type="text/javascript">
9898  */
9899
9900
9901
9902 //Notifies Element that fx methods are available
9903 Roo.enableFx = true;
9904
9905 /**
9906  * @class Roo.Fx
9907  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9908  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9909  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9910  * Element effects to work.</p><br/>
9911  *
9912  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9913  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9914  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9915  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9916  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9917  * expected results and should be done with care.</p><br/>
9918  *
9919  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9920  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9921 <pre>
9922 Value  Description
9923 -----  -----------------------------
9924 tl     The top left corner
9925 t      The center of the top edge
9926 tr     The top right corner
9927 l      The center of the left edge
9928 r      The center of the right edge
9929 bl     The bottom left corner
9930 b      The center of the bottom edge
9931 br     The bottom right corner
9932 </pre>
9933  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9934  * below are common options that can be passed to any Fx method.</b>
9935  * @cfg {Function} callback A function called when the effect is finished
9936  * @cfg {Object} scope The scope of the effect function
9937  * @cfg {String} easing A valid Easing value for the effect
9938  * @cfg {String} afterCls A css class to apply after the effect
9939  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9940  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9941  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9942  * effects that end with the element being visually hidden, ignored otherwise)
9943  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9944  * a function which returns such a specification that will be applied to the Element after the effect finishes
9945  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9946  * @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
9947  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9948  */
9949 Roo.Fx = {
9950         /**
9951          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9952          * origin for the slide effect.  This function automatically handles wrapping the element with
9953          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9954          * Usage:
9955          *<pre><code>
9956 // default: slide the element in from the top
9957 el.slideIn();
9958
9959 // custom: slide the element in from the right with a 2-second duration
9960 el.slideIn('r', { duration: 2 });
9961
9962 // common config options shown with default values
9963 el.slideIn('t', {
9964     easing: 'easeOut',
9965     duration: .5
9966 });
9967 </code></pre>
9968          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9969          * @param {Object} options (optional) Object literal with any of the Fx config options
9970          * @return {Roo.Element} The Element
9971          */
9972     slideIn : function(anchor, o){
9973         var el = this.getFxEl();
9974         o = o || {};
9975
9976         el.queueFx(o, function(){
9977
9978             anchor = anchor || "t";
9979
9980             // fix display to visibility
9981             this.fixDisplay();
9982
9983             // restore values after effect
9984             var r = this.getFxRestore();
9985             var b = this.getBox();
9986             // fixed size for slide
9987             this.setSize(b);
9988
9989             // wrap if needed
9990             var wrap = this.fxWrap(r.pos, o, "hidden");
9991
9992             var st = this.dom.style;
9993             st.visibility = "visible";
9994             st.position = "absolute";
9995
9996             // clear out temp styles after slide and unwrap
9997             var after = function(){
9998                 el.fxUnwrap(wrap, r.pos, o);
9999                 st.width = r.width;
10000                 st.height = r.height;
10001                 el.afterFx(o);
10002             };
10003             // time to calc the positions
10004             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10005
10006             switch(anchor.toLowerCase()){
10007                 case "t":
10008                     wrap.setSize(b.width, 0);
10009                     st.left = st.bottom = "0";
10010                     a = {height: bh};
10011                 break;
10012                 case "l":
10013                     wrap.setSize(0, b.height);
10014                     st.right = st.top = "0";
10015                     a = {width: bw};
10016                 break;
10017                 case "r":
10018                     wrap.setSize(0, b.height);
10019                     wrap.setX(b.right);
10020                     st.left = st.top = "0";
10021                     a = {width: bw, points: pt};
10022                 break;
10023                 case "b":
10024                     wrap.setSize(b.width, 0);
10025                     wrap.setY(b.bottom);
10026                     st.left = st.top = "0";
10027                     a = {height: bh, points: pt};
10028                 break;
10029                 case "tl":
10030                     wrap.setSize(0, 0);
10031                     st.right = st.bottom = "0";
10032                     a = {width: bw, height: bh};
10033                 break;
10034                 case "bl":
10035                     wrap.setSize(0, 0);
10036                     wrap.setY(b.y+b.height);
10037                     st.right = st.top = "0";
10038                     a = {width: bw, height: bh, points: pt};
10039                 break;
10040                 case "br":
10041                     wrap.setSize(0, 0);
10042                     wrap.setXY([b.right, b.bottom]);
10043                     st.left = st.top = "0";
10044                     a = {width: bw, height: bh, points: pt};
10045                 break;
10046                 case "tr":
10047                     wrap.setSize(0, 0);
10048                     wrap.setX(b.x+b.width);
10049                     st.left = st.bottom = "0";
10050                     a = {width: bw, height: bh, points: pt};
10051                 break;
10052             }
10053             this.dom.style.visibility = "visible";
10054             wrap.show();
10055
10056             arguments.callee.anim = wrap.fxanim(a,
10057                 o,
10058                 'motion',
10059                 .5,
10060                 'easeOut', after);
10061         });
10062         return this;
10063     },
10064     
10065         /**
10066          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10067          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10068          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10069          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10070          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10071          * Usage:
10072          *<pre><code>
10073 // default: slide the element out to the top
10074 el.slideOut();
10075
10076 // custom: slide the element out to the right with a 2-second duration
10077 el.slideOut('r', { duration: 2 });
10078
10079 // common config options shown with default values
10080 el.slideOut('t', {
10081     easing: 'easeOut',
10082     duration: .5,
10083     remove: false,
10084     useDisplay: false
10085 });
10086 </code></pre>
10087          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10088          * @param {Object} options (optional) Object literal with any of the Fx config options
10089          * @return {Roo.Element} The Element
10090          */
10091     slideOut : function(anchor, o){
10092         var el = this.getFxEl();
10093         o = o || {};
10094
10095         el.queueFx(o, function(){
10096
10097             anchor = anchor || "t";
10098
10099             // restore values after effect
10100             var r = this.getFxRestore();
10101             
10102             var b = this.getBox();
10103             // fixed size for slide
10104             this.setSize(b);
10105
10106             // wrap if needed
10107             var wrap = this.fxWrap(r.pos, o, "visible");
10108
10109             var st = this.dom.style;
10110             st.visibility = "visible";
10111             st.position = "absolute";
10112
10113             wrap.setSize(b);
10114
10115             var after = function(){
10116                 if(o.useDisplay){
10117                     el.setDisplayed(false);
10118                 }else{
10119                     el.hide();
10120                 }
10121
10122                 el.fxUnwrap(wrap, r.pos, o);
10123
10124                 st.width = r.width;
10125                 st.height = r.height;
10126
10127                 el.afterFx(o);
10128             };
10129
10130             var a, zero = {to: 0};
10131             switch(anchor.toLowerCase()){
10132                 case "t":
10133                     st.left = st.bottom = "0";
10134                     a = {height: zero};
10135                 break;
10136                 case "l":
10137                     st.right = st.top = "0";
10138                     a = {width: zero};
10139                 break;
10140                 case "r":
10141                     st.left = st.top = "0";
10142                     a = {width: zero, points: {to:[b.right, b.y]}};
10143                 break;
10144                 case "b":
10145                     st.left = st.top = "0";
10146                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10147                 break;
10148                 case "tl":
10149                     st.right = st.bottom = "0";
10150                     a = {width: zero, height: zero};
10151                 break;
10152                 case "bl":
10153                     st.right = st.top = "0";
10154                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10155                 break;
10156                 case "br":
10157                     st.left = st.top = "0";
10158                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10159                 break;
10160                 case "tr":
10161                     st.left = st.bottom = "0";
10162                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10163                 break;
10164             }
10165
10166             arguments.callee.anim = wrap.fxanim(a,
10167                 o,
10168                 'motion',
10169                 .5,
10170                 "easeOut", after);
10171         });
10172         return this;
10173     },
10174
10175         /**
10176          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10177          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10178          * The element must be removed from the DOM using the 'remove' config option if desired.
10179          * Usage:
10180          *<pre><code>
10181 // default
10182 el.puff();
10183
10184 // common config options shown with default values
10185 el.puff({
10186     easing: 'easeOut',
10187     duration: .5,
10188     remove: false,
10189     useDisplay: false
10190 });
10191 </code></pre>
10192          * @param {Object} options (optional) Object literal with any of the Fx config options
10193          * @return {Roo.Element} The Element
10194          */
10195     puff : function(o){
10196         var el = this.getFxEl();
10197         o = o || {};
10198
10199         el.queueFx(o, function(){
10200             this.clearOpacity();
10201             this.show();
10202
10203             // restore values after effect
10204             var r = this.getFxRestore();
10205             var st = this.dom.style;
10206
10207             var after = function(){
10208                 if(o.useDisplay){
10209                     el.setDisplayed(false);
10210                 }else{
10211                     el.hide();
10212                 }
10213
10214                 el.clearOpacity();
10215
10216                 el.setPositioning(r.pos);
10217                 st.width = r.width;
10218                 st.height = r.height;
10219                 st.fontSize = '';
10220                 el.afterFx(o);
10221             };
10222
10223             var width = this.getWidth();
10224             var height = this.getHeight();
10225
10226             arguments.callee.anim = this.fxanim({
10227                     width : {to: this.adjustWidth(width * 2)},
10228                     height : {to: this.adjustHeight(height * 2)},
10229                     points : {by: [-(width * .5), -(height * .5)]},
10230                     opacity : {to: 0},
10231                     fontSize: {to:200, unit: "%"}
10232                 },
10233                 o,
10234                 'motion',
10235                 .5,
10236                 "easeOut", after);
10237         });
10238         return this;
10239     },
10240
10241         /**
10242          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10243          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10244          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10245          * Usage:
10246          *<pre><code>
10247 // default
10248 el.switchOff();
10249
10250 // all config options shown with default values
10251 el.switchOff({
10252     easing: 'easeIn',
10253     duration: .3,
10254     remove: false,
10255     useDisplay: false
10256 });
10257 </code></pre>
10258          * @param {Object} options (optional) Object literal with any of the Fx config options
10259          * @return {Roo.Element} The Element
10260          */
10261     switchOff : function(o){
10262         var el = this.getFxEl();
10263         o = o || {};
10264
10265         el.queueFx(o, function(){
10266             this.clearOpacity();
10267             this.clip();
10268
10269             // restore values after effect
10270             var r = this.getFxRestore();
10271             var st = this.dom.style;
10272
10273             var after = function(){
10274                 if(o.useDisplay){
10275                     el.setDisplayed(false);
10276                 }else{
10277                     el.hide();
10278                 }
10279
10280                 el.clearOpacity();
10281                 el.setPositioning(r.pos);
10282                 st.width = r.width;
10283                 st.height = r.height;
10284
10285                 el.afterFx(o);
10286             };
10287
10288             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10289                 this.clearOpacity();
10290                 (function(){
10291                     this.fxanim({
10292                         height:{to:1},
10293                         points:{by:[0, this.getHeight() * .5]}
10294                     }, o, 'motion', 0.3, 'easeIn', after);
10295                 }).defer(100, this);
10296             });
10297         });
10298         return this;
10299     },
10300
10301     /**
10302      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10303      * changed using the "attr" config option) and then fading back to the original color. If no original
10304      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10305      * Usage:
10306 <pre><code>
10307 // default: highlight background to yellow
10308 el.highlight();
10309
10310 // custom: highlight foreground text to blue for 2 seconds
10311 el.highlight("0000ff", { attr: 'color', duration: 2 });
10312
10313 // common config options shown with default values
10314 el.highlight("ffff9c", {
10315     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10316     endColor: (current color) or "ffffff",
10317     easing: 'easeIn',
10318     duration: 1
10319 });
10320 </code></pre>
10321      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10322      * @param {Object} options (optional) Object literal with any of the Fx config options
10323      * @return {Roo.Element} The Element
10324      */ 
10325     highlight : function(color, o){
10326         var el = this.getFxEl();
10327         o = o || {};
10328
10329         el.queueFx(o, function(){
10330             color = color || "ffff9c";
10331             attr = o.attr || "backgroundColor";
10332
10333             this.clearOpacity();
10334             this.show();
10335
10336             var origColor = this.getColor(attr);
10337             var restoreColor = this.dom.style[attr];
10338             endColor = (o.endColor || origColor) || "ffffff";
10339
10340             var after = function(){
10341                 el.dom.style[attr] = restoreColor;
10342                 el.afterFx(o);
10343             };
10344
10345             var a = {};
10346             a[attr] = {from: color, to: endColor};
10347             arguments.callee.anim = this.fxanim(a,
10348                 o,
10349                 'color',
10350                 1,
10351                 'easeIn', after);
10352         });
10353         return this;
10354     },
10355
10356    /**
10357     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10358     * Usage:
10359 <pre><code>
10360 // default: a single light blue ripple
10361 el.frame();
10362
10363 // custom: 3 red ripples lasting 3 seconds total
10364 el.frame("ff0000", 3, { duration: 3 });
10365
10366 // common config options shown with default values
10367 el.frame("C3DAF9", 1, {
10368     duration: 1 //duration of entire animation (not each individual ripple)
10369     // Note: Easing is not configurable and will be ignored if included
10370 });
10371 </code></pre>
10372     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10373     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10374     * @param {Object} options (optional) Object literal with any of the Fx config options
10375     * @return {Roo.Element} The Element
10376     */
10377     frame : function(color, count, o){
10378         var el = this.getFxEl();
10379         o = o || {};
10380
10381         el.queueFx(o, function(){
10382             color = color || "#C3DAF9";
10383             if(color.length == 6){
10384                 color = "#" + color;
10385             }
10386             count = count || 1;
10387             duration = o.duration || 1;
10388             this.show();
10389
10390             var b = this.getBox();
10391             var animFn = function(){
10392                 var proxy = this.createProxy({
10393
10394                      style:{
10395                         visbility:"hidden",
10396                         position:"absolute",
10397                         "z-index":"35000", // yee haw
10398                         border:"0px solid " + color
10399                      }
10400                   });
10401                 var scale = Roo.isBorderBox ? 2 : 1;
10402                 proxy.animate({
10403                     top:{from:b.y, to:b.y - 20},
10404                     left:{from:b.x, to:b.x - 20},
10405                     borderWidth:{from:0, to:10},
10406                     opacity:{from:1, to:0},
10407                     height:{from:b.height, to:(b.height + (20*scale))},
10408                     width:{from:b.width, to:(b.width + (20*scale))}
10409                 }, duration, function(){
10410                     proxy.remove();
10411                 });
10412                 if(--count > 0){
10413                      animFn.defer((duration/2)*1000, this);
10414                 }else{
10415                     el.afterFx(o);
10416                 }
10417             };
10418             animFn.call(this);
10419         });
10420         return this;
10421     },
10422
10423    /**
10424     * Creates a pause before any subsequent queued effects begin.  If there are
10425     * no effects queued after the pause it will have no effect.
10426     * Usage:
10427 <pre><code>
10428 el.pause(1);
10429 </code></pre>
10430     * @param {Number} seconds The length of time to pause (in seconds)
10431     * @return {Roo.Element} The Element
10432     */
10433     pause : function(seconds){
10434         var el = this.getFxEl();
10435         var o = {};
10436
10437         el.queueFx(o, function(){
10438             setTimeout(function(){
10439                 el.afterFx(o);
10440             }, seconds * 1000);
10441         });
10442         return this;
10443     },
10444
10445    /**
10446     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10447     * using the "endOpacity" config option.
10448     * Usage:
10449 <pre><code>
10450 // default: fade in from opacity 0 to 100%
10451 el.fadeIn();
10452
10453 // custom: fade in from opacity 0 to 75% over 2 seconds
10454 el.fadeIn({ endOpacity: .75, duration: 2});
10455
10456 // common config options shown with default values
10457 el.fadeIn({
10458     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10459     easing: 'easeOut',
10460     duration: .5
10461 });
10462 </code></pre>
10463     * @param {Object} options (optional) Object literal with any of the Fx config options
10464     * @return {Roo.Element} The Element
10465     */
10466     fadeIn : function(o){
10467         var el = this.getFxEl();
10468         o = o || {};
10469         el.queueFx(o, function(){
10470             this.setOpacity(0);
10471             this.fixDisplay();
10472             this.dom.style.visibility = 'visible';
10473             var to = o.endOpacity || 1;
10474             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10475                 o, null, .5, "easeOut", function(){
10476                 if(to == 1){
10477                     this.clearOpacity();
10478                 }
10479                 el.afterFx(o);
10480             });
10481         });
10482         return this;
10483     },
10484
10485    /**
10486     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10487     * using the "endOpacity" config option.
10488     * Usage:
10489 <pre><code>
10490 // default: fade out from the element's current opacity to 0
10491 el.fadeOut();
10492
10493 // custom: fade out from the element's current opacity to 25% over 2 seconds
10494 el.fadeOut({ endOpacity: .25, duration: 2});
10495
10496 // common config options shown with default values
10497 el.fadeOut({
10498     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10499     easing: 'easeOut',
10500     duration: .5
10501     remove: false,
10502     useDisplay: false
10503 });
10504 </code></pre>
10505     * @param {Object} options (optional) Object literal with any of the Fx config options
10506     * @return {Roo.Element} The Element
10507     */
10508     fadeOut : function(o){
10509         var el = this.getFxEl();
10510         o = o || {};
10511         el.queueFx(o, function(){
10512             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10513                 o, null, .5, "easeOut", function(){
10514                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10515                      this.dom.style.display = "none";
10516                 }else{
10517                      this.dom.style.visibility = "hidden";
10518                 }
10519                 this.clearOpacity();
10520                 el.afterFx(o);
10521             });
10522         });
10523         return this;
10524     },
10525
10526    /**
10527     * Animates the transition of an element's dimensions from a starting height/width
10528     * to an ending height/width.
10529     * Usage:
10530 <pre><code>
10531 // change height and width to 100x100 pixels
10532 el.scale(100, 100);
10533
10534 // common config options shown with default values.  The height and width will default to
10535 // the element's existing values if passed as null.
10536 el.scale(
10537     [element's width],
10538     [element's height], {
10539     easing: 'easeOut',
10540     duration: .35
10541 });
10542 </code></pre>
10543     * @param {Number} width  The new width (pass undefined to keep the original width)
10544     * @param {Number} height  The new height (pass undefined to keep the original height)
10545     * @param {Object} options (optional) Object literal with any of the Fx config options
10546     * @return {Roo.Element} The Element
10547     */
10548     scale : function(w, h, o){
10549         this.shift(Roo.apply({}, o, {
10550             width: w,
10551             height: h
10552         }));
10553         return this;
10554     },
10555
10556    /**
10557     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10558     * Any of these properties not specified in the config object will not be changed.  This effect 
10559     * requires that at least one new dimension, position or opacity setting must be passed in on
10560     * the config object in order for the function to have any effect.
10561     * Usage:
10562 <pre><code>
10563 // slide the element horizontally to x position 200 while changing the height and opacity
10564 el.shift({ x: 200, height: 50, opacity: .8 });
10565
10566 // common config options shown with default values.
10567 el.shift({
10568     width: [element's width],
10569     height: [element's height],
10570     x: [element's x position],
10571     y: [element's y position],
10572     opacity: [element's opacity],
10573     easing: 'easeOut',
10574     duration: .35
10575 });
10576 </code></pre>
10577     * @param {Object} options  Object literal with any of the Fx config options
10578     * @return {Roo.Element} The Element
10579     */
10580     shift : function(o){
10581         var el = this.getFxEl();
10582         o = o || {};
10583         el.queueFx(o, function(){
10584             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10585             if(w !== undefined){
10586                 a.width = {to: this.adjustWidth(w)};
10587             }
10588             if(h !== undefined){
10589                 a.height = {to: this.adjustHeight(h)};
10590             }
10591             if(x !== undefined || y !== undefined){
10592                 a.points = {to: [
10593                     x !== undefined ? x : this.getX(),
10594                     y !== undefined ? y : this.getY()
10595                 ]};
10596             }
10597             if(op !== undefined){
10598                 a.opacity = {to: op};
10599             }
10600             if(o.xy !== undefined){
10601                 a.points = {to: o.xy};
10602             }
10603             arguments.callee.anim = this.fxanim(a,
10604                 o, 'motion', .35, "easeOut", function(){
10605                 el.afterFx(o);
10606             });
10607         });
10608         return this;
10609     },
10610
10611         /**
10612          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10613          * ending point of the effect.
10614          * Usage:
10615          *<pre><code>
10616 // default: slide the element downward while fading out
10617 el.ghost();
10618
10619 // custom: slide the element out to the right with a 2-second duration
10620 el.ghost('r', { duration: 2 });
10621
10622 // common config options shown with default values
10623 el.ghost('b', {
10624     easing: 'easeOut',
10625     duration: .5
10626     remove: false,
10627     useDisplay: false
10628 });
10629 </code></pre>
10630          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10631          * @param {Object} options (optional) Object literal with any of the Fx config options
10632          * @return {Roo.Element} The Element
10633          */
10634     ghost : function(anchor, o){
10635         var el = this.getFxEl();
10636         o = o || {};
10637
10638         el.queueFx(o, function(){
10639             anchor = anchor || "b";
10640
10641             // restore values after effect
10642             var r = this.getFxRestore();
10643             var w = this.getWidth(),
10644                 h = this.getHeight();
10645
10646             var st = this.dom.style;
10647
10648             var after = function(){
10649                 if(o.useDisplay){
10650                     el.setDisplayed(false);
10651                 }else{
10652                     el.hide();
10653                 }
10654
10655                 el.clearOpacity();
10656                 el.setPositioning(r.pos);
10657                 st.width = r.width;
10658                 st.height = r.height;
10659
10660                 el.afterFx(o);
10661             };
10662
10663             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10664             switch(anchor.toLowerCase()){
10665                 case "t":
10666                     pt.by = [0, -h];
10667                 break;
10668                 case "l":
10669                     pt.by = [-w, 0];
10670                 break;
10671                 case "r":
10672                     pt.by = [w, 0];
10673                 break;
10674                 case "b":
10675                     pt.by = [0, h];
10676                 break;
10677                 case "tl":
10678                     pt.by = [-w, -h];
10679                 break;
10680                 case "bl":
10681                     pt.by = [-w, h];
10682                 break;
10683                 case "br":
10684                     pt.by = [w, h];
10685                 break;
10686                 case "tr":
10687                     pt.by = [w, -h];
10688                 break;
10689             }
10690
10691             arguments.callee.anim = this.fxanim(a,
10692                 o,
10693                 'motion',
10694                 .5,
10695                 "easeOut", after);
10696         });
10697         return this;
10698     },
10699
10700         /**
10701          * Ensures that all effects queued after syncFx is called on the element are
10702          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10703          * @return {Roo.Element} The Element
10704          */
10705     syncFx : function(){
10706         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10707             block : false,
10708             concurrent : true,
10709             stopFx : false
10710         });
10711         return this;
10712     },
10713
10714         /**
10715          * Ensures that all effects queued after sequenceFx is called on the element are
10716          * run in sequence.  This is the opposite of {@link #syncFx}.
10717          * @return {Roo.Element} The Element
10718          */
10719     sequenceFx : function(){
10720         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10721             block : false,
10722             concurrent : false,
10723             stopFx : false
10724         });
10725         return this;
10726     },
10727
10728         /* @private */
10729     nextFx : function(){
10730         var ef = this.fxQueue[0];
10731         if(ef){
10732             ef.call(this);
10733         }
10734     },
10735
10736         /**
10737          * Returns true if the element has any effects actively running or queued, else returns false.
10738          * @return {Boolean} True if element has active effects, else false
10739          */
10740     hasActiveFx : function(){
10741         return this.fxQueue && this.fxQueue[0];
10742     },
10743
10744         /**
10745          * Stops any running effects and clears the element's internal effects queue if it contains
10746          * any additional effects that haven't started yet.
10747          * @return {Roo.Element} The Element
10748          */
10749     stopFx : function(){
10750         if(this.hasActiveFx()){
10751             var cur = this.fxQueue[0];
10752             if(cur && cur.anim && cur.anim.isAnimated()){
10753                 this.fxQueue = [cur]; // clear out others
10754                 cur.anim.stop(true);
10755             }
10756         }
10757         return this;
10758     },
10759
10760         /* @private */
10761     beforeFx : function(o){
10762         if(this.hasActiveFx() && !o.concurrent){
10763            if(o.stopFx){
10764                this.stopFx();
10765                return true;
10766            }
10767            return false;
10768         }
10769         return true;
10770     },
10771
10772         /**
10773          * Returns true if the element is currently blocking so that no other effect can be queued
10774          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10775          * used to ensure that an effect initiated by a user action runs to completion prior to the
10776          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10777          * @return {Boolean} True if blocking, else false
10778          */
10779     hasFxBlock : function(){
10780         var q = this.fxQueue;
10781         return q && q[0] && q[0].block;
10782     },
10783
10784         /* @private */
10785     queueFx : function(o, fn){
10786         if(!this.fxQueue){
10787             this.fxQueue = [];
10788         }
10789         if(!this.hasFxBlock()){
10790             Roo.applyIf(o, this.fxDefaults);
10791             if(!o.concurrent){
10792                 var run = this.beforeFx(o);
10793                 fn.block = o.block;
10794                 this.fxQueue.push(fn);
10795                 if(run){
10796                     this.nextFx();
10797                 }
10798             }else{
10799                 fn.call(this);
10800             }
10801         }
10802         return this;
10803     },
10804
10805         /* @private */
10806     fxWrap : function(pos, o, vis){
10807         var wrap;
10808         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10809             var wrapXY;
10810             if(o.fixPosition){
10811                 wrapXY = this.getXY();
10812             }
10813             var div = document.createElement("div");
10814             div.style.visibility = vis;
10815             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10816             wrap.setPositioning(pos);
10817             if(wrap.getStyle("position") == "static"){
10818                 wrap.position("relative");
10819             }
10820             this.clearPositioning('auto');
10821             wrap.clip();
10822             wrap.dom.appendChild(this.dom);
10823             if(wrapXY){
10824                 wrap.setXY(wrapXY);
10825             }
10826         }
10827         return wrap;
10828     },
10829
10830         /* @private */
10831     fxUnwrap : function(wrap, pos, o){
10832         this.clearPositioning();
10833         this.setPositioning(pos);
10834         if(!o.wrap){
10835             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10836             wrap.remove();
10837         }
10838     },
10839
10840         /* @private */
10841     getFxRestore : function(){
10842         var st = this.dom.style;
10843         return {pos: this.getPositioning(), width: st.width, height : st.height};
10844     },
10845
10846         /* @private */
10847     afterFx : function(o){
10848         if(o.afterStyle){
10849             this.applyStyles(o.afterStyle);
10850         }
10851         if(o.afterCls){
10852             this.addClass(o.afterCls);
10853         }
10854         if(o.remove === true){
10855             this.remove();
10856         }
10857         Roo.callback(o.callback, o.scope, [this]);
10858         if(!o.concurrent){
10859             this.fxQueue.shift();
10860             this.nextFx();
10861         }
10862     },
10863
10864         /* @private */
10865     getFxEl : function(){ // support for composite element fx
10866         return Roo.get(this.dom);
10867     },
10868
10869         /* @private */
10870     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10871         animType = animType || 'run';
10872         opt = opt || {};
10873         var anim = Roo.lib.Anim[animType](
10874             this.dom, args,
10875             (opt.duration || defaultDur) || .35,
10876             (opt.easing || defaultEase) || 'easeOut',
10877             function(){
10878                 Roo.callback(cb, this);
10879             },
10880             this
10881         );
10882         opt.anim = anim;
10883         return anim;
10884     }
10885 };
10886
10887 // backwords compat
10888 Roo.Fx.resize = Roo.Fx.scale;
10889
10890 //When included, Roo.Fx is automatically applied to Element so that all basic
10891 //effects are available directly via the Element API
10892 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10893  * Based on:
10894  * Ext JS Library 1.1.1
10895  * Copyright(c) 2006-2007, Ext JS, LLC.
10896  *
10897  * Originally Released Under LGPL - original licence link has changed is not relivant.
10898  *
10899  * Fork - LGPL
10900  * <script type="text/javascript">
10901  */
10902
10903
10904 /**
10905  * @class Roo.CompositeElement
10906  * Standard composite class. Creates a Roo.Element for every element in the collection.
10907  * <br><br>
10908  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10909  * actions will be performed on all the elements in this collection.</b>
10910  * <br><br>
10911  * All methods return <i>this</i> and can be chained.
10912  <pre><code>
10913  var els = Roo.select("#some-el div.some-class", true);
10914  // or select directly from an existing element
10915  var el = Roo.get('some-el');
10916  el.select('div.some-class', true);
10917
10918  els.setWidth(100); // all elements become 100 width
10919  els.hide(true); // all elements fade out and hide
10920  // or
10921  els.setWidth(100).hide(true);
10922  </code></pre>
10923  */
10924 Roo.CompositeElement = function(els){
10925     this.elements = [];
10926     this.addElements(els);
10927 };
10928 Roo.CompositeElement.prototype = {
10929     isComposite: true,
10930     addElements : function(els){
10931         if(!els) return this;
10932         if(typeof els == "string"){
10933             els = Roo.Element.selectorFunction(els);
10934         }
10935         var yels = this.elements;
10936         var index = yels.length-1;
10937         for(var i = 0, len = els.length; i < len; i++) {
10938                 yels[++index] = Roo.get(els[i]);
10939         }
10940         return this;
10941     },
10942
10943     /**
10944     * Clears this composite and adds the elements returned by the passed selector.
10945     * @param {String/Array} els A string CSS selector, an array of elements or an element
10946     * @return {CompositeElement} this
10947     */
10948     fill : function(els){
10949         this.elements = [];
10950         this.add(els);
10951         return this;
10952     },
10953
10954     /**
10955     * Filters this composite to only elements that match the passed selector.
10956     * @param {String} selector A string CSS selector
10957     * @return {CompositeElement} this
10958     */
10959     filter : function(selector){
10960         var els = [];
10961         this.each(function(el){
10962             if(el.is(selector)){
10963                 els[els.length] = el.dom;
10964             }
10965         });
10966         this.fill(els);
10967         return this;
10968     },
10969
10970     invoke : function(fn, args){
10971         var els = this.elements;
10972         for(var i = 0, len = els.length; i < len; i++) {
10973                 Roo.Element.prototype[fn].apply(els[i], args);
10974         }
10975         return this;
10976     },
10977     /**
10978     * Adds elements to this composite.
10979     * @param {String/Array} els A string CSS selector, an array of elements or an element
10980     * @return {CompositeElement} this
10981     */
10982     add : function(els){
10983         if(typeof els == "string"){
10984             this.addElements(Roo.Element.selectorFunction(els));
10985         }else if(els.length !== undefined){
10986             this.addElements(els);
10987         }else{
10988             this.addElements([els]);
10989         }
10990         return this;
10991     },
10992     /**
10993     * Calls the passed function passing (el, this, index) for each element in this composite.
10994     * @param {Function} fn The function to call
10995     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10996     * @return {CompositeElement} this
10997     */
10998     each : function(fn, scope){
10999         var els = this.elements;
11000         for(var i = 0, len = els.length; i < len; i++){
11001             if(fn.call(scope || els[i], els[i], this, i) === false) {
11002                 break;
11003             }
11004         }
11005         return this;
11006     },
11007
11008     /**
11009      * Returns the Element object at the specified index
11010      * @param {Number} index
11011      * @return {Roo.Element}
11012      */
11013     item : function(index){
11014         return this.elements[index] || null;
11015     },
11016
11017     /**
11018      * Returns the first Element
11019      * @return {Roo.Element}
11020      */
11021     first : function(){
11022         return this.item(0);
11023     },
11024
11025     /**
11026      * Returns the last Element
11027      * @return {Roo.Element}
11028      */
11029     last : function(){
11030         return this.item(this.elements.length-1);
11031     },
11032
11033     /**
11034      * Returns the number of elements in this composite
11035      * @return Number
11036      */
11037     getCount : function(){
11038         return this.elements.length;
11039     },
11040
11041     /**
11042      * Returns true if this composite contains the passed element
11043      * @return Boolean
11044      */
11045     contains : function(el){
11046         return this.indexOf(el) !== -1;
11047     },
11048
11049     /**
11050      * Returns true if this composite contains the passed element
11051      * @return Boolean
11052      */
11053     indexOf : function(el){
11054         return this.elements.indexOf(Roo.get(el));
11055     },
11056
11057
11058     /**
11059     * Removes the specified element(s).
11060     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11061     * or an array of any of those.
11062     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11063     * @return {CompositeElement} this
11064     */
11065     removeElement : function(el, removeDom){
11066         if(el instanceof Array){
11067             for(var i = 0, len = el.length; i < len; i++){
11068                 this.removeElement(el[i]);
11069             }
11070             return this;
11071         }
11072         var index = typeof el == 'number' ? el : this.indexOf(el);
11073         if(index !== -1){
11074             if(removeDom){
11075                 var d = this.elements[index];
11076                 if(d.dom){
11077                     d.remove();
11078                 }else{
11079                     d.parentNode.removeChild(d);
11080                 }
11081             }
11082             this.elements.splice(index, 1);
11083         }
11084         return this;
11085     },
11086
11087     /**
11088     * Replaces the specified element with the passed element.
11089     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11090     * to replace.
11091     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11092     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11093     * @return {CompositeElement} this
11094     */
11095     replaceElement : function(el, replacement, domReplace){
11096         var index = typeof el == 'number' ? el : this.indexOf(el);
11097         if(index !== -1){
11098             if(domReplace){
11099                 this.elements[index].replaceWith(replacement);
11100             }else{
11101                 this.elements.splice(index, 1, Roo.get(replacement))
11102             }
11103         }
11104         return this;
11105     },
11106
11107     /**
11108      * Removes all elements.
11109      */
11110     clear : function(){
11111         this.elements = [];
11112     }
11113 };
11114 (function(){
11115     Roo.CompositeElement.createCall = function(proto, fnName){
11116         if(!proto[fnName]){
11117             proto[fnName] = function(){
11118                 return this.invoke(fnName, arguments);
11119             };
11120         }
11121     };
11122     for(var fnName in Roo.Element.prototype){
11123         if(typeof Roo.Element.prototype[fnName] == "function"){
11124             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11125         }
11126     };
11127 })();
11128 /*
11129  * Based on:
11130  * Ext JS Library 1.1.1
11131  * Copyright(c) 2006-2007, Ext JS, LLC.
11132  *
11133  * Originally Released Under LGPL - original licence link has changed is not relivant.
11134  *
11135  * Fork - LGPL
11136  * <script type="text/javascript">
11137  */
11138
11139 /**
11140  * @class Roo.CompositeElementLite
11141  * @extends Roo.CompositeElement
11142  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11143  <pre><code>
11144  var els = Roo.select("#some-el div.some-class");
11145  // or select directly from an existing element
11146  var el = Roo.get('some-el');
11147  el.select('div.some-class');
11148
11149  els.setWidth(100); // all elements become 100 width
11150  els.hide(true); // all elements fade out and hide
11151  // or
11152  els.setWidth(100).hide(true);
11153  </code></pre><br><br>
11154  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11155  * actions will be performed on all the elements in this collection.</b>
11156  */
11157 Roo.CompositeElementLite = function(els){
11158     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11159     this.el = new Roo.Element.Flyweight();
11160 };
11161 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11162     addElements : function(els){
11163         if(els){
11164             if(els instanceof Array){
11165                 this.elements = this.elements.concat(els);
11166             }else{
11167                 var yels = this.elements;
11168                 var index = yels.length-1;
11169                 for(var i = 0, len = els.length; i < len; i++) {
11170                     yels[++index] = els[i];
11171                 }
11172             }
11173         }
11174         return this;
11175     },
11176     invoke : function(fn, args){
11177         var els = this.elements;
11178         var el = this.el;
11179         for(var i = 0, len = els.length; i < len; i++) {
11180             el.dom = els[i];
11181                 Roo.Element.prototype[fn].apply(el, args);
11182         }
11183         return this;
11184     },
11185     /**
11186      * Returns a flyweight Element of the dom element object at the specified index
11187      * @param {Number} index
11188      * @return {Roo.Element}
11189      */
11190     item : function(index){
11191         if(!this.elements[index]){
11192             return null;
11193         }
11194         this.el.dom = this.elements[index];
11195         return this.el;
11196     },
11197
11198     // fixes scope with flyweight
11199     addListener : function(eventName, handler, scope, opt){
11200         var els = this.elements;
11201         for(var i = 0, len = els.length; i < len; i++) {
11202             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11203         }
11204         return this;
11205     },
11206
11207     /**
11208     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11209     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11210     * a reference to the dom node, use el.dom.</b>
11211     * @param {Function} fn The function to call
11212     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11213     * @return {CompositeElement} this
11214     */
11215     each : function(fn, scope){
11216         var els = this.elements;
11217         var el = this.el;
11218         for(var i = 0, len = els.length; i < len; i++){
11219             el.dom = els[i];
11220                 if(fn.call(scope || el, el, this, i) === false){
11221                 break;
11222             }
11223         }
11224         return this;
11225     },
11226
11227     indexOf : function(el){
11228         return this.elements.indexOf(Roo.getDom(el));
11229     },
11230
11231     replaceElement : function(el, replacement, domReplace){
11232         var index = typeof el == 'number' ? el : this.indexOf(el);
11233         if(index !== -1){
11234             replacement = Roo.getDom(replacement);
11235             if(domReplace){
11236                 var d = this.elements[index];
11237                 d.parentNode.insertBefore(replacement, d);
11238                 d.parentNode.removeChild(d);
11239             }
11240             this.elements.splice(index, 1, replacement);
11241         }
11242         return this;
11243     }
11244 });
11245 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11246
11247 /*
11248  * Based on:
11249  * Ext JS Library 1.1.1
11250  * Copyright(c) 2006-2007, Ext JS, LLC.
11251  *
11252  * Originally Released Under LGPL - original licence link has changed is not relivant.
11253  *
11254  * Fork - LGPL
11255  * <script type="text/javascript">
11256  */
11257
11258  
11259
11260 /**
11261  * @class Roo.data.Connection
11262  * @extends Roo.util.Observable
11263  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11264  * either to a configured URL, or to a URL specified at request time.<br><br>
11265  * <p>
11266  * Requests made by this class are asynchronous, and will return immediately. No data from
11267  * the server will be available to the statement immediately following the {@link #request} call.
11268  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11269  * <p>
11270  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11271  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11272  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11273  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11274  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11275  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11276  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11277  * standard DOM methods.
11278  * @constructor
11279  * @param {Object} config a configuration object.
11280  */
11281 Roo.data.Connection = function(config){
11282     Roo.apply(this, config);
11283     this.addEvents({
11284         /**
11285          * @event beforerequest
11286          * Fires before a network request is made to retrieve a data object.
11287          * @param {Connection} conn This Connection object.
11288          * @param {Object} options The options config object passed to the {@link #request} method.
11289          */
11290         "beforerequest" : true,
11291         /**
11292          * @event requestcomplete
11293          * Fires if the request was successfully completed.
11294          * @param {Connection} conn This Connection object.
11295          * @param {Object} response The XHR object containing the response data.
11296          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11297          * @param {Object} options The options config object passed to the {@link #request} method.
11298          */
11299         "requestcomplete" : true,
11300         /**
11301          * @event requestexception
11302          * Fires if an error HTTP status was returned from the server.
11303          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11304          * @param {Connection} conn This Connection object.
11305          * @param {Object} response The XHR object containing the response data.
11306          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11307          * @param {Object} options The options config object passed to the {@link #request} method.
11308          */
11309         "requestexception" : true
11310     });
11311     Roo.data.Connection.superclass.constructor.call(this);
11312 };
11313
11314 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11315     /**
11316      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11317      */
11318     /**
11319      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11320      * extra parameters to each request made by this object. (defaults to undefined)
11321      */
11322     /**
11323      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11324      *  to each request made by this object. (defaults to undefined)
11325      */
11326     /**
11327      * @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)
11328      */
11329     /**
11330      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11331      */
11332     timeout : 30000,
11333     /**
11334      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11335      * @type Boolean
11336      */
11337     autoAbort:false,
11338
11339     /**
11340      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11341      * @type Boolean
11342      */
11343     disableCaching: true,
11344
11345     /**
11346      * Sends an HTTP request to a remote server.
11347      * @param {Object} options An object which may contain the following properties:<ul>
11348      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11349      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11350      * request, a url encoded string or a function to call to get either.</li>
11351      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11352      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11353      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11354      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11355      * <li>options {Object} The parameter to the request call.</li>
11356      * <li>success {Boolean} True if the request succeeded.</li>
11357      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11358      * </ul></li>
11359      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11360      * The callback is passed the following parameters:<ul>
11361      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11362      * <li>options {Object} The parameter to the request call.</li>
11363      * </ul></li>
11364      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11365      * The callback is passed the following parameters:<ul>
11366      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11367      * <li>options {Object} The parameter to the request call.</li>
11368      * </ul></li>
11369      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11370      * for the callback function. Defaults to the browser window.</li>
11371      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11372      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11373      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11374      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11375      * params for the post data. Any params will be appended to the URL.</li>
11376      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11377      * </ul>
11378      * @return {Number} transactionId
11379      */
11380     request : function(o){
11381         if(this.fireEvent("beforerequest", this, o) !== false){
11382             var p = o.params;
11383
11384             if(typeof p == "function"){
11385                 p = p.call(o.scope||window, o);
11386             }
11387             if(typeof p == "object"){
11388                 p = Roo.urlEncode(o.params);
11389             }
11390             if(this.extraParams){
11391                 var extras = Roo.urlEncode(this.extraParams);
11392                 p = p ? (p + '&' + extras) : extras;
11393             }
11394
11395             var url = o.url || this.url;
11396             if(typeof url == 'function'){
11397                 url = url.call(o.scope||window, o);
11398             }
11399
11400             if(o.form){
11401                 var form = Roo.getDom(o.form);
11402                 url = url || form.action;
11403
11404                 var enctype = form.getAttribute("enctype");
11405                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11406                     return this.doFormUpload(o, p, url);
11407                 }
11408                 var f = Roo.lib.Ajax.serializeForm(form);
11409                 p = p ? (p + '&' + f) : f;
11410             }
11411
11412             var hs = o.headers;
11413             if(this.defaultHeaders){
11414                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11415                 if(!o.headers){
11416                     o.headers = hs;
11417                 }
11418             }
11419
11420             var cb = {
11421                 success: this.handleResponse,
11422                 failure: this.handleFailure,
11423                 scope: this,
11424                 argument: {options: o},
11425                 timeout : o.timeout || this.timeout
11426             };
11427
11428             var method = o.method||this.method||(p ? "POST" : "GET");
11429
11430             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11431                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11432             }
11433
11434             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11435                 if(o.autoAbort){
11436                     this.abort();
11437                 }
11438             }else if(this.autoAbort !== false){
11439                 this.abort();
11440             }
11441
11442             if((method == 'GET' && p) || o.xmlData){
11443                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11444                 p = '';
11445             }
11446             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11447             return this.transId;
11448         }else{
11449             Roo.callback(o.callback, o.scope, [o, null, null]);
11450             return null;
11451         }
11452     },
11453
11454     /**
11455      * Determine whether this object has a request outstanding.
11456      * @param {Number} transactionId (Optional) defaults to the last transaction
11457      * @return {Boolean} True if there is an outstanding request.
11458      */
11459     isLoading : function(transId){
11460         if(transId){
11461             return Roo.lib.Ajax.isCallInProgress(transId);
11462         }else{
11463             return this.transId ? true : false;
11464         }
11465     },
11466
11467     /**
11468      * Aborts any outstanding request.
11469      * @param {Number} transactionId (Optional) defaults to the last transaction
11470      */
11471     abort : function(transId){
11472         if(transId || this.isLoading()){
11473             Roo.lib.Ajax.abort(transId || this.transId);
11474         }
11475     },
11476
11477     // private
11478     handleResponse : function(response){
11479         this.transId = false;
11480         var options = response.argument.options;
11481         response.argument = options ? options.argument : null;
11482         this.fireEvent("requestcomplete", this, response, options);
11483         Roo.callback(options.success, options.scope, [response, options]);
11484         Roo.callback(options.callback, options.scope, [options, true, response]);
11485     },
11486
11487     // private
11488     handleFailure : function(response, e){
11489         this.transId = false;
11490         var options = response.argument.options;
11491         response.argument = options ? options.argument : null;
11492         this.fireEvent("requestexception", this, response, options, e);
11493         Roo.callback(options.failure, options.scope, [response, options]);
11494         Roo.callback(options.callback, options.scope, [options, false, response]);
11495     },
11496
11497     // private
11498     doFormUpload : function(o, ps, url){
11499         var id = Roo.id();
11500         var frame = document.createElement('iframe');
11501         frame.id = id;
11502         frame.name = id;
11503         frame.className = 'x-hidden';
11504         if(Roo.isIE){
11505             frame.src = Roo.SSL_SECURE_URL;
11506         }
11507         document.body.appendChild(frame);
11508
11509         if(Roo.isIE){
11510            document.frames[id].name = id;
11511         }
11512
11513         var form = Roo.getDom(o.form);
11514         form.target = id;
11515         form.method = 'POST';
11516         form.enctype = form.encoding = 'multipart/form-data';
11517         if(url){
11518             form.action = url;
11519         }
11520
11521         var hiddens, hd;
11522         if(ps){ // add dynamic params
11523             hiddens = [];
11524             ps = Roo.urlDecode(ps, false);
11525             for(var k in ps){
11526                 if(ps.hasOwnProperty(k)){
11527                     hd = document.createElement('input');
11528                     hd.type = 'hidden';
11529                     hd.name = k;
11530                     hd.value = ps[k];
11531                     form.appendChild(hd);
11532                     hiddens.push(hd);
11533                 }
11534             }
11535         }
11536
11537         function cb(){
11538             var r = {  // bogus response object
11539                 responseText : '',
11540                 responseXML : null
11541             };
11542
11543             r.argument = o ? o.argument : null;
11544
11545             try { //
11546                 var doc;
11547                 if(Roo.isIE){
11548                     doc = frame.contentWindow.document;
11549                 }else {
11550                     doc = (frame.contentDocument || window.frames[id].document);
11551                 }
11552                 if(doc && doc.body){
11553                     r.responseText = doc.body.innerHTML;
11554                 }
11555                 if(doc && doc.XMLDocument){
11556                     r.responseXML = doc.XMLDocument;
11557                 }else {
11558                     r.responseXML = doc;
11559                 }
11560             }
11561             catch(e) {
11562                 // ignore
11563             }
11564
11565             Roo.EventManager.removeListener(frame, 'load', cb, this);
11566
11567             this.fireEvent("requestcomplete", this, r, o);
11568             Roo.callback(o.success, o.scope, [r, o]);
11569             Roo.callback(o.callback, o.scope, [o, true, r]);
11570
11571             setTimeout(function(){document.body.removeChild(frame);}, 100);
11572         }
11573
11574         Roo.EventManager.on(frame, 'load', cb, this);
11575         form.submit();
11576
11577         if(hiddens){ // remove dynamic params
11578             for(var i = 0, len = hiddens.length; i < len; i++){
11579                 form.removeChild(hiddens[i]);
11580             }
11581         }
11582     }
11583 });
11584 /*
11585  * Based on:
11586  * Ext JS Library 1.1.1
11587  * Copyright(c) 2006-2007, Ext JS, LLC.
11588  *
11589  * Originally Released Under LGPL - original licence link has changed is not relivant.
11590  *
11591  * Fork - LGPL
11592  * <script type="text/javascript">
11593  */
11594  
11595 /**
11596  * Global Ajax request class.
11597  * 
11598  * @class Roo.Ajax
11599  * @extends Roo.data.Connection
11600  * @static
11601  * 
11602  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11603  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11604  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11605  * @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)
11606  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11607  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11608  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11609  */
11610 Roo.Ajax = new Roo.data.Connection({
11611     // fix up the docs
11612     /**
11613      * @scope Roo.Ajax
11614      * @type {Boolear} 
11615      */
11616     autoAbort : false,
11617
11618     /**
11619      * Serialize the passed form into a url encoded string
11620      * @scope Roo.Ajax
11621      * @param {String/HTMLElement} form
11622      * @return {String}
11623      */
11624     serializeForm : function(form){
11625         return Roo.lib.Ajax.serializeForm(form);
11626     }
11627 });/*
11628  * Based on:
11629  * Ext JS Library 1.1.1
11630  * Copyright(c) 2006-2007, Ext JS, LLC.
11631  *
11632  * Originally Released Under LGPL - original licence link has changed is not relivant.
11633  *
11634  * Fork - LGPL
11635  * <script type="text/javascript">
11636  */
11637
11638  
11639 /**
11640  * @class Roo.UpdateManager
11641  * @extends Roo.util.Observable
11642  * Provides AJAX-style update for Element object.<br><br>
11643  * Usage:<br>
11644  * <pre><code>
11645  * // Get it from a Roo.Element object
11646  * var el = Roo.get("foo");
11647  * var mgr = el.getUpdateManager();
11648  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11649  * ...
11650  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11651  * <br>
11652  * // or directly (returns the same UpdateManager instance)
11653  * var mgr = new Roo.UpdateManager("myElementId");
11654  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11655  * mgr.on("update", myFcnNeedsToKnow);
11656  * <br>
11657    // short handed call directly from the element object
11658    Roo.get("foo").load({
11659         url: "bar.php",
11660         scripts:true,
11661         params: "for=bar",
11662         text: "Loading Foo..."
11663    });
11664  * </code></pre>
11665  * @constructor
11666  * Create new UpdateManager directly.
11667  * @param {String/HTMLElement/Roo.Element} el The element to update
11668  * @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).
11669  */
11670 Roo.UpdateManager = function(el, forceNew){
11671     el = Roo.get(el);
11672     if(!forceNew && el.updateManager){
11673         return el.updateManager;
11674     }
11675     /**
11676      * The Element object
11677      * @type Roo.Element
11678      */
11679     this.el = el;
11680     /**
11681      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11682      * @type String
11683      */
11684     this.defaultUrl = null;
11685
11686     this.addEvents({
11687         /**
11688          * @event beforeupdate
11689          * Fired before an update is made, return false from your handler and the update is cancelled.
11690          * @param {Roo.Element} el
11691          * @param {String/Object/Function} url
11692          * @param {String/Object} params
11693          */
11694         "beforeupdate": true,
11695         /**
11696          * @event update
11697          * Fired after successful update is made.
11698          * @param {Roo.Element} el
11699          * @param {Object} oResponseObject The response Object
11700          */
11701         "update": true,
11702         /**
11703          * @event failure
11704          * Fired on update failure.
11705          * @param {Roo.Element} el
11706          * @param {Object} oResponseObject The response Object
11707          */
11708         "failure": true
11709     });
11710     var d = Roo.UpdateManager.defaults;
11711     /**
11712      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11713      * @type String
11714      */
11715     this.sslBlankUrl = d.sslBlankUrl;
11716     /**
11717      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11718      * @type Boolean
11719      */
11720     this.disableCaching = d.disableCaching;
11721     /**
11722      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11723      * @type String
11724      */
11725     this.indicatorText = d.indicatorText;
11726     /**
11727      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11728      * @type String
11729      */
11730     this.showLoadIndicator = d.showLoadIndicator;
11731     /**
11732      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11733      * @type Number
11734      */
11735     this.timeout = d.timeout;
11736
11737     /**
11738      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11739      * @type Boolean
11740      */
11741     this.loadScripts = d.loadScripts;
11742
11743     /**
11744      * Transaction object of current executing transaction
11745      */
11746     this.transaction = null;
11747
11748     /**
11749      * @private
11750      */
11751     this.autoRefreshProcId = null;
11752     /**
11753      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11754      * @type Function
11755      */
11756     this.refreshDelegate = this.refresh.createDelegate(this);
11757     /**
11758      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11759      * @type Function
11760      */
11761     this.updateDelegate = this.update.createDelegate(this);
11762     /**
11763      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11764      * @type Function
11765      */
11766     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11767     /**
11768      * @private
11769      */
11770     this.successDelegate = this.processSuccess.createDelegate(this);
11771     /**
11772      * @private
11773      */
11774     this.failureDelegate = this.processFailure.createDelegate(this);
11775
11776     if(!this.renderer){
11777      /**
11778       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11779       */
11780     this.renderer = new Roo.UpdateManager.BasicRenderer();
11781     }
11782     
11783     Roo.UpdateManager.superclass.constructor.call(this);
11784 };
11785
11786 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11787     /**
11788      * Get the Element this UpdateManager is bound to
11789      * @return {Roo.Element} The element
11790      */
11791     getEl : function(){
11792         return this.el;
11793     },
11794     /**
11795      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11796      * @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:
11797 <pre><code>
11798 um.update({<br/>
11799     url: "your-url.php",<br/>
11800     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11801     callback: yourFunction,<br/>
11802     scope: yourObject, //(optional scope)  <br/>
11803     discardUrl: false, <br/>
11804     nocache: false,<br/>
11805     text: "Loading...",<br/>
11806     timeout: 30,<br/>
11807     scripts: false<br/>
11808 });
11809 </code></pre>
11810      * The only required property is url. The optional properties nocache, text and scripts
11811      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11812      * @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}
11813      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11814      * @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.
11815      */
11816     update : function(url, params, callback, discardUrl){
11817         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11818             var method = this.method,
11819                 cfg;
11820             if(typeof url == "object"){ // must be config object
11821                 cfg = url;
11822                 url = cfg.url;
11823                 params = params || cfg.params;
11824                 callback = callback || cfg.callback;
11825                 discardUrl = discardUrl || cfg.discardUrl;
11826                 if(callback && cfg.scope){
11827                     callback = callback.createDelegate(cfg.scope);
11828                 }
11829                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11830                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11831                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11832                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11833                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11834             }
11835             this.showLoading();
11836             if(!discardUrl){
11837                 this.defaultUrl = url;
11838             }
11839             if(typeof url == "function"){
11840                 url = url.call(this);
11841             }
11842
11843             method = method || (params ? "POST" : "GET");
11844             if(method == "GET"){
11845                 url = this.prepareUrl(url);
11846             }
11847
11848             var o = Roo.apply(cfg ||{}, {
11849                 url : url,
11850                 params: params,
11851                 success: this.successDelegate,
11852                 failure: this.failureDelegate,
11853                 callback: undefined,
11854                 timeout: (this.timeout*1000),
11855                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11856             });
11857             Roo.log("updated manager called with timeout of " + o.timeout);
11858             this.transaction = Roo.Ajax.request(o);
11859         }
11860     },
11861
11862     /**
11863      * 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.
11864      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11865      * @param {String/HTMLElement} form The form Id or form element
11866      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11867      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11868      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11869      */
11870     formUpdate : function(form, url, reset, callback){
11871         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11872             if(typeof url == "function"){
11873                 url = url.call(this);
11874             }
11875             form = Roo.getDom(form);
11876             this.transaction = Roo.Ajax.request({
11877                 form: form,
11878                 url:url,
11879                 success: this.successDelegate,
11880                 failure: this.failureDelegate,
11881                 timeout: (this.timeout*1000),
11882                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11883             });
11884             this.showLoading.defer(1, this);
11885         }
11886     },
11887
11888     /**
11889      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11890      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11891      */
11892     refresh : function(callback){
11893         if(this.defaultUrl == null){
11894             return;
11895         }
11896         this.update(this.defaultUrl, null, callback, true);
11897     },
11898
11899     /**
11900      * Set this element to auto refresh.
11901      * @param {Number} interval How often to update (in seconds).
11902      * @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)
11903      * @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}
11904      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11905      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11906      */
11907     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11908         if(refreshNow){
11909             this.update(url || this.defaultUrl, params, callback, true);
11910         }
11911         if(this.autoRefreshProcId){
11912             clearInterval(this.autoRefreshProcId);
11913         }
11914         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11915     },
11916
11917     /**
11918      * Stop auto refresh on this element.
11919      */
11920      stopAutoRefresh : function(){
11921         if(this.autoRefreshProcId){
11922             clearInterval(this.autoRefreshProcId);
11923             delete this.autoRefreshProcId;
11924         }
11925     },
11926
11927     isAutoRefreshing : function(){
11928        return this.autoRefreshProcId ? true : false;
11929     },
11930     /**
11931      * Called to update the element to "Loading" state. Override to perform custom action.
11932      */
11933     showLoading : function(){
11934         if(this.showLoadIndicator){
11935             this.el.update(this.indicatorText);
11936         }
11937     },
11938
11939     /**
11940      * Adds unique parameter to query string if disableCaching = true
11941      * @private
11942      */
11943     prepareUrl : function(url){
11944         if(this.disableCaching){
11945             var append = "_dc=" + (new Date().getTime());
11946             if(url.indexOf("?") !== -1){
11947                 url += "&" + append;
11948             }else{
11949                 url += "?" + append;
11950             }
11951         }
11952         return url;
11953     },
11954
11955     /**
11956      * @private
11957      */
11958     processSuccess : function(response){
11959         this.transaction = null;
11960         if(response.argument.form && response.argument.reset){
11961             try{ // put in try/catch since some older FF releases had problems with this
11962                 response.argument.form.reset();
11963             }catch(e){}
11964         }
11965         if(this.loadScripts){
11966             this.renderer.render(this.el, response, this,
11967                 this.updateComplete.createDelegate(this, [response]));
11968         }else{
11969             this.renderer.render(this.el, response, this);
11970             this.updateComplete(response);
11971         }
11972     },
11973
11974     updateComplete : function(response){
11975         this.fireEvent("update", this.el, response);
11976         if(typeof response.argument.callback == "function"){
11977             response.argument.callback(this.el, true, response);
11978         }
11979     },
11980
11981     /**
11982      * @private
11983      */
11984     processFailure : function(response){
11985         this.transaction = null;
11986         this.fireEvent("failure", this.el, response);
11987         if(typeof response.argument.callback == "function"){
11988             response.argument.callback(this.el, false, response);
11989         }
11990     },
11991
11992     /**
11993      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11994      * @param {Object} renderer The object implementing the render() method
11995      */
11996     setRenderer : function(renderer){
11997         this.renderer = renderer;
11998     },
11999
12000     getRenderer : function(){
12001        return this.renderer;
12002     },
12003
12004     /**
12005      * Set the defaultUrl used for updates
12006      * @param {String/Function} defaultUrl The url or a function to call to get the url
12007      */
12008     setDefaultUrl : function(defaultUrl){
12009         this.defaultUrl = defaultUrl;
12010     },
12011
12012     /**
12013      * Aborts the executing transaction
12014      */
12015     abort : function(){
12016         if(this.transaction){
12017             Roo.Ajax.abort(this.transaction);
12018         }
12019     },
12020
12021     /**
12022      * Returns true if an update is in progress
12023      * @return {Boolean}
12024      */
12025     isUpdating : function(){
12026         if(this.transaction){
12027             return Roo.Ajax.isLoading(this.transaction);
12028         }
12029         return false;
12030     }
12031 });
12032
12033 /**
12034  * @class Roo.UpdateManager.defaults
12035  * @static (not really - but it helps the doc tool)
12036  * The defaults collection enables customizing the default properties of UpdateManager
12037  */
12038    Roo.UpdateManager.defaults = {
12039        /**
12040          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12041          * @type Number
12042          */
12043          timeout : 30,
12044
12045          /**
12046          * True to process scripts by default (Defaults to false).
12047          * @type Boolean
12048          */
12049         loadScripts : false,
12050
12051         /**
12052         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12053         * @type String
12054         */
12055         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12056         /**
12057          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12058          * @type Boolean
12059          */
12060         disableCaching : false,
12061         /**
12062          * Whether to show indicatorText when loading (Defaults to true).
12063          * @type Boolean
12064          */
12065         showLoadIndicator : true,
12066         /**
12067          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12068          * @type String
12069          */
12070         indicatorText : '<div class="loading-indicator">Loading...</div>'
12071    };
12072
12073 /**
12074  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12075  *Usage:
12076  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12077  * @param {String/HTMLElement/Roo.Element} el The element to update
12078  * @param {String} url The url
12079  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12080  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12081  * @static
12082  * @deprecated
12083  * @member Roo.UpdateManager
12084  */
12085 Roo.UpdateManager.updateElement = function(el, url, params, options){
12086     var um = Roo.get(el, true).getUpdateManager();
12087     Roo.apply(um, options);
12088     um.update(url, params, options ? options.callback : null);
12089 };
12090 // alias for backwards compat
12091 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12092 /**
12093  * @class Roo.UpdateManager.BasicRenderer
12094  * Default Content renderer. Updates the elements innerHTML with the responseText.
12095  */
12096 Roo.UpdateManager.BasicRenderer = function(){};
12097
12098 Roo.UpdateManager.BasicRenderer.prototype = {
12099     /**
12100      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12101      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12102      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12103      * @param {Roo.Element} el The element being rendered
12104      * @param {Object} response The YUI Connect response object
12105      * @param {UpdateManager} updateManager The calling update manager
12106      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12107      */
12108      render : function(el, response, updateManager, callback){
12109         el.update(response.responseText, updateManager.loadScripts, callback);
12110     }
12111 };
12112 /*
12113  * Based on:
12114  * Roo JS
12115  * (c)) Alan Knowles
12116  * Licence : LGPL
12117  */
12118
12119
12120 /**
12121  * @class Roo.DomTemplate
12122  * @extends Roo.Template
12123  * An effort at a dom based template engine..
12124  *
12125  * Similar to XTemplate, except it uses dom parsing to create the template..
12126  *
12127  * Supported features:
12128  *
12129  *  Tags:
12130
12131 <pre><code>
12132       {a_variable} - output encoded.
12133       {a_variable.format:("Y-m-d")} - call a method on the variable
12134       {a_variable:raw} - unencoded output
12135       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12136       {a_variable:this.method_on_template(...)} - call a method on the template object.
12137  
12138 </code></pre>
12139  *  The tpl tag:
12140 <pre><code>
12141         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12142         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12143         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12144         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12145   
12146 </code></pre>
12147  *      
12148  */
12149 Roo.DomTemplate = function()
12150 {
12151      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12152      if (this.html) {
12153         this.compile();
12154      }
12155 };
12156
12157
12158 Roo.extend(Roo.DomTemplate, Roo.Template, {
12159     /**
12160      * id counter for sub templates.
12161      */
12162     id : 0,
12163     /**
12164      * flag to indicate if dom parser is inside a pre,
12165      * it will strip whitespace if not.
12166      */
12167     inPre : false,
12168     
12169     /**
12170      * The various sub templates
12171      */
12172     tpls : false,
12173     
12174     
12175     
12176     /**
12177      *
12178      * basic tag replacing syntax
12179      * WORD:WORD()
12180      *
12181      * // you can fake an object call by doing this
12182      *  x.t:(test,tesT) 
12183      * 
12184      */
12185     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12186     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12187     
12188     iterChild : function (node, method) {
12189         
12190         var oldPre = this.inPre;
12191         if (node.tagName == 'PRE') {
12192             this.inPre = true;
12193         }
12194         for( var i = 0; i < node.childNodes.length; i++) {
12195             method.call(this, node.childNodes[i]);
12196         }
12197         this.inPre = oldPre;
12198     },
12199     
12200     
12201     
12202     /**
12203      * compile the template
12204      *
12205      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12206      *
12207      */
12208     compile: function()
12209     {
12210         var s = this.html;
12211         
12212         // covert the html into DOM...
12213         var doc = false;
12214         var div =false;
12215         try {
12216             doc = document.implementation.createHTMLDocument("");
12217             doc.documentElement.innerHTML =   this.html  ;
12218             div = doc.documentElement;
12219         } catch (e) {
12220             // old IE... - nasty -- it causes all sorts of issues.. with
12221             // images getting pulled from server..
12222             div = document.createElement('div');
12223             div.innerHTML = this.html;
12224         }
12225         //doc.documentElement.innerHTML = htmlBody
12226          
12227         
12228         
12229         this.tpls = [];
12230         var _t = this;
12231         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12232         
12233         var tpls = this.tpls;
12234         
12235         // create a top level template from the snippet..
12236         
12237         //Roo.log(div.innerHTML);
12238         
12239         var tpl = {
12240             uid : 'master',
12241             id : this.id++,
12242             attr : false,
12243             value : false,
12244             body : div.innerHTML,
12245             
12246             forCall : false,
12247             execCall : false,
12248             dom : div,
12249             isTop : true
12250             
12251         };
12252         tpls.unshift(tpl);
12253         
12254         
12255         // compile them...
12256         this.tpls = [];
12257         Roo.each(tpls, function(tp){
12258             this.compileTpl(tp);
12259             this.tpls[tp.id] = tp;
12260         }, this);
12261         
12262         this.master = tpls[0];
12263         return this;
12264         
12265         
12266     },
12267     
12268     compileNode : function(node, istop) {
12269         // test for
12270         //Roo.log(node);
12271         
12272         
12273         // skip anything not a tag..
12274         if (node.nodeType != 1) {
12275             if (node.nodeType == 3 && !this.inPre) {
12276                 // reduce white space..
12277                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12278                 
12279             }
12280             return;
12281         }
12282         
12283         var tpl = {
12284             uid : false,
12285             id : false,
12286             attr : false,
12287             value : false,
12288             body : '',
12289             
12290             forCall : false,
12291             execCall : false,
12292             dom : false,
12293             isTop : istop
12294             
12295             
12296         };
12297         
12298         
12299         switch(true) {
12300             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12301             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12302             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12303             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12304             // no default..
12305         }
12306         
12307         
12308         if (!tpl.attr) {
12309             // just itterate children..
12310             this.iterChild(node,this.compileNode);
12311             return;
12312         }
12313         tpl.uid = this.id++;
12314         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12315         node.removeAttribute('roo-'+ tpl.attr);
12316         if (tpl.attr != 'name') {
12317             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12318             node.parentNode.replaceChild(placeholder,  node);
12319         } else {
12320             
12321             var placeholder =  document.createElement('span');
12322             placeholder.className = 'roo-tpl-' + tpl.value;
12323             node.parentNode.replaceChild(placeholder,  node);
12324         }
12325         
12326         // parent now sees '{domtplXXXX}
12327         this.iterChild(node,this.compileNode);
12328         
12329         // we should now have node body...
12330         var div = document.createElement('div');
12331         div.appendChild(node);
12332         tpl.dom = node;
12333         // this has the unfortunate side effect of converting tagged attributes
12334         // eg. href="{...}" into %7C...%7D
12335         // this has been fixed by searching for those combo's although it's a bit hacky..
12336         
12337         
12338         tpl.body = div.innerHTML;
12339         
12340         
12341          
12342         tpl.id = tpl.uid;
12343         switch(tpl.attr) {
12344             case 'for' :
12345                 switch (tpl.value) {
12346                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12347                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12348                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12349                 }
12350                 break;
12351             
12352             case 'exec':
12353                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12354                 break;
12355             
12356             case 'if':     
12357                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12358                 break;
12359             
12360             case 'name':
12361                 tpl.id  = tpl.value; // replace non characters???
12362                 break;
12363             
12364         }
12365         
12366         
12367         this.tpls.push(tpl);
12368         
12369         
12370         
12371     },
12372     
12373     
12374     
12375     
12376     /**
12377      * Compile a segment of the template into a 'sub-template'
12378      *
12379      * 
12380      * 
12381      *
12382      */
12383     compileTpl : function(tpl)
12384     {
12385         var fm = Roo.util.Format;
12386         var useF = this.disableFormats !== true;
12387         
12388         var sep = Roo.isGecko ? "+\n" : ",\n";
12389         
12390         var undef = function(str) {
12391             Roo.debug && Roo.log("Property not found :"  + str);
12392             return '';
12393         };
12394           
12395         //Roo.log(tpl.body);
12396         
12397         
12398         
12399         var fn = function(m, lbrace, name, format, args)
12400         {
12401             //Roo.log("ARGS");
12402             //Roo.log(arguments);
12403             args = args ? args.replace(/\\'/g,"'") : args;
12404             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12405             if (typeof(format) == 'undefined') {
12406                 format =  'htmlEncode'; 
12407             }
12408             if (format == 'raw' ) {
12409                 format = false;
12410             }
12411             
12412             if(name.substr(0, 6) == 'domtpl'){
12413                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12414             }
12415             
12416             // build an array of options to determine if value is undefined..
12417             
12418             // basically get 'xxxx.yyyy' then do
12419             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12420             //    (function () { Roo.log("Property not found"); return ''; })() :
12421             //    ......
12422             
12423             var udef_ar = [];
12424             var lookfor = '';
12425             Roo.each(name.split('.'), function(st) {
12426                 lookfor += (lookfor.length ? '.': '') + st;
12427                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12428             });
12429             
12430             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12431             
12432             
12433             if(format && useF){
12434                 
12435                 args = args ? ',' + args : "";
12436                  
12437                 if(format.substr(0, 5) != "this."){
12438                     format = "fm." + format + '(';
12439                 }else{
12440                     format = 'this.call("'+ format.substr(5) + '", ';
12441                     args = ", values";
12442                 }
12443                 
12444                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12445             }
12446              
12447             if (args && args.length) {
12448                 // called with xxyx.yuu:(test,test)
12449                 // change to ()
12450                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12451             }
12452             // raw.. - :raw modifier..
12453             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12454             
12455         };
12456         var body;
12457         // branched to use + in gecko and [].join() in others
12458         if(Roo.isGecko){
12459             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12460                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12461                     "';};};";
12462         }else{
12463             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12464             body.push(tpl.body.replace(/(\r\n|\n)/g,
12465                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12466             body.push("'].join('');};};");
12467             body = body.join('');
12468         }
12469         
12470         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12471        
12472         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12473         eval(body);
12474         
12475         return this;
12476     },
12477      
12478     /**
12479      * same as applyTemplate, except it's done to one of the subTemplates
12480      * when using named templates, you can do:
12481      *
12482      * var str = pl.applySubTemplate('your-name', values);
12483      *
12484      * 
12485      * @param {Number} id of the template
12486      * @param {Object} values to apply to template
12487      * @param {Object} parent (normaly the instance of this object)
12488      */
12489     applySubTemplate : function(id, values, parent)
12490     {
12491         
12492         
12493         var t = this.tpls[id];
12494         
12495         
12496         try { 
12497             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12498                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12499                 return '';
12500             }
12501         } catch(e) {
12502             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12503             Roo.log(values);
12504           
12505             return '';
12506         }
12507         try { 
12508             
12509             if(t.execCall && t.execCall.call(this, values, parent)){
12510                 return '';
12511             }
12512         } catch(e) {
12513             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12514             Roo.log(values);
12515             return '';
12516         }
12517         
12518         try {
12519             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12520             parent = t.target ? values : parent;
12521             if(t.forCall && vs instanceof Array){
12522                 var buf = [];
12523                 for(var i = 0, len = vs.length; i < len; i++){
12524                     try {
12525                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12526                     } catch (e) {
12527                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12528                         Roo.log(e.body);
12529                         //Roo.log(t.compiled);
12530                         Roo.log(vs[i]);
12531                     }   
12532                 }
12533                 return buf.join('');
12534             }
12535         } catch (e) {
12536             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12537             Roo.log(values);
12538             return '';
12539         }
12540         try {
12541             return t.compiled.call(this, vs, parent);
12542         } catch (e) {
12543             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12544             Roo.log(e.body);
12545             //Roo.log(t.compiled);
12546             Roo.log(values);
12547             return '';
12548         }
12549     },
12550
12551    
12552
12553     applyTemplate : function(values){
12554         return this.master.compiled.call(this, values, {});
12555         //var s = this.subs;
12556     },
12557
12558     apply : function(){
12559         return this.applyTemplate.apply(this, arguments);
12560     }
12561
12562  });
12563
12564 Roo.DomTemplate.from = function(el){
12565     el = Roo.getDom(el);
12566     return new Roo.Domtemplate(el.value || el.innerHTML);
12567 };/*
12568  * Based on:
12569  * Ext JS Library 1.1.1
12570  * Copyright(c) 2006-2007, Ext JS, LLC.
12571  *
12572  * Originally Released Under LGPL - original licence link has changed is not relivant.
12573  *
12574  * Fork - LGPL
12575  * <script type="text/javascript">
12576  */
12577
12578 /**
12579  * @class Roo.util.DelayedTask
12580  * Provides a convenient method of performing setTimeout where a new
12581  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12582  * You can use this class to buffer
12583  * the keypress events for a certain number of milliseconds, and perform only if they stop
12584  * for that amount of time.
12585  * @constructor The parameters to this constructor serve as defaults and are not required.
12586  * @param {Function} fn (optional) The default function to timeout
12587  * @param {Object} scope (optional) The default scope of that timeout
12588  * @param {Array} args (optional) The default Array of arguments
12589  */
12590 Roo.util.DelayedTask = function(fn, scope, args){
12591     var id = null, d, t;
12592
12593     var call = function(){
12594         var now = new Date().getTime();
12595         if(now - t >= d){
12596             clearInterval(id);
12597             id = null;
12598             fn.apply(scope, args || []);
12599         }
12600     };
12601     /**
12602      * Cancels any pending timeout and queues a new one
12603      * @param {Number} delay The milliseconds to delay
12604      * @param {Function} newFn (optional) Overrides function passed to constructor
12605      * @param {Object} newScope (optional) Overrides scope passed to constructor
12606      * @param {Array} newArgs (optional) Overrides args passed to constructor
12607      */
12608     this.delay = function(delay, newFn, newScope, newArgs){
12609         if(id && delay != d){
12610             this.cancel();
12611         }
12612         d = delay;
12613         t = new Date().getTime();
12614         fn = newFn || fn;
12615         scope = newScope || scope;
12616         args = newArgs || args;
12617         if(!id){
12618             id = setInterval(call, d);
12619         }
12620     };
12621
12622     /**
12623      * Cancel the last queued timeout
12624      */
12625     this.cancel = function(){
12626         if(id){
12627             clearInterval(id);
12628             id = null;
12629         }
12630     };
12631 };/*
12632  * Based on:
12633  * Ext JS Library 1.1.1
12634  * Copyright(c) 2006-2007, Ext JS, LLC.
12635  *
12636  * Originally Released Under LGPL - original licence link has changed is not relivant.
12637  *
12638  * Fork - LGPL
12639  * <script type="text/javascript">
12640  */
12641  
12642  
12643 Roo.util.TaskRunner = function(interval){
12644     interval = interval || 10;
12645     var tasks = [], removeQueue = [];
12646     var id = 0;
12647     var running = false;
12648
12649     var stopThread = function(){
12650         running = false;
12651         clearInterval(id);
12652         id = 0;
12653     };
12654
12655     var startThread = function(){
12656         if(!running){
12657             running = true;
12658             id = setInterval(runTasks, interval);
12659         }
12660     };
12661
12662     var removeTask = function(task){
12663         removeQueue.push(task);
12664         if(task.onStop){
12665             task.onStop();
12666         }
12667     };
12668
12669     var runTasks = function(){
12670         if(removeQueue.length > 0){
12671             for(var i = 0, len = removeQueue.length; i < len; i++){
12672                 tasks.remove(removeQueue[i]);
12673             }
12674             removeQueue = [];
12675             if(tasks.length < 1){
12676                 stopThread();
12677                 return;
12678             }
12679         }
12680         var now = new Date().getTime();
12681         for(var i = 0, len = tasks.length; i < len; ++i){
12682             var t = tasks[i];
12683             var itime = now - t.taskRunTime;
12684             if(t.interval <= itime){
12685                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12686                 t.taskRunTime = now;
12687                 if(rt === false || t.taskRunCount === t.repeat){
12688                     removeTask(t);
12689                     return;
12690                 }
12691             }
12692             if(t.duration && t.duration <= (now - t.taskStartTime)){
12693                 removeTask(t);
12694             }
12695         }
12696     };
12697
12698     /**
12699      * Queues a new task.
12700      * @param {Object} task
12701      */
12702     this.start = function(task){
12703         tasks.push(task);
12704         task.taskStartTime = new Date().getTime();
12705         task.taskRunTime = 0;
12706         task.taskRunCount = 0;
12707         startThread();
12708         return task;
12709     };
12710
12711     this.stop = function(task){
12712         removeTask(task);
12713         return task;
12714     };
12715
12716     this.stopAll = function(){
12717         stopThread();
12718         for(var i = 0, len = tasks.length; i < len; i++){
12719             if(tasks[i].onStop){
12720                 tasks[i].onStop();
12721             }
12722         }
12723         tasks = [];
12724         removeQueue = [];
12725     };
12726 };
12727
12728 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12729  * Based on:
12730  * Ext JS Library 1.1.1
12731  * Copyright(c) 2006-2007, Ext JS, LLC.
12732  *
12733  * Originally Released Under LGPL - original licence link has changed is not relivant.
12734  *
12735  * Fork - LGPL
12736  * <script type="text/javascript">
12737  */
12738
12739  
12740 /**
12741  * @class Roo.util.MixedCollection
12742  * @extends Roo.util.Observable
12743  * A Collection class that maintains both numeric indexes and keys and exposes events.
12744  * @constructor
12745  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12746  * collection (defaults to false)
12747  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12748  * and return the key value for that item.  This is used when available to look up the key on items that
12749  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12750  * equivalent to providing an implementation for the {@link #getKey} method.
12751  */
12752 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12753     this.items = [];
12754     this.map = {};
12755     this.keys = [];
12756     this.length = 0;
12757     this.addEvents({
12758         /**
12759          * @event clear
12760          * Fires when the collection is cleared.
12761          */
12762         "clear" : true,
12763         /**
12764          * @event add
12765          * Fires when an item is added to the collection.
12766          * @param {Number} index The index at which the item was added.
12767          * @param {Object} o The item added.
12768          * @param {String} key The key associated with the added item.
12769          */
12770         "add" : true,
12771         /**
12772          * @event replace
12773          * Fires when an item is replaced in the collection.
12774          * @param {String} key he key associated with the new added.
12775          * @param {Object} old The item being replaced.
12776          * @param {Object} new The new item.
12777          */
12778         "replace" : true,
12779         /**
12780          * @event remove
12781          * Fires when an item is removed from the collection.
12782          * @param {Object} o The item being removed.
12783          * @param {String} key (optional) The key associated with the removed item.
12784          */
12785         "remove" : true,
12786         "sort" : true
12787     });
12788     this.allowFunctions = allowFunctions === true;
12789     if(keyFn){
12790         this.getKey = keyFn;
12791     }
12792     Roo.util.MixedCollection.superclass.constructor.call(this);
12793 };
12794
12795 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12796     allowFunctions : false,
12797     
12798 /**
12799  * Adds an item to the collection.
12800  * @param {String} key The key to associate with the item
12801  * @param {Object} o The item to add.
12802  * @return {Object} The item added.
12803  */
12804     add : function(key, o){
12805         if(arguments.length == 1){
12806             o = arguments[0];
12807             key = this.getKey(o);
12808         }
12809         if(typeof key == "undefined" || key === null){
12810             this.length++;
12811             this.items.push(o);
12812             this.keys.push(null);
12813         }else{
12814             var old = this.map[key];
12815             if(old){
12816                 return this.replace(key, o);
12817             }
12818             this.length++;
12819             this.items.push(o);
12820             this.map[key] = o;
12821             this.keys.push(key);
12822         }
12823         this.fireEvent("add", this.length-1, o, key);
12824         return o;
12825     },
12826        
12827 /**
12828   * MixedCollection has a generic way to fetch keys if you implement getKey.
12829 <pre><code>
12830 // normal way
12831 var mc = new Roo.util.MixedCollection();
12832 mc.add(someEl.dom.id, someEl);
12833 mc.add(otherEl.dom.id, otherEl);
12834 //and so on
12835
12836 // using getKey
12837 var mc = new Roo.util.MixedCollection();
12838 mc.getKey = function(el){
12839    return el.dom.id;
12840 };
12841 mc.add(someEl);
12842 mc.add(otherEl);
12843
12844 // or via the constructor
12845 var mc = new Roo.util.MixedCollection(false, function(el){
12846    return el.dom.id;
12847 });
12848 mc.add(someEl);
12849 mc.add(otherEl);
12850 </code></pre>
12851  * @param o {Object} The item for which to find the key.
12852  * @return {Object} The key for the passed item.
12853  */
12854     getKey : function(o){
12855          return o.id; 
12856     },
12857    
12858 /**
12859  * Replaces an item in the collection.
12860  * @param {String} key The key associated with the item to replace, or the item to replace.
12861  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12862  * @return {Object}  The new item.
12863  */
12864     replace : function(key, o){
12865         if(arguments.length == 1){
12866             o = arguments[0];
12867             key = this.getKey(o);
12868         }
12869         var old = this.item(key);
12870         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12871              return this.add(key, o);
12872         }
12873         var index = this.indexOfKey(key);
12874         this.items[index] = o;
12875         this.map[key] = o;
12876         this.fireEvent("replace", key, old, o);
12877         return o;
12878     },
12879    
12880 /**
12881  * Adds all elements of an Array or an Object to the collection.
12882  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12883  * an Array of values, each of which are added to the collection.
12884  */
12885     addAll : function(objs){
12886         if(arguments.length > 1 || objs instanceof Array){
12887             var args = arguments.length > 1 ? arguments : objs;
12888             for(var i = 0, len = args.length; i < len; i++){
12889                 this.add(args[i]);
12890             }
12891         }else{
12892             for(var key in objs){
12893                 if(this.allowFunctions || typeof objs[key] != "function"){
12894                     this.add(key, objs[key]);
12895                 }
12896             }
12897         }
12898     },
12899    
12900 /**
12901  * Executes the specified function once for every item in the collection, passing each
12902  * item as the first and only parameter. returning false from the function will stop the iteration.
12903  * @param {Function} fn The function to execute for each item.
12904  * @param {Object} scope (optional) The scope in which to execute the function.
12905  */
12906     each : function(fn, scope){
12907         var items = [].concat(this.items); // each safe for removal
12908         for(var i = 0, len = items.length; i < len; i++){
12909             if(fn.call(scope || items[i], items[i], i, len) === false){
12910                 break;
12911             }
12912         }
12913     },
12914    
12915 /**
12916  * Executes the specified function once for every key in the collection, passing each
12917  * key, and its associated item as the first two parameters.
12918  * @param {Function} fn The function to execute for each item.
12919  * @param {Object} scope (optional) The scope in which to execute the function.
12920  */
12921     eachKey : function(fn, scope){
12922         for(var i = 0, len = this.keys.length; i < len; i++){
12923             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12924         }
12925     },
12926    
12927 /**
12928  * Returns the first item in the collection which elicits a true return value from the
12929  * passed selection function.
12930  * @param {Function} fn The selection function to execute for each item.
12931  * @param {Object} scope (optional) The scope in which to execute the function.
12932  * @return {Object} The first item in the collection which returned true from the selection function.
12933  */
12934     find : function(fn, scope){
12935         for(var i = 0, len = this.items.length; i < len; i++){
12936             if(fn.call(scope || window, this.items[i], this.keys[i])){
12937                 return this.items[i];
12938             }
12939         }
12940         return null;
12941     },
12942    
12943 /**
12944  * Inserts an item at the specified index in the collection.
12945  * @param {Number} index The index to insert the item at.
12946  * @param {String} key The key to associate with the new item, or the item itself.
12947  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12948  * @return {Object} The item inserted.
12949  */
12950     insert : function(index, key, o){
12951         if(arguments.length == 2){
12952             o = arguments[1];
12953             key = this.getKey(o);
12954         }
12955         if(index >= this.length){
12956             return this.add(key, o);
12957         }
12958         this.length++;
12959         this.items.splice(index, 0, o);
12960         if(typeof key != "undefined" && key != null){
12961             this.map[key] = o;
12962         }
12963         this.keys.splice(index, 0, key);
12964         this.fireEvent("add", index, o, key);
12965         return o;
12966     },
12967    
12968 /**
12969  * Removed an item from the collection.
12970  * @param {Object} o The item to remove.
12971  * @return {Object} The item removed.
12972  */
12973     remove : function(o){
12974         return this.removeAt(this.indexOf(o));
12975     },
12976    
12977 /**
12978  * Remove an item from a specified index in the collection.
12979  * @param {Number} index The index within the collection of the item to remove.
12980  */
12981     removeAt : function(index){
12982         if(index < this.length && index >= 0){
12983             this.length--;
12984             var o = this.items[index];
12985             this.items.splice(index, 1);
12986             var key = this.keys[index];
12987             if(typeof key != "undefined"){
12988                 delete this.map[key];
12989             }
12990             this.keys.splice(index, 1);
12991             this.fireEvent("remove", o, key);
12992         }
12993     },
12994    
12995 /**
12996  * Removed an item associated with the passed key fom the collection.
12997  * @param {String} key The key of the item to remove.
12998  */
12999     removeKey : function(key){
13000         return this.removeAt(this.indexOfKey(key));
13001     },
13002    
13003 /**
13004  * Returns the number of items in the collection.
13005  * @return {Number} the number of items in the collection.
13006  */
13007     getCount : function(){
13008         return this.length; 
13009     },
13010    
13011 /**
13012  * Returns index within the collection of the passed Object.
13013  * @param {Object} o The item to find the index of.
13014  * @return {Number} index of the item.
13015  */
13016     indexOf : function(o){
13017         if(!this.items.indexOf){
13018             for(var i = 0, len = this.items.length; i < len; i++){
13019                 if(this.items[i] == o) return i;
13020             }
13021             return -1;
13022         }else{
13023             return this.items.indexOf(o);
13024         }
13025     },
13026    
13027 /**
13028  * Returns index within the collection of the passed key.
13029  * @param {String} key The key to find the index of.
13030  * @return {Number} index of the key.
13031  */
13032     indexOfKey : function(key){
13033         if(!this.keys.indexOf){
13034             for(var i = 0, len = this.keys.length; i < len; i++){
13035                 if(this.keys[i] == key) return i;
13036             }
13037             return -1;
13038         }else{
13039             return this.keys.indexOf(key);
13040         }
13041     },
13042    
13043 /**
13044  * Returns the item associated with the passed key OR index. Key has priority over index.
13045  * @param {String/Number} key The key or index of the item.
13046  * @return {Object} The item associated with the passed key.
13047  */
13048     item : function(key){
13049         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13050         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13051     },
13052     
13053 /**
13054  * Returns the item at the specified index.
13055  * @param {Number} index The index of the item.
13056  * @return {Object}
13057  */
13058     itemAt : function(index){
13059         return this.items[index];
13060     },
13061     
13062 /**
13063  * Returns the item associated with the passed key.
13064  * @param {String/Number} key The key of the item.
13065  * @return {Object} The item associated with the passed key.
13066  */
13067     key : function(key){
13068         return this.map[key];
13069     },
13070    
13071 /**
13072  * Returns true if the collection contains the passed Object as an item.
13073  * @param {Object} o  The Object to look for in the collection.
13074  * @return {Boolean} True if the collection contains the Object as an item.
13075  */
13076     contains : function(o){
13077         return this.indexOf(o) != -1;
13078     },
13079    
13080 /**
13081  * Returns true if the collection contains the passed Object as a key.
13082  * @param {String} key The key to look for in the collection.
13083  * @return {Boolean} True if the collection contains the Object as a key.
13084  */
13085     containsKey : function(key){
13086         return typeof this.map[key] != "undefined";
13087     },
13088    
13089 /**
13090  * Removes all items from the collection.
13091  */
13092     clear : function(){
13093         this.length = 0;
13094         this.items = [];
13095         this.keys = [];
13096         this.map = {};
13097         this.fireEvent("clear");
13098     },
13099    
13100 /**
13101  * Returns the first item in the collection.
13102  * @return {Object} the first item in the collection..
13103  */
13104     first : function(){
13105         return this.items[0]; 
13106     },
13107    
13108 /**
13109  * Returns the last item in the collection.
13110  * @return {Object} the last item in the collection..
13111  */
13112     last : function(){
13113         return this.items[this.length-1];   
13114     },
13115     
13116     _sort : function(property, dir, fn){
13117         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13118         fn = fn || function(a, b){
13119             return a-b;
13120         };
13121         var c = [], k = this.keys, items = this.items;
13122         for(var i = 0, len = items.length; i < len; i++){
13123             c[c.length] = {key: k[i], value: items[i], index: i};
13124         }
13125         c.sort(function(a, b){
13126             var v = fn(a[property], b[property]) * dsc;
13127             if(v == 0){
13128                 v = (a.index < b.index ? -1 : 1);
13129             }
13130             return v;
13131         });
13132         for(var i = 0, len = c.length; i < len; i++){
13133             items[i] = c[i].value;
13134             k[i] = c[i].key;
13135         }
13136         this.fireEvent("sort", this);
13137     },
13138     
13139     /**
13140      * Sorts this collection with the passed comparison function
13141      * @param {String} direction (optional) "ASC" or "DESC"
13142      * @param {Function} fn (optional) comparison function
13143      */
13144     sort : function(dir, fn){
13145         this._sort("value", dir, fn);
13146     },
13147     
13148     /**
13149      * Sorts this collection by keys
13150      * @param {String} direction (optional) "ASC" or "DESC"
13151      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13152      */
13153     keySort : function(dir, fn){
13154         this._sort("key", dir, fn || function(a, b){
13155             return String(a).toUpperCase()-String(b).toUpperCase();
13156         });
13157     },
13158     
13159     /**
13160      * Returns a range of items in this collection
13161      * @param {Number} startIndex (optional) defaults to 0
13162      * @param {Number} endIndex (optional) default to the last item
13163      * @return {Array} An array of items
13164      */
13165     getRange : function(start, end){
13166         var items = this.items;
13167         if(items.length < 1){
13168             return [];
13169         }
13170         start = start || 0;
13171         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13172         var r = [];
13173         if(start <= end){
13174             for(var i = start; i <= end; i++) {
13175                     r[r.length] = items[i];
13176             }
13177         }else{
13178             for(var i = start; i >= end; i--) {
13179                     r[r.length] = items[i];
13180             }
13181         }
13182         return r;
13183     },
13184         
13185     /**
13186      * Filter the <i>objects</i> in this collection by a specific property. 
13187      * Returns a new collection that has been filtered.
13188      * @param {String} property A property on your objects
13189      * @param {String/RegExp} value Either string that the property values 
13190      * should start with or a RegExp to test against the property
13191      * @return {MixedCollection} The new filtered collection
13192      */
13193     filter : function(property, value){
13194         if(!value.exec){ // not a regex
13195             value = String(value);
13196             if(value.length == 0){
13197                 return this.clone();
13198             }
13199             value = new RegExp("^" + Roo.escapeRe(value), "i");
13200         }
13201         return this.filterBy(function(o){
13202             return o && value.test(o[property]);
13203         });
13204         },
13205     
13206     /**
13207      * Filter by a function. * Returns a new collection that has been filtered.
13208      * The passed function will be called with each 
13209      * object in the collection. If the function returns true, the value is included 
13210      * otherwise it is filtered.
13211      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13212      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13213      * @return {MixedCollection} The new filtered collection
13214      */
13215     filterBy : function(fn, scope){
13216         var r = new Roo.util.MixedCollection();
13217         r.getKey = this.getKey;
13218         var k = this.keys, it = this.items;
13219         for(var i = 0, len = it.length; i < len; i++){
13220             if(fn.call(scope||this, it[i], k[i])){
13221                                 r.add(k[i], it[i]);
13222                         }
13223         }
13224         return r;
13225     },
13226     
13227     /**
13228      * Creates a duplicate of this collection
13229      * @return {MixedCollection}
13230      */
13231     clone : function(){
13232         var r = new Roo.util.MixedCollection();
13233         var k = this.keys, it = this.items;
13234         for(var i = 0, len = it.length; i < len; i++){
13235             r.add(k[i], it[i]);
13236         }
13237         r.getKey = this.getKey;
13238         return r;
13239     }
13240 });
13241 /**
13242  * Returns the item associated with the passed key or index.
13243  * @method
13244  * @param {String/Number} key The key or index of the item.
13245  * @return {Object} The item associated with the passed key.
13246  */
13247 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13248  * Based on:
13249  * Ext JS Library 1.1.1
13250  * Copyright(c) 2006-2007, Ext JS, LLC.
13251  *
13252  * Originally Released Under LGPL - original licence link has changed is not relivant.
13253  *
13254  * Fork - LGPL
13255  * <script type="text/javascript">
13256  */
13257 /**
13258  * @class Roo.util.JSON
13259  * Modified version of Douglas Crockford"s json.js that doesn"t
13260  * mess with the Object prototype 
13261  * http://www.json.org/js.html
13262  * @singleton
13263  */
13264 Roo.util.JSON = new (function(){
13265     var useHasOwn = {}.hasOwnProperty ? true : false;
13266     
13267     // crashes Safari in some instances
13268     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13269     
13270     var pad = function(n) {
13271         return n < 10 ? "0" + n : n;
13272     };
13273     
13274     var m = {
13275         "\b": '\\b',
13276         "\t": '\\t',
13277         "\n": '\\n',
13278         "\f": '\\f',
13279         "\r": '\\r',
13280         '"' : '\\"',
13281         "\\": '\\\\'
13282     };
13283
13284     var encodeString = function(s){
13285         if (/["\\\x00-\x1f]/.test(s)) {
13286             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13287                 var c = m[b];
13288                 if(c){
13289                     return c;
13290                 }
13291                 c = b.charCodeAt();
13292                 return "\\u00" +
13293                     Math.floor(c / 16).toString(16) +
13294                     (c % 16).toString(16);
13295             }) + '"';
13296         }
13297         return '"' + s + '"';
13298     };
13299     
13300     var encodeArray = function(o){
13301         var a = ["["], b, i, l = o.length, v;
13302             for (i = 0; i < l; i += 1) {
13303                 v = o[i];
13304                 switch (typeof v) {
13305                     case "undefined":
13306                     case "function":
13307                     case "unknown":
13308                         break;
13309                     default:
13310                         if (b) {
13311                             a.push(',');
13312                         }
13313                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13314                         b = true;
13315                 }
13316             }
13317             a.push("]");
13318             return a.join("");
13319     };
13320     
13321     var encodeDate = function(o){
13322         return '"' + o.getFullYear() + "-" +
13323                 pad(o.getMonth() + 1) + "-" +
13324                 pad(o.getDate()) + "T" +
13325                 pad(o.getHours()) + ":" +
13326                 pad(o.getMinutes()) + ":" +
13327                 pad(o.getSeconds()) + '"';
13328     };
13329     
13330     /**
13331      * Encodes an Object, Array or other value
13332      * @param {Mixed} o The variable to encode
13333      * @return {String} The JSON string
13334      */
13335     this.encode = function(o)
13336     {
13337         // should this be extended to fully wrap stringify..
13338         
13339         if(typeof o == "undefined" || o === null){
13340             return "null";
13341         }else if(o instanceof Array){
13342             return encodeArray(o);
13343         }else if(o instanceof Date){
13344             return encodeDate(o);
13345         }else if(typeof o == "string"){
13346             return encodeString(o);
13347         }else if(typeof o == "number"){
13348             return isFinite(o) ? String(o) : "null";
13349         }else if(typeof o == "boolean"){
13350             return String(o);
13351         }else {
13352             var a = ["{"], b, i, v;
13353             for (i in o) {
13354                 if(!useHasOwn || o.hasOwnProperty(i)) {
13355                     v = o[i];
13356                     switch (typeof v) {
13357                     case "undefined":
13358                     case "function":
13359                     case "unknown":
13360                         break;
13361                     default:
13362                         if(b){
13363                             a.push(',');
13364                         }
13365                         a.push(this.encode(i), ":",
13366                                 v === null ? "null" : this.encode(v));
13367                         b = true;
13368                     }
13369                 }
13370             }
13371             a.push("}");
13372             return a.join("");
13373         }
13374     };
13375     
13376     /**
13377      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13378      * @param {String} json The JSON string
13379      * @return {Object} The resulting object
13380      */
13381     this.decode = function(json){
13382         
13383         return  /** eval:var:json */ eval("(" + json + ')');
13384     };
13385 })();
13386 /** 
13387  * Shorthand for {@link Roo.util.JSON#encode}
13388  * @member Roo encode 
13389  * @method */
13390 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13391 /** 
13392  * Shorthand for {@link Roo.util.JSON#decode}
13393  * @member Roo decode 
13394  * @method */
13395 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13396 /*
13397  * Based on:
13398  * Ext JS Library 1.1.1
13399  * Copyright(c) 2006-2007, Ext JS, LLC.
13400  *
13401  * Originally Released Under LGPL - original licence link has changed is not relivant.
13402  *
13403  * Fork - LGPL
13404  * <script type="text/javascript">
13405  */
13406  
13407 /**
13408  * @class Roo.util.Format
13409  * Reusable data formatting functions
13410  * @singleton
13411  */
13412 Roo.util.Format = function(){
13413     var trimRe = /^\s+|\s+$/g;
13414     return {
13415         /**
13416          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13417          * @param {String} value The string to truncate
13418          * @param {Number} length The maximum length to allow before truncating
13419          * @return {String} The converted text
13420          */
13421         ellipsis : function(value, len){
13422             if(value && value.length > len){
13423                 return value.substr(0, len-3)+"...";
13424             }
13425             return value;
13426         },
13427
13428         /**
13429          * Checks a reference and converts it to empty string if it is undefined
13430          * @param {Mixed} value Reference to check
13431          * @return {Mixed} Empty string if converted, otherwise the original value
13432          */
13433         undef : function(value){
13434             return typeof value != "undefined" ? value : "";
13435         },
13436
13437         /**
13438          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13439          * @param {String} value The string to encode
13440          * @return {String} The encoded text
13441          */
13442         htmlEncode : function(value){
13443             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13444         },
13445
13446         /**
13447          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13448          * @param {String} value The string to decode
13449          * @return {String} The decoded text
13450          */
13451         htmlDecode : function(value){
13452             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13453         },
13454
13455         /**
13456          * Trims any whitespace from either side of a string
13457          * @param {String} value The text to trim
13458          * @return {String} The trimmed text
13459          */
13460         trim : function(value){
13461             return String(value).replace(trimRe, "");
13462         },
13463
13464         /**
13465          * Returns a substring from within an original string
13466          * @param {String} value The original text
13467          * @param {Number} start The start index of the substring
13468          * @param {Number} length The length of the substring
13469          * @return {String} The substring
13470          */
13471         substr : function(value, start, length){
13472             return String(value).substr(start, length);
13473         },
13474
13475         /**
13476          * Converts a string to all lower case letters
13477          * @param {String} value The text to convert
13478          * @return {String} The converted text
13479          */
13480         lowercase : function(value){
13481             return String(value).toLowerCase();
13482         },
13483
13484         /**
13485          * Converts a string to all upper case letters
13486          * @param {String} value The text to convert
13487          * @return {String} The converted text
13488          */
13489         uppercase : function(value){
13490             return String(value).toUpperCase();
13491         },
13492
13493         /**
13494          * Converts the first character only of a string to upper case
13495          * @param {String} value The text to convert
13496          * @return {String} The converted text
13497          */
13498         capitalize : function(value){
13499             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13500         },
13501
13502         // private
13503         call : function(value, fn){
13504             if(arguments.length > 2){
13505                 var args = Array.prototype.slice.call(arguments, 2);
13506                 args.unshift(value);
13507                  
13508                 return /** eval:var:value */  eval(fn).apply(window, args);
13509             }else{
13510                 /** eval:var:value */
13511                 return /** eval:var:value */ eval(fn).call(window, value);
13512             }
13513         },
13514
13515        
13516         /**
13517          * safer version of Math.toFixed..??/
13518          * @param {Number/String} value The numeric value to format
13519          * @param {Number/String} value Decimal places 
13520          * @return {String} The formatted currency string
13521          */
13522         toFixed : function(v, n)
13523         {
13524             // why not use to fixed - precision is buggered???
13525             if (!n) {
13526                 return Math.round(v-0);
13527             }
13528             var fact = Math.pow(10,n+1);
13529             v = (Math.round((v-0)*fact))/fact;
13530             var z = (''+fact).substring(2);
13531             if (v == Math.floor(v)) {
13532                 return Math.floor(v) + '.' + z;
13533             }
13534             
13535             // now just padd decimals..
13536             var ps = String(v).split('.');
13537             var fd = (ps[1] + z);
13538             var r = fd.substring(0,n); 
13539             var rm = fd.substring(n); 
13540             if (rm < 5) {
13541                 return ps[0] + '.' + r;
13542             }
13543             r*=1; // turn it into a number;
13544             r++;
13545             if (String(r).length != n) {
13546                 ps[0]*=1;
13547                 ps[0]++;
13548                 r = String(r).substring(1); // chop the end off.
13549             }
13550             
13551             return ps[0] + '.' + r;
13552              
13553         },
13554         
13555         /**
13556          * Format a number as US currency
13557          * @param {Number/String} value The numeric value to format
13558          * @return {String} The formatted currency string
13559          */
13560         usMoney : function(v){
13561             return '$' + Roo.util.Format.number(v);
13562         },
13563         
13564         /**
13565          * Format a number
13566          * eventually this should probably emulate php's number_format
13567          * @param {Number/String} value The numeric value to format
13568          * @param {Number} decimals number of decimal places
13569          * @return {String} The formatted currency string
13570          */
13571         number : function(v,decimals)
13572         {
13573             // multiply and round.
13574             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13575             var mul = Math.pow(10, decimals);
13576             var zero = String(mul).substring(1);
13577             v = (Math.round((v-0)*mul))/mul;
13578             
13579             // if it's '0' number.. then
13580             
13581             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13582             v = String(v);
13583             var ps = v.split('.');
13584             var whole = ps[0];
13585             
13586             
13587             var r = /(\d+)(\d{3})/;
13588             // add comma's
13589             while (r.test(whole)) {
13590                 whole = whole.replace(r, '$1' + ',' + '$2');
13591             }
13592             
13593             
13594             var sub = ps[1] ?
13595                     // has decimals..
13596                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13597                     // does not have decimals
13598                     (decimals ? ('.' + zero) : '');
13599             
13600             
13601             return whole + sub ;
13602         },
13603         
13604         /**
13605          * Parse a value into a formatted date using the specified format pattern.
13606          * @param {Mixed} value The value to format
13607          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13608          * @return {String} The formatted date string
13609          */
13610         date : function(v, format){
13611             if(!v){
13612                 return "";
13613             }
13614             if(!(v instanceof Date)){
13615                 v = new Date(Date.parse(v));
13616             }
13617             return v.dateFormat(format || "m/d/Y");
13618         },
13619
13620         /**
13621          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13622          * @param {String} format Any valid date format string
13623          * @return {Function} The date formatting function
13624          */
13625         dateRenderer : function(format){
13626             return function(v){
13627                 return Roo.util.Format.date(v, format);  
13628             };
13629         },
13630
13631         // private
13632         stripTagsRE : /<\/?[^>]+>/gi,
13633         
13634         /**
13635          * Strips all HTML tags
13636          * @param {Mixed} value The text from which to strip tags
13637          * @return {String} The stripped text
13638          */
13639         stripTags : function(v){
13640             return !v ? v : String(v).replace(this.stripTagsRE, "");
13641         }
13642     };
13643 }();/*
13644  * Based on:
13645  * Ext JS Library 1.1.1
13646  * Copyright(c) 2006-2007, Ext JS, LLC.
13647  *
13648  * Originally Released Under LGPL - original licence link has changed is not relivant.
13649  *
13650  * Fork - LGPL
13651  * <script type="text/javascript">
13652  */
13653
13654
13655  
13656
13657 /**
13658  * @class Roo.MasterTemplate
13659  * @extends Roo.Template
13660  * Provides a template that can have child templates. The syntax is:
13661 <pre><code>
13662 var t = new Roo.MasterTemplate(
13663         '&lt;select name="{name}"&gt;',
13664                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13665         '&lt;/select&gt;'
13666 );
13667 t.add('options', {value: 'foo', text: 'bar'});
13668 // or you can add multiple child elements in one shot
13669 t.addAll('options', [
13670     {value: 'foo', text: 'bar'},
13671     {value: 'foo2', text: 'bar2'},
13672     {value: 'foo3', text: 'bar3'}
13673 ]);
13674 // then append, applying the master template values
13675 t.append('my-form', {name: 'my-select'});
13676 </code></pre>
13677 * A name attribute for the child template is not required if you have only one child
13678 * template or you want to refer to them by index.
13679  */
13680 Roo.MasterTemplate = function(){
13681     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13682     this.originalHtml = this.html;
13683     var st = {};
13684     var m, re = this.subTemplateRe;
13685     re.lastIndex = 0;
13686     var subIndex = 0;
13687     while(m = re.exec(this.html)){
13688         var name = m[1], content = m[2];
13689         st[subIndex] = {
13690             name: name,
13691             index: subIndex,
13692             buffer: [],
13693             tpl : new Roo.Template(content)
13694         };
13695         if(name){
13696             st[name] = st[subIndex];
13697         }
13698         st[subIndex].tpl.compile();
13699         st[subIndex].tpl.call = this.call.createDelegate(this);
13700         subIndex++;
13701     }
13702     this.subCount = subIndex;
13703     this.subs = st;
13704 };
13705 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13706     /**
13707     * The regular expression used to match sub templates
13708     * @type RegExp
13709     * @property
13710     */
13711     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13712
13713     /**
13714      * Applies the passed values to a child template.
13715      * @param {String/Number} name (optional) The name or index of the child template
13716      * @param {Array/Object} values The values to be applied to the template
13717      * @return {MasterTemplate} this
13718      */
13719      add : function(name, values){
13720         if(arguments.length == 1){
13721             values = arguments[0];
13722             name = 0;
13723         }
13724         var s = this.subs[name];
13725         s.buffer[s.buffer.length] = s.tpl.apply(values);
13726         return this;
13727     },
13728
13729     /**
13730      * Applies all the passed values to a child template.
13731      * @param {String/Number} name (optional) The name or index of the child template
13732      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13733      * @param {Boolean} reset (optional) True to reset the template first
13734      * @return {MasterTemplate} this
13735      */
13736     fill : function(name, values, reset){
13737         var a = arguments;
13738         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13739             values = a[0];
13740             name = 0;
13741             reset = a[1];
13742         }
13743         if(reset){
13744             this.reset();
13745         }
13746         for(var i = 0, len = values.length; i < len; i++){
13747             this.add(name, values[i]);
13748         }
13749         return this;
13750     },
13751
13752     /**
13753      * Resets the template for reuse
13754      * @return {MasterTemplate} this
13755      */
13756      reset : function(){
13757         var s = this.subs;
13758         for(var i = 0; i < this.subCount; i++){
13759             s[i].buffer = [];
13760         }
13761         return this;
13762     },
13763
13764     applyTemplate : function(values){
13765         var s = this.subs;
13766         var replaceIndex = -1;
13767         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13768             return s[++replaceIndex].buffer.join("");
13769         });
13770         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13771     },
13772
13773     apply : function(){
13774         return this.applyTemplate.apply(this, arguments);
13775     },
13776
13777     compile : function(){return this;}
13778 });
13779
13780 /**
13781  * Alias for fill().
13782  * @method
13783  */
13784 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13785  /**
13786  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13787  * var tpl = Roo.MasterTemplate.from('element-id');
13788  * @param {String/HTMLElement} el
13789  * @param {Object} config
13790  * @static
13791  */
13792 Roo.MasterTemplate.from = function(el, config){
13793     el = Roo.getDom(el);
13794     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13795 };/*
13796  * Based on:
13797  * Ext JS Library 1.1.1
13798  * Copyright(c) 2006-2007, Ext JS, LLC.
13799  *
13800  * Originally Released Under LGPL - original licence link has changed is not relivant.
13801  *
13802  * Fork - LGPL
13803  * <script type="text/javascript">
13804  */
13805
13806  
13807 /**
13808  * @class Roo.util.CSS
13809  * Utility class for manipulating CSS rules
13810  * @singleton
13811  */
13812 Roo.util.CSS = function(){
13813         var rules = null;
13814         var doc = document;
13815
13816     var camelRe = /(-[a-z])/gi;
13817     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13818
13819    return {
13820    /**
13821     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13822     * tag and appended to the HEAD of the document.
13823     * @param {String|Object} cssText The text containing the css rules
13824     * @param {String} id An id to add to the stylesheet for later removal
13825     * @return {StyleSheet}
13826     */
13827     createStyleSheet : function(cssText, id){
13828         var ss;
13829         var head = doc.getElementsByTagName("head")[0];
13830         var nrules = doc.createElement("style");
13831         nrules.setAttribute("type", "text/css");
13832         if(id){
13833             nrules.setAttribute("id", id);
13834         }
13835         if (typeof(cssText) != 'string') {
13836             // support object maps..
13837             // not sure if this a good idea.. 
13838             // perhaps it should be merged with the general css handling
13839             // and handle js style props.
13840             var cssTextNew = [];
13841             for(var n in cssText) {
13842                 var citems = [];
13843                 for(var k in cssText[n]) {
13844                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13845                 }
13846                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13847                 
13848             }
13849             cssText = cssTextNew.join("\n");
13850             
13851         }
13852        
13853        
13854        if(Roo.isIE){
13855            head.appendChild(nrules);
13856            ss = nrules.styleSheet;
13857            ss.cssText = cssText;
13858        }else{
13859            try{
13860                 nrules.appendChild(doc.createTextNode(cssText));
13861            }catch(e){
13862                nrules.cssText = cssText; 
13863            }
13864            head.appendChild(nrules);
13865            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13866        }
13867        this.cacheStyleSheet(ss);
13868        return ss;
13869    },
13870
13871    /**
13872     * Removes a style or link tag by id
13873     * @param {String} id The id of the tag
13874     */
13875    removeStyleSheet : function(id){
13876        var existing = doc.getElementById(id);
13877        if(existing){
13878            existing.parentNode.removeChild(existing);
13879        }
13880    },
13881
13882    /**
13883     * Dynamically swaps an existing stylesheet reference for a new one
13884     * @param {String} id The id of an existing link tag to remove
13885     * @param {String} url The href of the new stylesheet to include
13886     */
13887    swapStyleSheet : function(id, url){
13888        this.removeStyleSheet(id);
13889        var ss = doc.createElement("link");
13890        ss.setAttribute("rel", "stylesheet");
13891        ss.setAttribute("type", "text/css");
13892        ss.setAttribute("id", id);
13893        ss.setAttribute("href", url);
13894        doc.getElementsByTagName("head")[0].appendChild(ss);
13895    },
13896    
13897    /**
13898     * Refresh the rule cache if you have dynamically added stylesheets
13899     * @return {Object} An object (hash) of rules indexed by selector
13900     */
13901    refreshCache : function(){
13902        return this.getRules(true);
13903    },
13904
13905    // private
13906    cacheStyleSheet : function(stylesheet){
13907        if(!rules){
13908            rules = {};
13909        }
13910        try{// try catch for cross domain access issue
13911            var ssRules = stylesheet.cssRules || stylesheet.rules;
13912            for(var j = ssRules.length-1; j >= 0; --j){
13913                rules[ssRules[j].selectorText] = ssRules[j];
13914            }
13915        }catch(e){}
13916    },
13917    
13918    /**
13919     * Gets all css rules for the document
13920     * @param {Boolean} refreshCache true to refresh the internal cache
13921     * @return {Object} An object (hash) of rules indexed by selector
13922     */
13923    getRules : function(refreshCache){
13924                 if(rules == null || refreshCache){
13925                         rules = {};
13926                         var ds = doc.styleSheets;
13927                         for(var i =0, len = ds.length; i < len; i++){
13928                             try{
13929                         this.cacheStyleSheet(ds[i]);
13930                     }catch(e){} 
13931                 }
13932                 }
13933                 return rules;
13934         },
13935         
13936         /**
13937     * Gets an an individual CSS rule by selector(s)
13938     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13939     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13940     * @return {CSSRule} The CSS rule or null if one is not found
13941     */
13942    getRule : function(selector, refreshCache){
13943                 var rs = this.getRules(refreshCache);
13944                 if(!(selector instanceof Array)){
13945                     return rs[selector];
13946                 }
13947                 for(var i = 0; i < selector.length; i++){
13948                         if(rs[selector[i]]){
13949                                 return rs[selector[i]];
13950                         }
13951                 }
13952                 return null;
13953         },
13954         
13955         
13956         /**
13957     * Updates a rule property
13958     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13959     * @param {String} property The css property
13960     * @param {String} value The new value for the property
13961     * @return {Boolean} true If a rule was found and updated
13962     */
13963    updateRule : function(selector, property, value){
13964                 if(!(selector instanceof Array)){
13965                         var rule = this.getRule(selector);
13966                         if(rule){
13967                                 rule.style[property.replace(camelRe, camelFn)] = value;
13968                                 return true;
13969                         }
13970                 }else{
13971                         for(var i = 0; i < selector.length; i++){
13972                                 if(this.updateRule(selector[i], property, value)){
13973                                         return true;
13974                                 }
13975                         }
13976                 }
13977                 return false;
13978         }
13979    };   
13980 }();/*
13981  * Based on:
13982  * Ext JS Library 1.1.1
13983  * Copyright(c) 2006-2007, Ext JS, LLC.
13984  *
13985  * Originally Released Under LGPL - original licence link has changed is not relivant.
13986  *
13987  * Fork - LGPL
13988  * <script type="text/javascript">
13989  */
13990
13991  
13992
13993 /**
13994  * @class Roo.util.ClickRepeater
13995  * @extends Roo.util.Observable
13996  * 
13997  * A wrapper class which can be applied to any element. Fires a "click" event while the
13998  * mouse is pressed. The interval between firings may be specified in the config but
13999  * defaults to 10 milliseconds.
14000  * 
14001  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14002  * 
14003  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14004  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14005  * Similar to an autorepeat key delay.
14006  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14007  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14008  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14009  *           "interval" and "delay" are ignored. "immediate" is honored.
14010  * @cfg {Boolean} preventDefault True to prevent the default click event
14011  * @cfg {Boolean} stopDefault True to stop the default click event
14012  * 
14013  * @history
14014  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14015  *     2007-02-02 jvs Renamed to ClickRepeater
14016  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14017  *
14018  *  @constructor
14019  * @param {String/HTMLElement/Element} el The element to listen on
14020  * @param {Object} config
14021  **/
14022 Roo.util.ClickRepeater = function(el, config)
14023 {
14024     this.el = Roo.get(el);
14025     this.el.unselectable();
14026
14027     Roo.apply(this, config);
14028
14029     this.addEvents({
14030     /**
14031      * @event mousedown
14032      * Fires when the mouse button is depressed.
14033      * @param {Roo.util.ClickRepeater} this
14034      */
14035         "mousedown" : true,
14036     /**
14037      * @event click
14038      * Fires on a specified interval during the time the element is pressed.
14039      * @param {Roo.util.ClickRepeater} this
14040      */
14041         "click" : true,
14042     /**
14043      * @event mouseup
14044      * Fires when the mouse key is released.
14045      * @param {Roo.util.ClickRepeater} this
14046      */
14047         "mouseup" : true
14048     });
14049
14050     this.el.on("mousedown", this.handleMouseDown, this);
14051     if(this.preventDefault || this.stopDefault){
14052         this.el.on("click", function(e){
14053             if(this.preventDefault){
14054                 e.preventDefault();
14055             }
14056             if(this.stopDefault){
14057                 e.stopEvent();
14058             }
14059         }, this);
14060     }
14061
14062     // allow inline handler
14063     if(this.handler){
14064         this.on("click", this.handler,  this.scope || this);
14065     }
14066
14067     Roo.util.ClickRepeater.superclass.constructor.call(this);
14068 };
14069
14070 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14071     interval : 20,
14072     delay: 250,
14073     preventDefault : true,
14074     stopDefault : false,
14075     timer : 0,
14076
14077     // private
14078     handleMouseDown : function(){
14079         clearTimeout(this.timer);
14080         this.el.blur();
14081         if(this.pressClass){
14082             this.el.addClass(this.pressClass);
14083         }
14084         this.mousedownTime = new Date();
14085
14086         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14087         this.el.on("mouseout", this.handleMouseOut, this);
14088
14089         this.fireEvent("mousedown", this);
14090         this.fireEvent("click", this);
14091         
14092         this.timer = this.click.defer(this.delay || this.interval, this);
14093     },
14094
14095     // private
14096     click : function(){
14097         this.fireEvent("click", this);
14098         this.timer = this.click.defer(this.getInterval(), this);
14099     },
14100
14101     // private
14102     getInterval: function(){
14103         if(!this.accelerate){
14104             return this.interval;
14105         }
14106         var pressTime = this.mousedownTime.getElapsed();
14107         if(pressTime < 500){
14108             return 400;
14109         }else if(pressTime < 1700){
14110             return 320;
14111         }else if(pressTime < 2600){
14112             return 250;
14113         }else if(pressTime < 3500){
14114             return 180;
14115         }else if(pressTime < 4400){
14116             return 140;
14117         }else if(pressTime < 5300){
14118             return 80;
14119         }else if(pressTime < 6200){
14120             return 50;
14121         }else{
14122             return 10;
14123         }
14124     },
14125
14126     // private
14127     handleMouseOut : function(){
14128         clearTimeout(this.timer);
14129         if(this.pressClass){
14130             this.el.removeClass(this.pressClass);
14131         }
14132         this.el.on("mouseover", this.handleMouseReturn, this);
14133     },
14134
14135     // private
14136     handleMouseReturn : function(){
14137         this.el.un("mouseover", this.handleMouseReturn);
14138         if(this.pressClass){
14139             this.el.addClass(this.pressClass);
14140         }
14141         this.click();
14142     },
14143
14144     // private
14145     handleMouseUp : function(){
14146         clearTimeout(this.timer);
14147         this.el.un("mouseover", this.handleMouseReturn);
14148         this.el.un("mouseout", this.handleMouseOut);
14149         Roo.get(document).un("mouseup", this.handleMouseUp);
14150         this.el.removeClass(this.pressClass);
14151         this.fireEvent("mouseup", this);
14152     }
14153 });/*
14154  * Based on:
14155  * Ext JS Library 1.1.1
14156  * Copyright(c) 2006-2007, Ext JS, LLC.
14157  *
14158  * Originally Released Under LGPL - original licence link has changed is not relivant.
14159  *
14160  * Fork - LGPL
14161  * <script type="text/javascript">
14162  */
14163
14164  
14165 /**
14166  * @class Roo.KeyNav
14167  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14168  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14169  * way to implement custom navigation schemes for any UI component.</p>
14170  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14171  * pageUp, pageDown, del, home, end.  Usage:</p>
14172  <pre><code>
14173 var nav = new Roo.KeyNav("my-element", {
14174     "left" : function(e){
14175         this.moveLeft(e.ctrlKey);
14176     },
14177     "right" : function(e){
14178         this.moveRight(e.ctrlKey);
14179     },
14180     "enter" : function(e){
14181         this.save();
14182     },
14183     scope : this
14184 });
14185 </code></pre>
14186  * @constructor
14187  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14188  * @param {Object} config The config
14189  */
14190 Roo.KeyNav = function(el, config){
14191     this.el = Roo.get(el);
14192     Roo.apply(this, config);
14193     if(!this.disabled){
14194         this.disabled = true;
14195         this.enable();
14196     }
14197 };
14198
14199 Roo.KeyNav.prototype = {
14200     /**
14201      * @cfg {Boolean} disabled
14202      * True to disable this KeyNav instance (defaults to false)
14203      */
14204     disabled : false,
14205     /**
14206      * @cfg {String} defaultEventAction
14207      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14208      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14209      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14210      */
14211     defaultEventAction: "stopEvent",
14212     /**
14213      * @cfg {Boolean} forceKeyDown
14214      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14215      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14216      * handle keydown instead of keypress.
14217      */
14218     forceKeyDown : false,
14219
14220     // private
14221     prepareEvent : function(e){
14222         var k = e.getKey();
14223         var h = this.keyToHandler[k];
14224         //if(h && this[h]){
14225         //    e.stopPropagation();
14226         //}
14227         if(Roo.isSafari && h && k >= 37 && k <= 40){
14228             e.stopEvent();
14229         }
14230     },
14231
14232     // private
14233     relay : function(e){
14234         var k = e.getKey();
14235         var h = this.keyToHandler[k];
14236         if(h && this[h]){
14237             if(this.doRelay(e, this[h], h) !== true){
14238                 e[this.defaultEventAction]();
14239             }
14240         }
14241     },
14242
14243     // private
14244     doRelay : function(e, h, hname){
14245         return h.call(this.scope || this, e);
14246     },
14247
14248     // possible handlers
14249     enter : false,
14250     left : false,
14251     right : false,
14252     up : false,
14253     down : false,
14254     tab : false,
14255     esc : false,
14256     pageUp : false,
14257     pageDown : false,
14258     del : false,
14259     home : false,
14260     end : false,
14261
14262     // quick lookup hash
14263     keyToHandler : {
14264         37 : "left",
14265         39 : "right",
14266         38 : "up",
14267         40 : "down",
14268         33 : "pageUp",
14269         34 : "pageDown",
14270         46 : "del",
14271         36 : "home",
14272         35 : "end",
14273         13 : "enter",
14274         27 : "esc",
14275         9  : "tab"
14276     },
14277
14278         /**
14279          * Enable this KeyNav
14280          */
14281         enable: function(){
14282                 if(this.disabled){
14283             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14284             // the EventObject will normalize Safari automatically
14285             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14286                 this.el.on("keydown", this.relay,  this);
14287             }else{
14288                 this.el.on("keydown", this.prepareEvent,  this);
14289                 this.el.on("keypress", this.relay,  this);
14290             }
14291                     this.disabled = false;
14292                 }
14293         },
14294
14295         /**
14296          * Disable this KeyNav
14297          */
14298         disable: function(){
14299                 if(!this.disabled){
14300                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14301                 this.el.un("keydown", this.relay);
14302             }else{
14303                 this.el.un("keydown", this.prepareEvent);
14304                 this.el.un("keypress", this.relay);
14305             }
14306                     this.disabled = true;
14307                 }
14308         }
14309 };/*
14310  * Based on:
14311  * Ext JS Library 1.1.1
14312  * Copyright(c) 2006-2007, Ext JS, LLC.
14313  *
14314  * Originally Released Under LGPL - original licence link has changed is not relivant.
14315  *
14316  * Fork - LGPL
14317  * <script type="text/javascript">
14318  */
14319
14320  
14321 /**
14322  * @class Roo.KeyMap
14323  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14324  * The constructor accepts the same config object as defined by {@link #addBinding}.
14325  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14326  * combination it will call the function with this signature (if the match is a multi-key
14327  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14328  * A KeyMap can also handle a string representation of keys.<br />
14329  * Usage:
14330  <pre><code>
14331 // map one key by key code
14332 var map = new Roo.KeyMap("my-element", {
14333     key: 13, // or Roo.EventObject.ENTER
14334     fn: myHandler,
14335     scope: myObject
14336 });
14337
14338 // map multiple keys to one action by string
14339 var map = new Roo.KeyMap("my-element", {
14340     key: "a\r\n\t",
14341     fn: myHandler,
14342     scope: myObject
14343 });
14344
14345 // map multiple keys to multiple actions by strings and array of codes
14346 var map = new Roo.KeyMap("my-element", [
14347     {
14348         key: [10,13],
14349         fn: function(){ alert("Return was pressed"); }
14350     }, {
14351         key: "abc",
14352         fn: function(){ alert('a, b or c was pressed'); }
14353     }, {
14354         key: "\t",
14355         ctrl:true,
14356         shift:true,
14357         fn: function(){ alert('Control + shift + tab was pressed.'); }
14358     }
14359 ]);
14360 </code></pre>
14361  * <b>Note: A KeyMap starts enabled</b>
14362  * @constructor
14363  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14364  * @param {Object} config The config (see {@link #addBinding})
14365  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14366  */
14367 Roo.KeyMap = function(el, config, eventName){
14368     this.el  = Roo.get(el);
14369     this.eventName = eventName || "keydown";
14370     this.bindings = [];
14371     if(config){
14372         this.addBinding(config);
14373     }
14374     this.enable();
14375 };
14376
14377 Roo.KeyMap.prototype = {
14378     /**
14379      * True to stop the event from bubbling and prevent the default browser action if the
14380      * key was handled by the KeyMap (defaults to false)
14381      * @type Boolean
14382      */
14383     stopEvent : false,
14384
14385     /**
14386      * Add a new binding to this KeyMap. The following config object properties are supported:
14387      * <pre>
14388 Property    Type             Description
14389 ----------  ---------------  ----------------------------------------------------------------------
14390 key         String/Array     A single keycode or an array of keycodes to handle
14391 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14392 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14393 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14394 fn          Function         The function to call when KeyMap finds the expected key combination
14395 scope       Object           The scope of the callback function
14396 </pre>
14397      *
14398      * Usage:
14399      * <pre><code>
14400 // Create a KeyMap
14401 var map = new Roo.KeyMap(document, {
14402     key: Roo.EventObject.ENTER,
14403     fn: handleKey,
14404     scope: this
14405 });
14406
14407 //Add a new binding to the existing KeyMap later
14408 map.addBinding({
14409     key: 'abc',
14410     shift: true,
14411     fn: handleKey,
14412     scope: this
14413 });
14414 </code></pre>
14415      * @param {Object/Array} config A single KeyMap config or an array of configs
14416      */
14417         addBinding : function(config){
14418         if(config instanceof Array){
14419             for(var i = 0, len = config.length; i < len; i++){
14420                 this.addBinding(config[i]);
14421             }
14422             return;
14423         }
14424         var keyCode = config.key,
14425             shift = config.shift, 
14426             ctrl = config.ctrl, 
14427             alt = config.alt,
14428             fn = config.fn,
14429             scope = config.scope;
14430         if(typeof keyCode == "string"){
14431             var ks = [];
14432             var keyString = keyCode.toUpperCase();
14433             for(var j = 0, len = keyString.length; j < len; j++){
14434                 ks.push(keyString.charCodeAt(j));
14435             }
14436             keyCode = ks;
14437         }
14438         var keyArray = keyCode instanceof Array;
14439         var handler = function(e){
14440             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14441                 var k = e.getKey();
14442                 if(keyArray){
14443                     for(var i = 0, len = keyCode.length; i < len; i++){
14444                         if(keyCode[i] == k){
14445                           if(this.stopEvent){
14446                               e.stopEvent();
14447                           }
14448                           fn.call(scope || window, k, e);
14449                           return;
14450                         }
14451                     }
14452                 }else{
14453                     if(k == keyCode){
14454                         if(this.stopEvent){
14455                            e.stopEvent();
14456                         }
14457                         fn.call(scope || window, k, e);
14458                     }
14459                 }
14460             }
14461         };
14462         this.bindings.push(handler);  
14463         },
14464
14465     /**
14466      * Shorthand for adding a single key listener
14467      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14468      * following options:
14469      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14470      * @param {Function} fn The function to call
14471      * @param {Object} scope (optional) The scope of the function
14472      */
14473     on : function(key, fn, scope){
14474         var keyCode, shift, ctrl, alt;
14475         if(typeof key == "object" && !(key instanceof Array)){
14476             keyCode = key.key;
14477             shift = key.shift;
14478             ctrl = key.ctrl;
14479             alt = key.alt;
14480         }else{
14481             keyCode = key;
14482         }
14483         this.addBinding({
14484             key: keyCode,
14485             shift: shift,
14486             ctrl: ctrl,
14487             alt: alt,
14488             fn: fn,
14489             scope: scope
14490         })
14491     },
14492
14493     // private
14494     handleKeyDown : function(e){
14495             if(this.enabled){ //just in case
14496             var b = this.bindings;
14497             for(var i = 0, len = b.length; i < len; i++){
14498                 b[i].call(this, e);
14499             }
14500             }
14501         },
14502         
14503         /**
14504          * Returns true if this KeyMap is enabled
14505          * @return {Boolean} 
14506          */
14507         isEnabled : function(){
14508             return this.enabled;  
14509         },
14510         
14511         /**
14512          * Enables this KeyMap
14513          */
14514         enable: function(){
14515                 if(!this.enabled){
14516                     this.el.on(this.eventName, this.handleKeyDown, this);
14517                     this.enabled = true;
14518                 }
14519         },
14520
14521         /**
14522          * Disable this KeyMap
14523          */
14524         disable: function(){
14525                 if(this.enabled){
14526                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14527                     this.enabled = false;
14528                 }
14529         }
14530 };/*
14531  * Based on:
14532  * Ext JS Library 1.1.1
14533  * Copyright(c) 2006-2007, Ext JS, LLC.
14534  *
14535  * Originally Released Under LGPL - original licence link has changed is not relivant.
14536  *
14537  * Fork - LGPL
14538  * <script type="text/javascript">
14539  */
14540
14541  
14542 /**
14543  * @class Roo.util.TextMetrics
14544  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14545  * wide, in pixels, a given block of text will be.
14546  * @singleton
14547  */
14548 Roo.util.TextMetrics = function(){
14549     var shared;
14550     return {
14551         /**
14552          * Measures the size of the specified text
14553          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14554          * that can affect the size of the rendered text
14555          * @param {String} text The text to measure
14556          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14557          * in order to accurately measure the text height
14558          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14559          */
14560         measure : function(el, text, fixedWidth){
14561             if(!shared){
14562                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14563             }
14564             shared.bind(el);
14565             shared.setFixedWidth(fixedWidth || 'auto');
14566             return shared.getSize(text);
14567         },
14568
14569         /**
14570          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14571          * the overhead of multiple calls to initialize the style properties on each measurement.
14572          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14573          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14574          * in order to accurately measure the text height
14575          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14576          */
14577         createInstance : function(el, fixedWidth){
14578             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14579         }
14580     };
14581 }();
14582
14583  
14584
14585 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14586     var ml = new Roo.Element(document.createElement('div'));
14587     document.body.appendChild(ml.dom);
14588     ml.position('absolute');
14589     ml.setLeftTop(-1000, -1000);
14590     ml.hide();
14591
14592     if(fixedWidth){
14593         ml.setWidth(fixedWidth);
14594     }
14595      
14596     var instance = {
14597         /**
14598          * Returns the size of the specified text based on the internal element's style and width properties
14599          * @memberOf Roo.util.TextMetrics.Instance#
14600          * @param {String} text The text to measure
14601          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14602          */
14603         getSize : function(text){
14604             ml.update(text);
14605             var s = ml.getSize();
14606             ml.update('');
14607             return s;
14608         },
14609
14610         /**
14611          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14612          * that can affect the size of the rendered text
14613          * @memberOf Roo.util.TextMetrics.Instance#
14614          * @param {String/HTMLElement} el The element, dom node or id
14615          */
14616         bind : function(el){
14617             ml.setStyle(
14618                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14619             );
14620         },
14621
14622         /**
14623          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14624          * to set a fixed width in order to accurately measure the text height.
14625          * @memberOf Roo.util.TextMetrics.Instance#
14626          * @param {Number} width The width to set on the element
14627          */
14628         setFixedWidth : function(width){
14629             ml.setWidth(width);
14630         },
14631
14632         /**
14633          * Returns the measured width of the specified text
14634          * @memberOf Roo.util.TextMetrics.Instance#
14635          * @param {String} text The text to measure
14636          * @return {Number} width The width in pixels
14637          */
14638         getWidth : function(text){
14639             ml.dom.style.width = 'auto';
14640             return this.getSize(text).width;
14641         },
14642
14643         /**
14644          * Returns the measured height of the specified text.  For multiline text, be sure to call
14645          * {@link #setFixedWidth} if necessary.
14646          * @memberOf Roo.util.TextMetrics.Instance#
14647          * @param {String} text The text to measure
14648          * @return {Number} height The height in pixels
14649          */
14650         getHeight : function(text){
14651             return this.getSize(text).height;
14652         }
14653     };
14654
14655     instance.bind(bindTo);
14656
14657     return instance;
14658 };
14659
14660 // backwards compat
14661 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14662  * Based on:
14663  * Ext JS Library 1.1.1
14664  * Copyright(c) 2006-2007, Ext JS, LLC.
14665  *
14666  * Originally Released Under LGPL - original licence link has changed is not relivant.
14667  *
14668  * Fork - LGPL
14669  * <script type="text/javascript">
14670  */
14671
14672 /**
14673  * @class Roo.state.Provider
14674  * Abstract base class for state provider implementations. This class provides methods
14675  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14676  * Provider interface.
14677  */
14678 Roo.state.Provider = function(){
14679     /**
14680      * @event statechange
14681      * Fires when a state change occurs.
14682      * @param {Provider} this This state provider
14683      * @param {String} key The state key which was changed
14684      * @param {String} value The encoded value for the state
14685      */
14686     this.addEvents({
14687         "statechange": true
14688     });
14689     this.state = {};
14690     Roo.state.Provider.superclass.constructor.call(this);
14691 };
14692 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14693     /**
14694      * Returns the current value for a key
14695      * @param {String} name The key name
14696      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14697      * @return {Mixed} The state data
14698      */
14699     get : function(name, defaultValue){
14700         return typeof this.state[name] == "undefined" ?
14701             defaultValue : this.state[name];
14702     },
14703     
14704     /**
14705      * Clears a value from the state
14706      * @param {String} name The key name
14707      */
14708     clear : function(name){
14709         delete this.state[name];
14710         this.fireEvent("statechange", this, name, null);
14711     },
14712     
14713     /**
14714      * Sets the value for a key
14715      * @param {String} name The key name
14716      * @param {Mixed} value The value to set
14717      */
14718     set : function(name, value){
14719         this.state[name] = value;
14720         this.fireEvent("statechange", this, name, value);
14721     },
14722     
14723     /**
14724      * Decodes a string previously encoded with {@link #encodeValue}.
14725      * @param {String} value The value to decode
14726      * @return {Mixed} The decoded value
14727      */
14728     decodeValue : function(cookie){
14729         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14730         var matches = re.exec(unescape(cookie));
14731         if(!matches || !matches[1]) return; // non state cookie
14732         var type = matches[1];
14733         var v = matches[2];
14734         switch(type){
14735             case "n":
14736                 return parseFloat(v);
14737             case "d":
14738                 return new Date(Date.parse(v));
14739             case "b":
14740                 return (v == "1");
14741             case "a":
14742                 var all = [];
14743                 var values = v.split("^");
14744                 for(var i = 0, len = values.length; i < len; i++){
14745                     all.push(this.decodeValue(values[i]));
14746                 }
14747                 return all;
14748            case "o":
14749                 var all = {};
14750                 var values = v.split("^");
14751                 for(var i = 0, len = values.length; i < len; i++){
14752                     var kv = values[i].split("=");
14753                     all[kv[0]] = this.decodeValue(kv[1]);
14754                 }
14755                 return all;
14756            default:
14757                 return v;
14758         }
14759     },
14760     
14761     /**
14762      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14763      * @param {Mixed} value The value to encode
14764      * @return {String} The encoded value
14765      */
14766     encodeValue : function(v){
14767         var enc;
14768         if(typeof v == "number"){
14769             enc = "n:" + v;
14770         }else if(typeof v == "boolean"){
14771             enc = "b:" + (v ? "1" : "0");
14772         }else if(v instanceof Date){
14773             enc = "d:" + v.toGMTString();
14774         }else if(v instanceof Array){
14775             var flat = "";
14776             for(var i = 0, len = v.length; i < len; i++){
14777                 flat += this.encodeValue(v[i]);
14778                 if(i != len-1) flat += "^";
14779             }
14780             enc = "a:" + flat;
14781         }else if(typeof v == "object"){
14782             var flat = "";
14783             for(var key in v){
14784                 if(typeof v[key] != "function"){
14785                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14786                 }
14787             }
14788             enc = "o:" + flat.substring(0, flat.length-1);
14789         }else{
14790             enc = "s:" + v;
14791         }
14792         return escape(enc);        
14793     }
14794 });
14795
14796 /*
14797  * Based on:
14798  * Ext JS Library 1.1.1
14799  * Copyright(c) 2006-2007, Ext JS, LLC.
14800  *
14801  * Originally Released Under LGPL - original licence link has changed is not relivant.
14802  *
14803  * Fork - LGPL
14804  * <script type="text/javascript">
14805  */
14806 /**
14807  * @class Roo.state.Manager
14808  * This is the global state manager. By default all components that are "state aware" check this class
14809  * for state information if you don't pass them a custom state provider. In order for this class
14810  * to be useful, it must be initialized with a provider when your application initializes.
14811  <pre><code>
14812 // in your initialization function
14813 init : function(){
14814    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14815    ...
14816    // supposed you have a {@link Roo.BorderLayout}
14817    var layout = new Roo.BorderLayout(...);
14818    layout.restoreState();
14819    // or a {Roo.BasicDialog}
14820    var dialog = new Roo.BasicDialog(...);
14821    dialog.restoreState();
14822  </code></pre>
14823  * @singleton
14824  */
14825 Roo.state.Manager = function(){
14826     var provider = new Roo.state.Provider();
14827     
14828     return {
14829         /**
14830          * Configures the default state provider for your application
14831          * @param {Provider} stateProvider The state provider to set
14832          */
14833         setProvider : function(stateProvider){
14834             provider = stateProvider;
14835         },
14836         
14837         /**
14838          * Returns the current value for a key
14839          * @param {String} name The key name
14840          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14841          * @return {Mixed} The state data
14842          */
14843         get : function(key, defaultValue){
14844             return provider.get(key, defaultValue);
14845         },
14846         
14847         /**
14848          * Sets the value for a key
14849          * @param {String} name The key name
14850          * @param {Mixed} value The state data
14851          */
14852          set : function(key, value){
14853             provider.set(key, value);
14854         },
14855         
14856         /**
14857          * Clears a value from the state
14858          * @param {String} name The key name
14859          */
14860         clear : function(key){
14861             provider.clear(key);
14862         },
14863         
14864         /**
14865          * Gets the currently configured state provider
14866          * @return {Provider} The state provider
14867          */
14868         getProvider : function(){
14869             return provider;
14870         }
14871     };
14872 }();
14873 /*
14874  * Based on:
14875  * Ext JS Library 1.1.1
14876  * Copyright(c) 2006-2007, Ext JS, LLC.
14877  *
14878  * Originally Released Under LGPL - original licence link has changed is not relivant.
14879  *
14880  * Fork - LGPL
14881  * <script type="text/javascript">
14882  */
14883 /**
14884  * @class Roo.state.CookieProvider
14885  * @extends Roo.state.Provider
14886  * The default Provider implementation which saves state via cookies.
14887  * <br />Usage:
14888  <pre><code>
14889    var cp = new Roo.state.CookieProvider({
14890        path: "/cgi-bin/",
14891        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14892        domain: "roojs.com"
14893    })
14894    Roo.state.Manager.setProvider(cp);
14895  </code></pre>
14896  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14897  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14898  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14899  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14900  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14901  * domain the page is running on including the 'www' like 'www.roojs.com')
14902  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14903  * @constructor
14904  * Create a new CookieProvider
14905  * @param {Object} config The configuration object
14906  */
14907 Roo.state.CookieProvider = function(config){
14908     Roo.state.CookieProvider.superclass.constructor.call(this);
14909     this.path = "/";
14910     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14911     this.domain = null;
14912     this.secure = false;
14913     Roo.apply(this, config);
14914     this.state = this.readCookies();
14915 };
14916
14917 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14918     // private
14919     set : function(name, value){
14920         if(typeof value == "undefined" || value === null){
14921             this.clear(name);
14922             return;
14923         }
14924         this.setCookie(name, value);
14925         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14926     },
14927
14928     // private
14929     clear : function(name){
14930         this.clearCookie(name);
14931         Roo.state.CookieProvider.superclass.clear.call(this, name);
14932     },
14933
14934     // private
14935     readCookies : function(){
14936         var cookies = {};
14937         var c = document.cookie + ";";
14938         var re = /\s?(.*?)=(.*?);/g;
14939         var matches;
14940         while((matches = re.exec(c)) != null){
14941             var name = matches[1];
14942             var value = matches[2];
14943             if(name && name.substring(0,3) == "ys-"){
14944                 cookies[name.substr(3)] = this.decodeValue(value);
14945             }
14946         }
14947         return cookies;
14948     },
14949
14950     // private
14951     setCookie : function(name, value){
14952         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14953            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14954            ((this.path == null) ? "" : ("; path=" + this.path)) +
14955            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14956            ((this.secure == true) ? "; secure" : "");
14957     },
14958
14959     // private
14960     clearCookie : function(name){
14961         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14962            ((this.path == null) ? "" : ("; path=" + this.path)) +
14963            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14964            ((this.secure == true) ? "; secure" : "");
14965     }
14966 });/*
14967  * Based on:
14968  * Ext JS Library 1.1.1
14969  * Copyright(c) 2006-2007, Ext JS, LLC.
14970  *
14971  * Originally Released Under LGPL - original licence link has changed is not relivant.
14972  *
14973  * Fork - LGPL
14974  * <script type="text/javascript">
14975  */
14976
14977
14978
14979 /*
14980  * These classes are derivatives of the similarly named classes in the YUI Library.
14981  * The original license:
14982  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14983  * Code licensed under the BSD License:
14984  * http://developer.yahoo.net/yui/license.txt
14985  */
14986
14987 (function() {
14988
14989 var Event=Roo.EventManager;
14990 var Dom=Roo.lib.Dom;
14991
14992 /**
14993  * @class Roo.dd.DragDrop
14994  * @extends Roo.util.Observable
14995  * Defines the interface and base operation of items that that can be
14996  * dragged or can be drop targets.  It was designed to be extended, overriding
14997  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14998  * Up to three html elements can be associated with a DragDrop instance:
14999  * <ul>
15000  * <li>linked element: the element that is passed into the constructor.
15001  * This is the element which defines the boundaries for interaction with
15002  * other DragDrop objects.</li>
15003  * <li>handle element(s): The drag operation only occurs if the element that
15004  * was clicked matches a handle element.  By default this is the linked
15005  * element, but there are times that you will want only a portion of the
15006  * linked element to initiate the drag operation, and the setHandleElId()
15007  * method provides a way to define this.</li>
15008  * <li>drag element: this represents the element that would be moved along
15009  * with the cursor during a drag operation.  By default, this is the linked
15010  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
15011  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
15012  * </li>
15013  * </ul>
15014  * This class should not be instantiated until the onload event to ensure that
15015  * the associated elements are available.
15016  * The following would define a DragDrop obj that would interact with any
15017  * other DragDrop obj in the "group1" group:
15018  * <pre>
15019  *  dd = new Roo.dd.DragDrop("div1", "group1");
15020  * </pre>
15021  * Since none of the event handlers have been implemented, nothing would
15022  * actually happen if you were to run the code above.  Normally you would
15023  * override this class or one of the default implementations, but you can
15024  * also override the methods you want on an instance of the class...
15025  * <pre>
15026  *  dd.onDragDrop = function(e, id) {
15027  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
15028  *  }
15029  * </pre>
15030  * @constructor
15031  * @param {String} id of the element that is linked to this instance
15032  * @param {String} sGroup the group of related DragDrop objects
15033  * @param {object} config an object containing configurable attributes
15034  *                Valid properties for DragDrop:
15035  *                    padding, isTarget, maintainOffset, primaryButtonOnly
15036  */
15037 Roo.dd.DragDrop = function(id, sGroup, config) {
15038     if (id) {
15039         this.init(id, sGroup, config);
15040     }
15041     
15042 };
15043
15044 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
15045
15046     /**
15047      * The id of the element associated with this object.  This is what we
15048      * refer to as the "linked element" because the size and position of
15049      * this element is used to determine when the drag and drop objects have
15050      * interacted.
15051      * @property id
15052      * @type String
15053      */
15054     id: null,
15055
15056     /**
15057      * Configuration attributes passed into the constructor
15058      * @property config
15059      * @type object
15060      */
15061     config: null,
15062
15063     /**
15064      * The id of the element that will be dragged.  By default this is same
15065      * as the linked element , but could be changed to another element. Ex:
15066      * Roo.dd.DDProxy
15067      * @property dragElId
15068      * @type String
15069      * @private
15070      */
15071     dragElId: null,
15072
15073     /**
15074      * the id of the element that initiates the drag operation.  By default
15075      * this is the linked element, but could be changed to be a child of this
15076      * element.  This lets us do things like only starting the drag when the
15077      * header element within the linked html element is clicked.
15078      * @property handleElId
15079      * @type String
15080      * @private
15081      */
15082     handleElId: null,
15083
15084     /**
15085      * An associative array of HTML tags that will be ignored if clicked.
15086      * @property invalidHandleTypes
15087      * @type {string: string}
15088      */
15089     invalidHandleTypes: null,
15090
15091     /**
15092      * An associative array of ids for elements that will be ignored if clicked
15093      * @property invalidHandleIds
15094      * @type {string: string}
15095      */
15096     invalidHandleIds: null,
15097
15098     /**
15099      * An indexted array of css class names for elements that will be ignored
15100      * if clicked.
15101      * @property invalidHandleClasses
15102      * @type string[]
15103      */
15104     invalidHandleClasses: null,
15105
15106     /**
15107      * The linked element's absolute X position at the time the drag was
15108      * started
15109      * @property startPageX
15110      * @type int
15111      * @private
15112      */
15113     startPageX: 0,
15114
15115     /**
15116      * The linked element's absolute X position at the time the drag was
15117      * started
15118      * @property startPageY
15119      * @type int
15120      * @private
15121      */
15122     startPageY: 0,
15123
15124     /**
15125      * The group defines a logical collection of DragDrop objects that are
15126      * related.  Instances only get events when interacting with other
15127      * DragDrop object in the same group.  This lets us define multiple
15128      * groups using a single DragDrop subclass if we want.
15129      * @property groups
15130      * @type {string: string}
15131      */
15132     groups: null,
15133
15134     /**
15135      * Individual drag/drop instances can be locked.  This will prevent
15136      * onmousedown start drag.
15137      * @property locked
15138      * @type boolean
15139      * @private
15140      */
15141     locked: false,
15142
15143     /**
15144      * Lock this instance
15145      * @method lock
15146      */
15147     lock: function() { this.locked = true; },
15148
15149     /**
15150      * Unlock this instace
15151      * @method unlock
15152      */
15153     unlock: function() { this.locked = false; },
15154
15155     /**
15156      * By default, all insances can be a drop target.  This can be disabled by
15157      * setting isTarget to false.
15158      * @method isTarget
15159      * @type boolean
15160      */
15161     isTarget: true,
15162
15163     /**
15164      * The padding configured for this drag and drop object for calculating
15165      * the drop zone intersection with this object.
15166      * @method padding
15167      * @type int[]
15168      */
15169     padding: null,
15170
15171     /**
15172      * Cached reference to the linked element
15173      * @property _domRef
15174      * @private
15175      */
15176     _domRef: null,
15177
15178     /**
15179      * Internal typeof flag
15180      * @property __ygDragDrop
15181      * @private
15182      */
15183     __ygDragDrop: true,
15184
15185     /**
15186      * Set to true when horizontal contraints are applied
15187      * @property constrainX
15188      * @type boolean
15189      * @private
15190      */
15191     constrainX: false,
15192
15193     /**
15194      * Set to true when vertical contraints are applied
15195      * @property constrainY
15196      * @type boolean
15197      * @private
15198      */
15199     constrainY: false,
15200
15201     /**
15202      * The left constraint
15203      * @property minX
15204      * @type int
15205      * @private
15206      */
15207     minX: 0,
15208
15209     /**
15210      * The right constraint
15211      * @property maxX
15212      * @type int
15213      * @private
15214      */
15215     maxX: 0,
15216
15217     /**
15218      * The up constraint
15219      * @property minY
15220      * @type int
15221      * @type int
15222      * @private
15223      */
15224     minY: 0,
15225
15226     /**
15227      * The down constraint
15228      * @property maxY
15229      * @type int
15230      * @private
15231      */
15232     maxY: 0,
15233
15234     /**
15235      * Maintain offsets when we resetconstraints.  Set to true when you want
15236      * the position of the element relative to its parent to stay the same
15237      * when the page changes
15238      *
15239      * @property maintainOffset
15240      * @type boolean
15241      */
15242     maintainOffset: false,
15243
15244     /**
15245      * Array of pixel locations the element will snap to if we specified a
15246      * horizontal graduation/interval.  This array is generated automatically
15247      * when you define a tick interval.
15248      * @property xTicks
15249      * @type int[]
15250      */
15251     xTicks: null,
15252
15253     /**
15254      * Array of pixel locations the element will snap to if we specified a
15255      * vertical graduation/interval.  This array is generated automatically
15256      * when you define a tick interval.
15257      * @property yTicks
15258      * @type int[]
15259      */
15260     yTicks: null,
15261
15262     /**
15263      * By default the drag and drop instance will only respond to the primary
15264      * button click (left button for a right-handed mouse).  Set to true to
15265      * allow drag and drop to start with any mouse click that is propogated
15266      * by the browser
15267      * @property primaryButtonOnly
15268      * @type boolean
15269      */
15270     primaryButtonOnly: true,
15271
15272     /**
15273      * The availabe property is false until the linked dom element is accessible.
15274      * @property available
15275      * @type boolean
15276      */
15277     available: false,
15278
15279     /**
15280      * By default, drags can only be initiated if the mousedown occurs in the
15281      * region the linked element is.  This is done in part to work around a
15282      * bug in some browsers that mis-report the mousedown if the previous
15283      * mouseup happened outside of the window.  This property is set to true
15284      * if outer handles are defined.
15285      *
15286      * @property hasOuterHandles
15287      * @type boolean
15288      * @default false
15289      */
15290     hasOuterHandles: false,
15291
15292     /**
15293      * Code that executes immediately before the startDrag event
15294      * @method b4StartDrag
15295      * @private
15296      */
15297     b4StartDrag: function(x, y) { },
15298
15299     /**
15300      * Abstract method called after a drag/drop object is clicked
15301      * and the drag or mousedown time thresholds have beeen met.
15302      * @method startDrag
15303      * @param {int} X click location
15304      * @param {int} Y click location
15305      */
15306     startDrag: function(x, y) { /* override this */ },
15307
15308     /**
15309      * Code that executes immediately before the onDrag event
15310      * @method b4Drag
15311      * @private
15312      */
15313     b4Drag: function(e) { },
15314
15315     /**
15316      * Abstract method called during the onMouseMove event while dragging an
15317      * object.
15318      * @method onDrag
15319      * @param {Event} e the mousemove event
15320      */
15321     onDrag: function(e) { /* override this */ },
15322
15323     /**
15324      * Abstract method called when this element fist begins hovering over
15325      * another DragDrop obj
15326      * @method onDragEnter
15327      * @param {Event} e the mousemove event
15328      * @param {String|DragDrop[]} id In POINT mode, the element
15329      * id this is hovering over.  In INTERSECT mode, an array of one or more
15330      * dragdrop items being hovered over.
15331      */
15332     onDragEnter: function(e, id) { /* override this */ },
15333
15334     /**
15335      * Code that executes immediately before the onDragOver event
15336      * @method b4DragOver
15337      * @private
15338      */
15339     b4DragOver: function(e) { },
15340
15341     /**
15342      * Abstract method called when this element is hovering over another
15343      * DragDrop obj
15344      * @method onDragOver
15345      * @param {Event} e the mousemove event
15346      * @param {String|DragDrop[]} id In POINT mode, the element
15347      * id this is hovering over.  In INTERSECT mode, an array of dd items
15348      * being hovered over.
15349      */
15350     onDragOver: function(e, id) { /* override this */ },
15351
15352     /**
15353      * Code that executes immediately before the onDragOut event
15354      * @method b4DragOut
15355      * @private
15356      */
15357     b4DragOut: function(e) { },
15358
15359     /**
15360      * Abstract method called when we are no longer hovering over an element
15361      * @method onDragOut
15362      * @param {Event} e the mousemove event
15363      * @param {String|DragDrop[]} id In POINT mode, the element
15364      * id this was hovering over.  In INTERSECT mode, an array of dd items
15365      * that the mouse is no longer over.
15366      */
15367     onDragOut: function(e, id) { /* override this */ },
15368
15369     /**
15370      * Code that executes immediately before the onDragDrop event
15371      * @method b4DragDrop
15372      * @private
15373      */
15374     b4DragDrop: function(e) { },
15375
15376     /**
15377      * Abstract method called when this item is dropped on another DragDrop
15378      * obj
15379      * @method onDragDrop
15380      * @param {Event} e the mouseup event
15381      * @param {String|DragDrop[]} id In POINT mode, the element
15382      * id this was dropped on.  In INTERSECT mode, an array of dd items this
15383      * was dropped on.
15384      */
15385     onDragDrop: function(e, id) { /* override this */ },
15386
15387     /**
15388      * Abstract method called when this item is dropped on an area with no
15389      * drop target
15390      * @method onInvalidDrop
15391      * @param {Event} e the mouseup event
15392      */
15393     onInvalidDrop: function(e) { /* override this */ },
15394
15395     /**
15396      * Code that executes immediately before the endDrag event
15397      * @method b4EndDrag
15398      * @private
15399      */
15400     b4EndDrag: function(e) { },
15401
15402     /**
15403      * Fired when we are done dragging the object
15404      * @method endDrag
15405      * @param {Event} e the mouseup event
15406      */
15407     endDrag: function(e) { /* override this */ },
15408
15409     /**
15410      * Code executed immediately before the onMouseDown event
15411      * @method b4MouseDown
15412      * @param {Event} e the mousedown event
15413      * @private
15414      */
15415     b4MouseDown: function(e) {  },
15416
15417     /**
15418      * Event handler that fires when a drag/drop obj gets a mousedown
15419      * @method onMouseDown
15420      * @param {Event} e the mousedown event
15421      */
15422     onMouseDown: function(e) { /* override this */ },
15423
15424     /**
15425      * Event handler that fires when a drag/drop obj gets a mouseup
15426      * @method onMouseUp
15427      * @param {Event} e the mouseup event
15428      */
15429     onMouseUp: function(e) { /* override this */ },
15430
15431     /**
15432      * Override the onAvailable method to do what is needed after the initial
15433      * position was determined.
15434      * @method onAvailable
15435      */
15436     onAvailable: function () {
15437     },
15438
15439     /*
15440      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
15441      * @type Object
15442      */
15443     defaultPadding : {left:0, right:0, top:0, bottom:0},
15444
15445     /*
15446      * Initializes the drag drop object's constraints to restrict movement to a certain element.
15447  *
15448  * Usage:
15449  <pre><code>
15450  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
15451                 { dragElId: "existingProxyDiv" });
15452  dd.startDrag = function(){
15453      this.constrainTo("parent-id");
15454  };
15455  </code></pre>
15456  * Or you can initalize it using the {@link Roo.Element} object:
15457  <pre><code>
15458  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
15459      startDrag : function(){
15460          this.constrainTo("parent-id");
15461      }
15462  });
15463  </code></pre>
15464      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
15465      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
15466      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
15467      * an object containing the sides to pad. For example: {right:10, bottom:10}
15468      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
15469      */
15470     constrainTo : function(constrainTo, pad, inContent){
15471         if(typeof pad == "number"){
15472             pad = {left: pad, right:pad, top:pad, bottom:pad};
15473         }
15474         pad = pad || this.defaultPadding;
15475         var b = Roo.get(this.getEl()).getBox();
15476         var ce = Roo.get(constrainTo);
15477         var s = ce.getScroll();
15478         var c, cd = ce.dom;
15479         if(cd == document.body){
15480             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15481         }else{
15482             xy = ce.getXY();
15483             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15484         }
15485
15486
15487         var topSpace = b.y - c.y;
15488         var leftSpace = b.x - c.x;
15489
15490         this.resetConstraints();
15491         this.setXConstraint(leftSpace - (pad.left||0), // left
15492                 c.width - leftSpace - b.width - (pad.right||0) //right
15493         );
15494         this.setYConstraint(topSpace - (pad.top||0), //top
15495                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15496         );
15497     },
15498
15499     /**
15500      * Returns a reference to the linked element
15501      * @method getEl
15502      * @return {HTMLElement} the html element
15503      */
15504     getEl: function() {
15505         if (!this._domRef) {
15506             this._domRef = Roo.getDom(this.id);
15507         }
15508
15509         return this._domRef;
15510     },
15511
15512     /**
15513      * Returns a reference to the actual element to drag.  By default this is
15514      * the same as the html element, but it can be assigned to another
15515      * element. An example of this can be found in Roo.dd.DDProxy
15516      * @method getDragEl
15517      * @return {HTMLElement} the html element
15518      */
15519     getDragEl: function() {
15520         return Roo.getDom(this.dragElId);
15521     },
15522
15523     /**
15524      * Sets up the DragDrop object.  Must be called in the constructor of any
15525      * Roo.dd.DragDrop subclass
15526      * @method init
15527      * @param id the id of the linked element
15528      * @param {String} sGroup the group of related items
15529      * @param {object} config configuration attributes
15530      */
15531     init: function(id, sGroup, config) {
15532         this.initTarget(id, sGroup, config);
15533         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15534         // Event.on(this.id, "selectstart", Event.preventDefault);
15535     },
15536
15537     /**
15538      * Initializes Targeting functionality only... the object does not
15539      * get a mousedown handler.
15540      * @method initTarget
15541      * @param id the id of the linked element
15542      * @param {String} sGroup the group of related items
15543      * @param {object} config configuration attributes
15544      */
15545     initTarget: function(id, sGroup, config) {
15546
15547         // configuration attributes
15548         this.config = config || {};
15549
15550         // create a local reference to the drag and drop manager
15551         this.DDM = Roo.dd.DDM;
15552         // initialize the groups array
15553         this.groups = {};
15554
15555         // assume that we have an element reference instead of an id if the
15556         // parameter is not a string
15557         if (typeof id !== "string") {
15558             id = Roo.id(id);
15559         }
15560
15561         // set the id
15562         this.id = id;
15563
15564         // add to an interaction group
15565         this.addToGroup((sGroup) ? sGroup : "default");
15566
15567         // We don't want to register this as the handle with the manager
15568         // so we just set the id rather than calling the setter.
15569         this.handleElId = id;
15570
15571         // the linked element is the element that gets dragged by default
15572         this.setDragElId(id);
15573
15574         // by default, clicked anchors will not start drag operations.
15575         this.invalidHandleTypes = { A: "A" };
15576         this.invalidHandleIds = {};
15577         this.invalidHandleClasses = [];
15578
15579         this.applyConfig();
15580
15581         this.handleOnAvailable();
15582     },
15583
15584     /**
15585      * Applies the configuration parameters that were passed into the constructor.
15586      * This is supposed to happen at each level through the inheritance chain.  So
15587      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15588      * DragDrop in order to get all of the parameters that are available in
15589      * each object.
15590      * @method applyConfig
15591      */
15592     applyConfig: function() {
15593
15594         // configurable properties:
15595         //    padding, isTarget, maintainOffset, primaryButtonOnly
15596         this.padding           = this.config.padding || [0, 0, 0, 0];
15597         this.isTarget          = (this.config.isTarget !== false);
15598         this.maintainOffset    = (this.config.maintainOffset);
15599         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15600
15601     },
15602
15603     /**
15604      * Executed when the linked element is available
15605      * @method handleOnAvailable
15606      * @private
15607      */
15608     handleOnAvailable: function() {
15609         this.available = true;
15610         this.resetConstraints();
15611         this.onAvailable();
15612     },
15613
15614      /**
15615      * Configures the padding for the target zone in px.  Effectively expands
15616      * (or reduces) the virtual object size for targeting calculations.
15617      * Supports css-style shorthand; if only one parameter is passed, all sides
15618      * will have that padding, and if only two are passed, the top and bottom
15619      * will have the first param, the left and right the second.
15620      * @method setPadding
15621      * @param {int} iTop    Top pad
15622      * @param {int} iRight  Right pad
15623      * @param {int} iBot    Bot pad
15624      * @param {int} iLeft   Left pad
15625      */
15626     setPadding: function(iTop, iRight, iBot, iLeft) {
15627         // this.padding = [iLeft, iRight, iTop, iBot];
15628         if (!iRight && 0 !== iRight) {
15629             this.padding = [iTop, iTop, iTop, iTop];
15630         } else if (!iBot && 0 !== iBot) {
15631             this.padding = [iTop, iRight, iTop, iRight];
15632         } else {
15633             this.padding = [iTop, iRight, iBot, iLeft];
15634         }
15635     },
15636
15637     /**
15638      * Stores the initial placement of the linked element.
15639      * @method setInitialPosition
15640      * @param {int} diffX   the X offset, default 0
15641      * @param {int} diffY   the Y offset, default 0
15642      */
15643     setInitPosition: function(diffX, diffY) {
15644         var el = this.getEl();
15645
15646         if (!this.DDM.verifyEl(el)) {
15647             return;
15648         }
15649
15650         var dx = diffX || 0;
15651         var dy = diffY || 0;
15652
15653         var p = Dom.getXY( el );
15654
15655         this.initPageX = p[0] - dx;
15656         this.initPageY = p[1] - dy;
15657
15658         this.lastPageX = p[0];
15659         this.lastPageY = p[1];
15660
15661
15662         this.setStartPosition(p);
15663     },
15664
15665     /**
15666      * Sets the start position of the element.  This is set when the obj
15667      * is initialized, the reset when a drag is started.
15668      * @method setStartPosition
15669      * @param pos current position (from previous lookup)
15670      * @private
15671      */
15672     setStartPosition: function(pos) {
15673         var p = pos || Dom.getXY( this.getEl() );
15674         this.deltaSetXY = null;
15675
15676         this.startPageX = p[0];
15677         this.startPageY = p[1];
15678     },
15679
15680     /**
15681      * Add this instance to a group of related drag/drop objects.  All
15682      * instances belong to at least one group, and can belong to as many
15683      * groups as needed.
15684      * @method addToGroup
15685      * @param sGroup {string} the name of the group
15686      */
15687     addToGroup: function(sGroup) {
15688         this.groups[sGroup] = true;
15689         this.DDM.regDragDrop(this, sGroup);
15690     },
15691
15692     /**
15693      * Remove's this instance from the supplied interaction group
15694      * @method removeFromGroup
15695      * @param {string}  sGroup  The group to drop
15696      */
15697     removeFromGroup: function(sGroup) {
15698         if (this.groups[sGroup]) {
15699             delete this.groups[sGroup];
15700         }
15701
15702         this.DDM.removeDDFromGroup(this, sGroup);
15703     },
15704
15705     /**
15706      * Allows you to specify that an element other than the linked element
15707      * will be moved with the cursor during a drag
15708      * @method setDragElId
15709      * @param id {string} the id of the element that will be used to initiate the drag
15710      */
15711     setDragElId: function(id) {
15712         this.dragElId = id;
15713     },
15714
15715     /**
15716      * Allows you to specify a child of the linked element that should be
15717      * used to initiate the drag operation.  An example of this would be if
15718      * you have a content div with text and links.  Clicking anywhere in the
15719      * content area would normally start the drag operation.  Use this method
15720      * to specify that an element inside of the content div is the element
15721      * that starts the drag operation.
15722      * @method setHandleElId
15723      * @param id {string} the id of the element that will be used to
15724      * initiate the drag.
15725      */
15726     setHandleElId: function(id) {
15727         if (typeof id !== "string") {
15728             id = Roo.id(id);
15729         }
15730         this.handleElId = id;
15731         this.DDM.regHandle(this.id, id);
15732     },
15733
15734     /**
15735      * Allows you to set an element outside of the linked element as a drag
15736      * handle
15737      * @method setOuterHandleElId
15738      * @param id the id of the element that will be used to initiate the drag
15739      */
15740     setOuterHandleElId: function(id) {
15741         if (typeof id !== "string") {
15742             id = Roo.id(id);
15743         }
15744         Event.on(id, "mousedown",
15745                 this.handleMouseDown, this);
15746         this.setHandleElId(id);
15747
15748         this.hasOuterHandles = true;
15749     },
15750
15751     /**
15752      * Remove all drag and drop hooks for this element
15753      * @method unreg
15754      */
15755     unreg: function() {
15756         Event.un(this.id, "mousedown",
15757                 this.handleMouseDown);
15758         this._domRef = null;
15759         this.DDM._remove(this);
15760     },
15761
15762     destroy : function(){
15763         this.unreg();
15764     },
15765
15766     /**
15767      * Returns true if this instance is locked, or the drag drop mgr is locked
15768      * (meaning that all drag/drop is disabled on the page.)
15769      * @method isLocked
15770      * @return {boolean} true if this obj or all drag/drop is locked, else
15771      * false
15772      */
15773     isLocked: function() {
15774         return (this.DDM.isLocked() || this.locked);
15775     },
15776
15777     /**
15778      * Fired when this object is clicked
15779      * @method handleMouseDown
15780      * @param {Event} e
15781      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15782      * @private
15783      */
15784     handleMouseDown: function(e, oDD){
15785         if (this.primaryButtonOnly && e.button != 0) {
15786             return;
15787         }
15788
15789         if (this.isLocked()) {
15790             return;
15791         }
15792
15793         this.DDM.refreshCache(this.groups);
15794
15795         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15796         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15797         } else {
15798             if (this.clickValidator(e)) {
15799
15800                 // set the initial element position
15801                 this.setStartPosition();
15802
15803
15804                 this.b4MouseDown(e);
15805                 this.onMouseDown(e);
15806
15807                 this.DDM.handleMouseDown(e, this);
15808
15809                 this.DDM.stopEvent(e);
15810             } else {
15811
15812
15813             }
15814         }
15815     },
15816
15817     clickValidator: function(e) {
15818         var target = e.getTarget();
15819         return ( this.isValidHandleChild(target) &&
15820                     (this.id == this.handleElId ||
15821                         this.DDM.handleWasClicked(target, this.id)) );
15822     },
15823
15824     /**
15825      * Allows you to specify a tag name that should not start a drag operation
15826      * when clicked.  This is designed to facilitate embedding links within a
15827      * drag handle that do something other than start the drag.
15828      * @method addInvalidHandleType
15829      * @param {string} tagName the type of element to exclude
15830      */
15831     addInvalidHandleType: function(tagName) {
15832         var type = tagName.toUpperCase();
15833         this.invalidHandleTypes[type] = type;
15834     },
15835
15836     /**
15837      * Lets you to specify an element id for a child of a drag handle
15838      * that should not initiate a drag
15839      * @method addInvalidHandleId
15840      * @param {string} id the element id of the element you wish to ignore
15841      */
15842     addInvalidHandleId: function(id) {
15843         if (typeof id !== "string") {
15844             id = Roo.id(id);
15845         }
15846         this.invalidHandleIds[id] = id;
15847     },
15848
15849     /**
15850      * Lets you specify a css class of elements that will not initiate a drag
15851      * @method addInvalidHandleClass
15852      * @param {string} cssClass the class of the elements you wish to ignore
15853      */
15854     addInvalidHandleClass: function(cssClass) {
15855         this.invalidHandleClasses.push(cssClass);
15856     },
15857
15858     /**
15859      * Unsets an excluded tag name set by addInvalidHandleType
15860      * @method removeInvalidHandleType
15861      * @param {string} tagName the type of element to unexclude
15862      */
15863     removeInvalidHandleType: function(tagName) {
15864         var type = tagName.toUpperCase();
15865         // this.invalidHandleTypes[type] = null;
15866         delete this.invalidHandleTypes[type];
15867     },
15868
15869     /**
15870      * Unsets an invalid handle id
15871      * @method removeInvalidHandleId
15872      * @param {string} id the id of the element to re-enable
15873      */
15874     removeInvalidHandleId: function(id) {
15875         if (typeof id !== "string") {
15876             id = Roo.id(id);
15877         }
15878         delete this.invalidHandleIds[id];
15879     },
15880
15881     /**
15882      * Unsets an invalid css class
15883      * @method removeInvalidHandleClass
15884      * @param {string} cssClass the class of the element(s) you wish to
15885      * re-enable
15886      */
15887     removeInvalidHandleClass: function(cssClass) {
15888         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15889             if (this.invalidHandleClasses[i] == cssClass) {
15890                 delete this.invalidHandleClasses[i];
15891             }
15892         }
15893     },
15894
15895     /**
15896      * Checks the tag exclusion list to see if this click should be ignored
15897      * @method isValidHandleChild
15898      * @param {HTMLElement} node the HTMLElement to evaluate
15899      * @return {boolean} true if this is a valid tag type, false if not
15900      */
15901     isValidHandleChild: function(node) {
15902
15903         var valid = true;
15904         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15905         var nodeName;
15906         try {
15907             nodeName = node.nodeName.toUpperCase();
15908         } catch(e) {
15909             nodeName = node.nodeName;
15910         }
15911         valid = valid && !this.invalidHandleTypes[nodeName];
15912         valid = valid && !this.invalidHandleIds[node.id];
15913
15914         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15915             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15916         }
15917
15918
15919         return valid;
15920
15921     },
15922
15923     /**
15924      * Create the array of horizontal tick marks if an interval was specified
15925      * in setXConstraint().
15926      * @method setXTicks
15927      * @private
15928      */
15929     setXTicks: function(iStartX, iTickSize) {
15930         this.xTicks = [];
15931         this.xTickSize = iTickSize;
15932
15933         var tickMap = {};
15934
15935         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15936             if (!tickMap[i]) {
15937                 this.xTicks[this.xTicks.length] = i;
15938                 tickMap[i] = true;
15939             }
15940         }
15941
15942         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15943             if (!tickMap[i]) {
15944                 this.xTicks[this.xTicks.length] = i;
15945                 tickMap[i] = true;
15946             }
15947         }
15948
15949         this.xTicks.sort(this.DDM.numericSort) ;
15950     },
15951
15952     /**
15953      * Create the array of vertical tick marks if an interval was specified in
15954      * setYConstraint().
15955      * @method setYTicks
15956      * @private
15957      */
15958     setYTicks: function(iStartY, iTickSize) {
15959         this.yTicks = [];
15960         this.yTickSize = iTickSize;
15961
15962         var tickMap = {};
15963
15964         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15965             if (!tickMap[i]) {
15966                 this.yTicks[this.yTicks.length] = i;
15967                 tickMap[i] = true;
15968             }
15969         }
15970
15971         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15972             if (!tickMap[i]) {
15973                 this.yTicks[this.yTicks.length] = i;
15974                 tickMap[i] = true;
15975             }
15976         }
15977
15978         this.yTicks.sort(this.DDM.numericSort) ;
15979     },
15980
15981     /**
15982      * By default, the element can be dragged any place on the screen.  Use
15983      * this method to limit the horizontal travel of the element.  Pass in
15984      * 0,0 for the parameters if you want to lock the drag to the y axis.
15985      * @method setXConstraint
15986      * @param {int} iLeft the number of pixels the element can move to the left
15987      * @param {int} iRight the number of pixels the element can move to the
15988      * right
15989      * @param {int} iTickSize optional parameter for specifying that the
15990      * element
15991      * should move iTickSize pixels at a time.
15992      */
15993     setXConstraint: function(iLeft, iRight, iTickSize) {
15994         this.leftConstraint = iLeft;
15995         this.rightConstraint = iRight;
15996
15997         this.minX = this.initPageX - iLeft;
15998         this.maxX = this.initPageX + iRight;
15999         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
16000
16001         this.constrainX = true;
16002     },
16003
16004     /**
16005      * Clears any constraints applied to this instance.  Also clears ticks
16006      * since they can't exist independent of a constraint at this time.
16007      * @method clearConstraints
16008      */
16009     clearConstraints: function() {
16010         this.constrainX = false;
16011         this.constrainY = false;
16012         this.clearTicks();
16013     },
16014
16015     /**
16016      * Clears any tick interval defined for this instance
16017      * @method clearTicks
16018      */
16019     clearTicks: function() {
16020         this.xTicks = null;
16021         this.yTicks = null;
16022         this.xTickSize = 0;
16023         this.yTickSize = 0;
16024     },
16025
16026     /**
16027      * By default, the element can be dragged any place on the screen.  Set
16028      * this to limit the vertical travel of the element.  Pass in 0,0 for the
16029      * parameters if you want to lock the drag to the x axis.
16030      * @method setYConstraint
16031      * @param {int} iUp the number of pixels the element can move up
16032      * @param {int} iDown the number of pixels the element can move down
16033      * @param {int} iTickSize optional parameter for specifying that the
16034      * element should move iTickSize pixels at a time.
16035      */
16036     setYConstraint: function(iUp, iDown, iTickSize) {
16037         this.topConstraint = iUp;
16038         this.bottomConstraint = iDown;
16039
16040         this.minY = this.initPageY - iUp;
16041         this.maxY = this.initPageY + iDown;
16042         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
16043
16044         this.constrainY = true;
16045
16046     },
16047
16048     /**
16049      * resetConstraints must be called if you manually reposition a dd element.
16050      * @method resetConstraints
16051      * @param {boolean} maintainOffset
16052      */
16053     resetConstraints: function() {
16054
16055
16056         // Maintain offsets if necessary
16057         if (this.initPageX || this.initPageX === 0) {
16058             // figure out how much this thing has moved
16059             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
16060             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
16061
16062             this.setInitPosition(dx, dy);
16063
16064         // This is the first time we have detected the element's position
16065         } else {
16066             this.setInitPosition();
16067         }
16068
16069         if (this.constrainX) {
16070             this.setXConstraint( this.leftConstraint,
16071                                  this.rightConstraint,
16072                                  this.xTickSize        );
16073         }
16074
16075         if (this.constrainY) {
16076             this.setYConstraint( this.topConstraint,
16077                                  this.bottomConstraint,
16078                                  this.yTickSize         );
16079         }
16080     },
16081
16082     /**
16083      * Normally the drag element is moved pixel by pixel, but we can specify
16084      * that it move a number of pixels at a time.  This method resolves the
16085      * location when we have it set up like this.
16086      * @method getTick
16087      * @param {int} val where we want to place the object
16088      * @param {int[]} tickArray sorted array of valid points
16089      * @return {int} the closest tick
16090      * @private
16091      */
16092     getTick: function(val, tickArray) {
16093
16094         if (!tickArray) {
16095             // If tick interval is not defined, it is effectively 1 pixel,
16096             // so we return the value passed to us.
16097             return val;
16098         } else if (tickArray[0] >= val) {
16099             // The value is lower than the first tick, so we return the first
16100             // tick.
16101             return tickArray[0];
16102         } else {
16103             for (var i=0, len=tickArray.length; i<len; ++i) {
16104                 var next = i + 1;
16105                 if (tickArray[next] && tickArray[next] >= val) {
16106                     var diff1 = val - tickArray[i];
16107                     var diff2 = tickArray[next] - val;
16108                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
16109                 }
16110             }
16111
16112             // The value is larger than the last tick, so we return the last
16113             // tick.
16114             return tickArray[tickArray.length - 1];
16115         }
16116     },
16117
16118     /**
16119      * toString method
16120      * @method toString
16121      * @return {string} string representation of the dd obj
16122      */
16123     toString: function() {
16124         return ("DragDrop " + this.id);
16125     }
16126
16127 });
16128
16129 })();
16130 /*
16131  * Based on:
16132  * Ext JS Library 1.1.1
16133  * Copyright(c) 2006-2007, Ext JS, LLC.
16134  *
16135  * Originally Released Under LGPL - original licence link has changed is not relivant.
16136  *
16137  * Fork - LGPL
16138  * <script type="text/javascript">
16139  */
16140
16141
16142 /**
16143  * The drag and drop utility provides a framework for building drag and drop
16144  * applications.  In addition to enabling drag and drop for specific elements,
16145  * the drag and drop elements are tracked by the manager class, and the
16146  * interactions between the various elements are tracked during the drag and
16147  * the implementing code is notified about these important moments.
16148  */
16149
16150 // Only load the library once.  Rewriting the manager class would orphan
16151 // existing drag and drop instances.
16152 if (!Roo.dd.DragDropMgr) {
16153
16154 /**
16155  * @class Roo.dd.DragDropMgr
16156  * DragDropMgr is a singleton that tracks the element interaction for
16157  * all DragDrop items in the window.  Generally, you will not call
16158  * this class directly, but it does have helper methods that could
16159  * be useful in your DragDrop implementations.
16160  * @singleton
16161  */
16162 Roo.dd.DragDropMgr = function() {
16163
16164     var Event = Roo.EventManager;
16165
16166     return {
16167
16168         /**
16169          * Two dimensional Array of registered DragDrop objects.  The first
16170          * dimension is the DragDrop item group, the second the DragDrop
16171          * object.
16172          * @property ids
16173          * @type {string: string}
16174          * @private
16175          * @static
16176          */
16177         ids: {},
16178
16179         /**
16180          * Array of element ids defined as drag handles.  Used to determine
16181          * if the element that generated the mousedown event is actually the
16182          * handle and not the html element itself.
16183          * @property handleIds
16184          * @type {string: string}
16185          * @private
16186          * @static
16187          */
16188         handleIds: {},
16189
16190         /**
16191          * the DragDrop object that is currently being dragged
16192          * @property dragCurrent
16193          * @type DragDrop
16194          * @private
16195          * @static
16196          **/
16197         dragCurrent: null,
16198
16199         /**
16200          * the DragDrop object(s) that are being hovered over
16201          * @property dragOvers
16202          * @type Array
16203          * @private
16204          * @static
16205          */
16206         dragOvers: {},
16207
16208         /**
16209          * the X distance between the cursor and the object being dragged
16210          * @property deltaX
16211          * @type int
16212          * @private
16213          * @static
16214          */
16215         deltaX: 0,
16216
16217         /**
16218          * the Y distance between the cursor and the object being dragged
16219          * @property deltaY
16220          * @type int
16221          * @private
16222          * @static
16223          */
16224         deltaY: 0,
16225
16226         /**
16227          * Flag to determine if we should prevent the default behavior of the
16228          * events we define. By default this is true, but this can be set to
16229          * false if you need the default behavior (not recommended)
16230          * @property preventDefault
16231          * @type boolean
16232          * @static
16233          */
16234         preventDefault: true,
16235
16236         /**
16237          * Flag to determine if we should stop the propagation of the events
16238          * we generate. This is true by default but you may want to set it to
16239          * false if the html element contains other features that require the
16240          * mouse click.
16241          * @property stopPropagation
16242          * @type boolean
16243          * @static
16244          */
16245         stopPropagation: true,
16246
16247         /**
16248          * Internal flag that is set to true when drag and drop has been
16249          * intialized
16250          * @property initialized
16251          * @private
16252          * @static
16253          */
16254         initalized: false,
16255
16256         /**
16257          * All drag and drop can be disabled.
16258          * @property locked
16259          * @private
16260          * @static
16261          */
16262         locked: false,
16263
16264         /**
16265          * Called the first time an element is registered.
16266          * @method init
16267          * @private
16268          * @static
16269          */
16270         init: function() {
16271             this.initialized = true;
16272         },
16273
16274         /**
16275          * In point mode, drag and drop interaction is defined by the
16276          * location of the cursor during the drag/drop
16277          * @property POINT
16278          * @type int
16279          * @static
16280          */
16281         POINT: 0,
16282
16283         /**
16284          * In intersect mode, drag and drop interactio nis defined by the
16285          * overlap of two or more drag and drop objects.
16286          * @property INTERSECT
16287          * @type int
16288          * @static
16289          */
16290         INTERSECT: 1,
16291
16292         /**
16293          * The current drag and drop mode.  Default: POINT
16294          * @property mode
16295          * @type int
16296          * @static
16297          */
16298         mode: 0,
16299
16300         /**
16301          * Runs method on all drag and drop objects
16302          * @method _execOnAll
16303          * @private
16304          * @static
16305          */
16306         _execOnAll: function(sMethod, args) {
16307             for (var i in this.ids) {
16308                 for (var j in this.ids[i]) {
16309                     var oDD = this.ids[i][j];
16310                     if (! this.isTypeOfDD(oDD)) {
16311                         continue;
16312                     }
16313                     oDD[sMethod].apply(oDD, args);
16314                 }
16315             }
16316         },
16317
16318         /**
16319          * Drag and drop initialization.  Sets up the global event handlers
16320          * @method _onLoad
16321          * @private
16322          * @static
16323          */
16324         _onLoad: function() {
16325
16326             this.init();
16327
16328
16329             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
16330             Event.on(document, "mousemove", this.handleMouseMove, this, true);
16331             Event.on(window,   "unload",    this._onUnload, this, true);
16332             Event.on(window,   "resize",    this._onResize, this, true);
16333             // Event.on(window,   "mouseout",    this._test);
16334
16335         },
16336
16337         /**
16338          * Reset constraints on all drag and drop objs
16339          * @method _onResize
16340          * @private
16341          * @static
16342          */
16343         _onResize: function(e) {
16344             this._execOnAll("resetConstraints", []);
16345         },
16346
16347         /**
16348          * Lock all drag and drop functionality
16349          * @method lock
16350          * @static
16351          */
16352         lock: function() { this.locked = true; },
16353
16354         /**
16355          * Unlock all drag and drop functionality
16356          * @method unlock
16357          * @static
16358          */
16359         unlock: function() { this.locked = false; },
16360
16361         /**
16362          * Is drag and drop locked?
16363          * @method isLocked
16364          * @return {boolean} True if drag and drop is locked, false otherwise.
16365          * @static
16366          */
16367         isLocked: function() { return this.locked; },
16368
16369         /**
16370          * Location cache that is set for all drag drop objects when a drag is
16371          * initiated, cleared when the drag is finished.
16372          * @property locationCache
16373          * @private
16374          * @static
16375          */
16376         locationCache: {},
16377
16378         /**
16379          * Set useCache to false if you want to force object the lookup of each
16380          * drag and drop linked element constantly during a drag.
16381          * @property useCache
16382          * @type boolean
16383          * @static
16384          */
16385         useCache: true,
16386
16387         /**
16388          * The number of pixels that the mouse needs to move after the
16389          * mousedown before the drag is initiated.  Default=3;
16390          * @property clickPixelThresh
16391          * @type int
16392          * @static
16393          */
16394         clickPixelThresh: 3,
16395
16396         /**
16397          * The number of milliseconds after the mousedown event to initiate the
16398          * drag if we don't get a mouseup event. Default=1000
16399          * @property clickTimeThresh
16400          * @type int
16401          * @static
16402          */
16403         clickTimeThresh: 350,
16404
16405         /**
16406          * Flag that indicates that either the drag pixel threshold or the
16407          * mousdown time threshold has been met
16408          * @property dragThreshMet
16409          * @type boolean
16410          * @private
16411          * @static
16412          */
16413         dragThreshMet: false,
16414
16415         /**
16416          * Timeout used for the click time threshold
16417          * @property clickTimeout
16418          * @type Object
16419          * @private
16420          * @static
16421          */
16422         clickTimeout: null,
16423
16424         /**
16425          * The X position of the mousedown event stored for later use when a
16426          * drag threshold is met.
16427          * @property startX
16428          * @type int
16429          * @private
16430          * @static
16431          */
16432         startX: 0,
16433
16434         /**
16435          * The Y position of the mousedown event stored for later use when a
16436          * drag threshold is met.
16437          * @property startY
16438          * @type int
16439          * @private
16440          * @static
16441          */
16442         startY: 0,
16443
16444         /**
16445          * Each DragDrop instance must be registered with the DragDropMgr.
16446          * This is executed in DragDrop.init()
16447          * @method regDragDrop
16448          * @param {DragDrop} oDD the DragDrop object to register
16449          * @param {String} sGroup the name of the group this element belongs to
16450          * @static
16451          */
16452         regDragDrop: function(oDD, sGroup) {
16453             if (!this.initialized) { this.init(); }
16454
16455             if (!this.ids[sGroup]) {
16456                 this.ids[sGroup] = {};
16457             }
16458             this.ids[sGroup][oDD.id] = oDD;
16459         },
16460
16461         /**
16462          * Removes the supplied dd instance from the supplied group. Executed
16463          * by DragDrop.removeFromGroup, so don't call this function directly.
16464          * @method removeDDFromGroup
16465          * @private
16466          * @static
16467          */
16468         removeDDFromGroup: function(oDD, sGroup) {
16469             if (!this.ids[sGroup]) {
16470                 this.ids[sGroup] = {};
16471             }
16472
16473             var obj = this.ids[sGroup];
16474             if (obj && obj[oDD.id]) {
16475                 delete obj[oDD.id];
16476             }
16477         },
16478
16479         /**
16480          * Unregisters a drag and drop item.  This is executed in
16481          * DragDrop.unreg, use that method instead of calling this directly.
16482          * @method _remove
16483          * @private
16484          * @static
16485          */
16486         _remove: function(oDD) {
16487             for (var g in oDD.groups) {
16488                 if (g && this.ids[g][oDD.id]) {
16489                     delete this.ids[g][oDD.id];
16490                 }
16491             }
16492             delete this.handleIds[oDD.id];
16493         },
16494
16495         /**
16496          * Each DragDrop handle element must be registered.  This is done
16497          * automatically when executing DragDrop.setHandleElId()
16498          * @method regHandle
16499          * @param {String} sDDId the DragDrop id this element is a handle for
16500          * @param {String} sHandleId the id of the element that is the drag
16501          * handle
16502          * @static
16503          */
16504         regHandle: function(sDDId, sHandleId) {
16505             if (!this.handleIds[sDDId]) {
16506                 this.handleIds[sDDId] = {};
16507             }
16508             this.handleIds[sDDId][sHandleId] = sHandleId;
16509         },
16510
16511         /**
16512          * Utility function to determine if a given element has been
16513          * registered as a drag drop item.
16514          * @method isDragDrop
16515          * @param {String} id the element id to check
16516          * @return {boolean} true if this element is a DragDrop item,
16517          * false otherwise
16518          * @static
16519          */
16520         isDragDrop: function(id) {
16521             return ( this.getDDById(id) ) ? true : false;
16522         },
16523
16524         /**
16525          * Returns the drag and drop instances that are in all groups the
16526          * passed in instance belongs to.
16527          * @method getRelated
16528          * @param {DragDrop} p_oDD the obj to get related data for
16529          * @param {boolean} bTargetsOnly if true, only return targetable objs
16530          * @return {DragDrop[]} the related instances
16531          * @static
16532          */
16533         getRelated: function(p_oDD, bTargetsOnly) {
16534             var oDDs = [];
16535             for (var i in p_oDD.groups) {
16536                 for (j in this.ids[i]) {
16537                     var dd = this.ids[i][j];
16538                     if (! this.isTypeOfDD(dd)) {
16539                         continue;
16540                     }
16541                     if (!bTargetsOnly || dd.isTarget) {
16542                         oDDs[oDDs.length] = dd;
16543                     }
16544                 }
16545             }
16546
16547             return oDDs;
16548         },
16549
16550         /**
16551          * Returns true if the specified dd target is a legal target for
16552          * the specifice drag obj
16553          * @method isLegalTarget
16554          * @param {DragDrop} the drag obj
16555          * @param {DragDrop} the target
16556          * @return {boolean} true if the target is a legal target for the
16557          * dd obj
16558          * @static
16559          */
16560         isLegalTarget: function (oDD, oTargetDD) {
16561             var targets = this.getRelated(oDD, true);
16562             for (var i=0, len=targets.length;i<len;++i) {
16563                 if (targets[i].id == oTargetDD.id) {
16564                     return true;
16565                 }
16566             }
16567
16568             return false;
16569         },
16570
16571         /**
16572          * My goal is to be able to transparently determine if an object is
16573          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16574          * returns "object", oDD.constructor.toString() always returns
16575          * "DragDrop" and not the name of the subclass.  So for now it just
16576          * evaluates a well-known variable in DragDrop.
16577          * @method isTypeOfDD
16578          * @param {Object} the object to evaluate
16579          * @return {boolean} true if typeof oDD = DragDrop
16580          * @static
16581          */
16582         isTypeOfDD: function (oDD) {
16583             return (oDD && oDD.__ygDragDrop);
16584         },
16585
16586         /**
16587          * Utility function to determine if a given element has been
16588          * registered as a drag drop handle for the given Drag Drop object.
16589          * @method isHandle
16590          * @param {String} id the element id to check
16591          * @return {boolean} true if this element is a DragDrop handle, false
16592          * otherwise
16593          * @static
16594          */
16595         isHandle: function(sDDId, sHandleId) {
16596             return ( this.handleIds[sDDId] &&
16597                             this.handleIds[sDDId][sHandleId] );
16598         },
16599
16600         /**
16601          * Returns the DragDrop instance for a given id
16602          * @method getDDById
16603          * @param {String} id the id of the DragDrop object
16604          * @return {DragDrop} the drag drop object, null if it is not found
16605          * @static
16606          */
16607         getDDById: function(id) {
16608             for (var i in this.ids) {
16609                 if (this.ids[i][id]) {
16610                     return this.ids[i][id];
16611                 }
16612             }
16613             return null;
16614         },
16615
16616         /**
16617          * Fired after a registered DragDrop object gets the mousedown event.
16618          * Sets up the events required to track the object being dragged
16619          * @method handleMouseDown
16620          * @param {Event} e the event
16621          * @param oDD the DragDrop object being dragged
16622          * @private
16623          * @static
16624          */
16625         handleMouseDown: function(e, oDD) {
16626             if(Roo.QuickTips){
16627                 Roo.QuickTips.disable();
16628             }
16629             this.currentTarget = e.getTarget();
16630
16631             this.dragCurrent = oDD;
16632
16633             var el = oDD.getEl();
16634
16635             // track start position
16636             this.startX = e.getPageX();
16637             this.startY = e.getPageY();
16638
16639             this.deltaX = this.startX - el.offsetLeft;
16640             this.deltaY = this.startY - el.offsetTop;
16641
16642             this.dragThreshMet = false;
16643
16644             this.clickTimeout = setTimeout(
16645                     function() {
16646                         var DDM = Roo.dd.DDM;
16647                         DDM.startDrag(DDM.startX, DDM.startY);
16648                     },
16649                     this.clickTimeThresh );
16650         },
16651
16652         /**
16653          * Fired when either the drag pixel threshol or the mousedown hold
16654          * time threshold has been met.
16655          * @method startDrag
16656          * @param x {int} the X position of the original mousedown
16657          * @param y {int} the Y position of the original mousedown
16658          * @static
16659          */
16660         startDrag: function(x, y) {
16661             clearTimeout(this.clickTimeout);
16662             if (this.dragCurrent) {
16663                 this.dragCurrent.b4StartDrag(x, y);
16664                 this.dragCurrent.startDrag(x, y);
16665             }
16666             this.dragThreshMet = true;
16667         },
16668
16669         /**
16670          * Internal function to handle the mouseup event.  Will be invoked
16671          * from the context of the document.
16672          * @method handleMouseUp
16673          * @param {Event} e the event
16674          * @private
16675          * @static
16676          */
16677         handleMouseUp: function(e) {
16678
16679             if(Roo.QuickTips){
16680                 Roo.QuickTips.enable();
16681             }
16682             if (! this.dragCurrent) {
16683                 return;
16684             }
16685
16686             clearTimeout(this.clickTimeout);
16687
16688             if (this.dragThreshMet) {
16689                 this.fireEvents(e, true);
16690             } else {
16691             }
16692
16693             this.stopDrag(e);
16694
16695             this.stopEvent(e);
16696         },
16697
16698         /**
16699          * Utility to stop event propagation and event default, if these
16700          * features are turned on.
16701          * @method stopEvent
16702          * @param {Event} e the event as returned by this.getEvent()
16703          * @static
16704          */
16705         stopEvent: function(e){
16706             if(this.stopPropagation) {
16707                 e.stopPropagation();
16708             }
16709
16710             if (this.preventDefault) {
16711                 e.preventDefault();
16712             }
16713         },
16714
16715         /**
16716          * Internal function to clean up event handlers after the drag
16717          * operation is complete
16718          * @method stopDrag
16719          * @param {Event} e the event
16720          * @private
16721          * @static
16722          */
16723         stopDrag: function(e) {
16724             // Fire the drag end event for the item that was dragged
16725             if (this.dragCurrent) {
16726                 if (this.dragThreshMet) {
16727                     this.dragCurrent.b4EndDrag(e);
16728                     this.dragCurrent.endDrag(e);
16729                 }
16730
16731                 this.dragCurrent.onMouseUp(e);
16732             }
16733
16734             this.dragCurrent = null;
16735             this.dragOvers = {};
16736         },
16737
16738         /**
16739          * Internal function to handle the mousemove event.  Will be invoked
16740          * from the context of the html element.
16741          *
16742          * @TODO figure out what we can do about mouse events lost when the
16743          * user drags objects beyond the window boundary.  Currently we can
16744          * detect this in internet explorer by verifying that the mouse is
16745          * down during the mousemove event.  Firefox doesn't give us the
16746          * button state on the mousemove event.
16747          * @method handleMouseMove
16748          * @param {Event} e the event
16749          * @private
16750          * @static
16751          */
16752         handleMouseMove: function(e) {
16753             if (! this.dragCurrent) {
16754                 return true;
16755             }
16756
16757             // var button = e.which || e.button;
16758
16759             // check for IE mouseup outside of page boundary
16760             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16761                 this.stopEvent(e);
16762                 return this.handleMouseUp(e);
16763             }
16764
16765             if (!this.dragThreshMet) {
16766                 var diffX = Math.abs(this.startX - e.getPageX());
16767                 var diffY = Math.abs(this.startY - e.getPageY());
16768                 if (diffX > this.clickPixelThresh ||
16769                             diffY > this.clickPixelThresh) {
16770                     this.startDrag(this.startX, this.startY);
16771                 }
16772             }
16773
16774             if (this.dragThreshMet) {
16775                 this.dragCurrent.b4Drag(e);
16776                 this.dragCurrent.onDrag(e);
16777                 if(!this.dragCurrent.moveOnly){
16778                     this.fireEvents(e, false);
16779                 }
16780             }
16781
16782             this.stopEvent(e);
16783
16784             return true;
16785         },
16786
16787         /**
16788          * Iterates over all of the DragDrop elements to find ones we are
16789          * hovering over or dropping on
16790          * @method fireEvents
16791          * @param {Event} e the event
16792          * @param {boolean} isDrop is this a drop op or a mouseover op?
16793          * @private
16794          * @static
16795          */
16796         fireEvents: function(e, isDrop) {
16797             var dc = this.dragCurrent;
16798
16799             // If the user did the mouse up outside of the window, we could
16800             // get here even though we have ended the drag.
16801             if (!dc || dc.isLocked()) {
16802                 return;
16803             }
16804
16805             var pt = e.getPoint();
16806
16807             // cache the previous dragOver array
16808             var oldOvers = [];
16809
16810             var outEvts   = [];
16811             var overEvts  = [];
16812             var dropEvts  = [];
16813             var enterEvts = [];
16814
16815             // Check to see if the object(s) we were hovering over is no longer
16816             // being hovered over so we can fire the onDragOut event
16817             for (var i in this.dragOvers) {
16818
16819                 var ddo = this.dragOvers[i];
16820
16821                 if (! this.isTypeOfDD(ddo)) {
16822                     continue;
16823                 }
16824
16825                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16826                     outEvts.push( ddo );
16827                 }
16828
16829                 oldOvers[i] = true;
16830                 delete this.dragOvers[i];
16831             }
16832
16833             for (var sGroup in dc.groups) {
16834
16835                 if ("string" != typeof sGroup) {
16836                     continue;
16837                 }
16838
16839                 for (i in this.ids[sGroup]) {
16840                     var oDD = this.ids[sGroup][i];
16841                     if (! this.isTypeOfDD(oDD)) {
16842                         continue;
16843                     }
16844
16845                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16846                         if (this.isOverTarget(pt, oDD, this.mode)) {
16847                             // look for drop interactions
16848                             if (isDrop) {
16849                                 dropEvts.push( oDD );
16850                             // look for drag enter and drag over interactions
16851                             } else {
16852
16853                                 // initial drag over: dragEnter fires
16854                                 if (!oldOvers[oDD.id]) {
16855                                     enterEvts.push( oDD );
16856                                 // subsequent drag overs: dragOver fires
16857                                 } else {
16858                                     overEvts.push( oDD );
16859                                 }
16860
16861                                 this.dragOvers[oDD.id] = oDD;
16862                             }
16863                         }
16864                     }
16865                 }
16866             }
16867
16868             if (this.mode) {
16869                 if (outEvts.length) {
16870                     dc.b4DragOut(e, outEvts);
16871                     dc.onDragOut(e, outEvts);
16872                 }
16873
16874                 if (enterEvts.length) {
16875                     dc.onDragEnter(e, enterEvts);
16876                 }
16877
16878                 if (overEvts.length) {
16879                     dc.b4DragOver(e, overEvts);
16880                     dc.onDragOver(e, overEvts);
16881                 }
16882
16883                 if (dropEvts.length) {
16884                     dc.b4DragDrop(e, dropEvts);
16885                     dc.onDragDrop(e, dropEvts);
16886                 }
16887
16888             } else {
16889                 // fire dragout events
16890                 var len = 0;
16891                 for (i=0, len=outEvts.length; i<len; ++i) {
16892                     dc.b4DragOut(e, outEvts[i].id);
16893                     dc.onDragOut(e, outEvts[i].id);
16894                 }
16895
16896                 // fire enter events
16897                 for (i=0,len=enterEvts.length; i<len; ++i) {
16898                     // dc.b4DragEnter(e, oDD.id);
16899                     dc.onDragEnter(e, enterEvts[i].id);
16900                 }
16901
16902                 // fire over events
16903                 for (i=0,len=overEvts.length; i<len; ++i) {
16904                     dc.b4DragOver(e, overEvts[i].id);
16905                     dc.onDragOver(e, overEvts[i].id);
16906                 }
16907
16908                 // fire drop events
16909                 for (i=0, len=dropEvts.length; i<len; ++i) {
16910                     dc.b4DragDrop(e, dropEvts[i].id);
16911                     dc.onDragDrop(e, dropEvts[i].id);
16912                 }
16913
16914             }
16915
16916             // notify about a drop that did not find a target
16917             if (isDrop && !dropEvts.length) {
16918                 dc.onInvalidDrop(e);
16919             }
16920
16921         },
16922
16923         /**
16924          * Helper function for getting the best match from the list of drag
16925          * and drop objects returned by the drag and drop events when we are
16926          * in INTERSECT mode.  It returns either the first object that the
16927          * cursor is over, or the object that has the greatest overlap with
16928          * the dragged element.
16929          * @method getBestMatch
16930          * @param  {DragDrop[]} dds The array of drag and drop objects
16931          * targeted
16932          * @return {DragDrop}       The best single match
16933          * @static
16934          */
16935         getBestMatch: function(dds) {
16936             var winner = null;
16937             // Return null if the input is not what we expect
16938             //if (!dds || !dds.length || dds.length == 0) {
16939                // winner = null;
16940             // If there is only one item, it wins
16941             //} else if (dds.length == 1) {
16942
16943             var len = dds.length;
16944
16945             if (len == 1) {
16946                 winner = dds[0];
16947             } else {
16948                 // Loop through the targeted items
16949                 for (var i=0; i<len; ++i) {
16950                     var dd = dds[i];
16951                     // If the cursor is over the object, it wins.  If the
16952                     // cursor is over multiple matches, the first one we come
16953                     // to wins.
16954                     if (dd.cursorIsOver) {
16955                         winner = dd;
16956                         break;
16957                     // Otherwise the object with the most overlap wins
16958                     } else {
16959                         if (!winner ||
16960                             winner.overlap.getArea() < dd.overlap.getArea()) {
16961                             winner = dd;
16962                         }
16963                     }
16964                 }
16965             }
16966
16967             return winner;
16968         },
16969
16970         /**
16971          * Refreshes the cache of the top-left and bottom-right points of the
16972          * drag and drop objects in the specified group(s).  This is in the
16973          * format that is stored in the drag and drop instance, so typical
16974          * usage is:
16975          * <code>
16976          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16977          * </code>
16978          * Alternatively:
16979          * <code>
16980          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16981          * </code>
16982          * @TODO this really should be an indexed array.  Alternatively this
16983          * method could accept both.
16984          * @method refreshCache
16985          * @param {Object} groups an associative array of groups to refresh
16986          * @static
16987          */
16988         refreshCache: function(groups) {
16989             for (var sGroup in groups) {
16990                 if ("string" != typeof sGroup) {
16991                     continue;
16992                 }
16993                 for (var i in this.ids[sGroup]) {
16994                     var oDD = this.ids[sGroup][i];
16995
16996                     if (this.isTypeOfDD(oDD)) {
16997                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16998                         var loc = this.getLocation(oDD);
16999                         if (loc) {
17000                             this.locationCache[oDD.id] = loc;
17001                         } else {
17002                             delete this.locationCache[oDD.id];
17003                             // this will unregister the drag and drop object if
17004                             // the element is not in a usable state
17005                             // oDD.unreg();
17006                         }
17007                     }
17008                 }
17009             }
17010         },
17011
17012         /**
17013          * This checks to make sure an element exists and is in the DOM.  The
17014          * main purpose is to handle cases where innerHTML is used to remove
17015          * drag and drop objects from the DOM.  IE provides an 'unspecified
17016          * error' when trying to access the offsetParent of such an element
17017          * @method verifyEl
17018          * @param {HTMLElement} el the element to check
17019          * @return {boolean} true if the element looks usable
17020          * @static
17021          */
17022         verifyEl: function(el) {
17023             if (el) {
17024                 var parent;
17025                 if(Roo.isIE){
17026                     try{
17027                         parent = el.offsetParent;
17028                     }catch(e){}
17029                 }else{
17030                     parent = el.offsetParent;
17031                 }
17032                 if (parent) {
17033                     return true;
17034                 }
17035             }
17036
17037             return false;
17038         },
17039
17040         /**
17041          * Returns a Region object containing the drag and drop element's position
17042          * and size, including the padding configured for it
17043          * @method getLocation
17044          * @param {DragDrop} oDD the drag and drop object to get the
17045          *                       location for
17046          * @return {Roo.lib.Region} a Region object representing the total area
17047          *                             the element occupies, including any padding
17048          *                             the instance is configured for.
17049          * @static
17050          */
17051         getLocation: function(oDD) {
17052             if (! this.isTypeOfDD(oDD)) {
17053                 return null;
17054             }
17055
17056             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
17057
17058             try {
17059                 pos= Roo.lib.Dom.getXY(el);
17060             } catch (e) { }
17061
17062             if (!pos) {
17063                 return null;
17064             }
17065
17066             x1 = pos[0];
17067             x2 = x1 + el.offsetWidth;
17068             y1 = pos[1];
17069             y2 = y1 + el.offsetHeight;
17070
17071             t = y1 - oDD.padding[0];
17072             r = x2 + oDD.padding[1];
17073             b = y2 + oDD.padding[2];
17074             l = x1 - oDD.padding[3];
17075
17076             return new Roo.lib.Region( t, r, b, l );
17077         },
17078
17079         /**
17080          * Checks the cursor location to see if it over the target
17081          * @method isOverTarget
17082          * @param {Roo.lib.Point} pt The point to evaluate
17083          * @param {DragDrop} oTarget the DragDrop object we are inspecting
17084          * @return {boolean} true if the mouse is over the target
17085          * @private
17086          * @static
17087          */
17088         isOverTarget: function(pt, oTarget, intersect) {
17089             // use cache if available
17090             var loc = this.locationCache[oTarget.id];
17091             if (!loc || !this.useCache) {
17092                 loc = this.getLocation(oTarget);
17093                 this.locationCache[oTarget.id] = loc;
17094
17095             }
17096
17097             if (!loc) {
17098                 return false;
17099             }
17100
17101             oTarget.cursorIsOver = loc.contains( pt );
17102
17103             // DragDrop is using this as a sanity check for the initial mousedown
17104             // in this case we are done.  In POINT mode, if the drag obj has no
17105             // contraints, we are also done. Otherwise we need to evaluate the
17106             // location of the target as related to the actual location of the
17107             // dragged element.
17108             var dc = this.dragCurrent;
17109             if (!dc || !dc.getTargetCoord ||
17110                     (!intersect && !dc.constrainX && !dc.constrainY)) {
17111                 return oTarget.cursorIsOver;
17112             }
17113
17114             oTarget.overlap = null;
17115
17116             // Get the current location of the drag element, this is the
17117             // location of the mouse event less the delta that represents
17118             // where the original mousedown happened on the element.  We
17119             // need to consider constraints and ticks as well.
17120             var pos = dc.getTargetCoord(pt.x, pt.y);
17121
17122             var el = dc.getDragEl();
17123             var curRegion = new Roo.lib.Region( pos.y,
17124                                                    pos.x + el.offsetWidth,
17125                                                    pos.y + el.offsetHeight,
17126                                                    pos.x );
17127
17128             var overlap = curRegion.intersect(loc);
17129
17130             if (overlap) {
17131                 oTarget.overlap = overlap;
17132                 return (intersect) ? true : oTarget.cursorIsOver;
17133             } else {
17134                 return false;
17135             }
17136         },
17137
17138         /**
17139          * unload event handler
17140          * @method _onUnload
17141          * @private
17142          * @static
17143          */
17144         _onUnload: function(e, me) {
17145             Roo.dd.DragDropMgr.unregAll();
17146         },
17147
17148         /**
17149          * Cleans up the drag and drop events and objects.
17150          * @method unregAll
17151          * @private
17152          * @static
17153          */
17154         unregAll: function() {
17155
17156             if (this.dragCurrent) {
17157                 this.stopDrag();
17158                 this.dragCurrent = null;
17159             }
17160
17161             this._execOnAll("unreg", []);
17162
17163             for (i in this.elementCache) {
17164                 delete this.elementCache[i];
17165             }
17166
17167             this.elementCache = {};
17168             this.ids = {};
17169         },
17170
17171         /**
17172          * A cache of DOM elements
17173          * @property elementCache
17174          * @private
17175          * @static
17176          */
17177         elementCache: {},
17178
17179         /**
17180          * Get the wrapper for the DOM element specified
17181          * @method getElWrapper
17182          * @param {String} id the id of the element to get
17183          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
17184          * @private
17185          * @deprecated This wrapper isn't that useful
17186          * @static
17187          */
17188         getElWrapper: function(id) {
17189             var oWrapper = this.elementCache[id];
17190             if (!oWrapper || !oWrapper.el) {
17191                 oWrapper = this.elementCache[id] =
17192                     new this.ElementWrapper(Roo.getDom(id));
17193             }
17194             return oWrapper;
17195         },
17196
17197         /**
17198          * Returns the actual DOM element
17199          * @method getElement
17200          * @param {String} id the id of the elment to get
17201          * @return {Object} The element
17202          * @deprecated use Roo.getDom instead
17203          * @static
17204          */
17205         getElement: function(id) {
17206             return Roo.getDom(id);
17207         },
17208
17209         /**
17210          * Returns the style property for the DOM element (i.e.,
17211          * document.getElById(id).style)
17212          * @method getCss
17213          * @param {String} id the id of the elment to get
17214          * @return {Object} The style property of the element
17215          * @deprecated use Roo.getDom instead
17216          * @static
17217          */
17218         getCss: function(id) {
17219             var el = Roo.getDom(id);
17220             return (el) ? el.style : null;
17221         },
17222
17223         /**
17224          * Inner class for cached elements
17225          * @class DragDropMgr.ElementWrapper
17226          * @for DragDropMgr
17227          * @private
17228          * @deprecated
17229          */
17230         ElementWrapper: function(el) {
17231                 /**
17232                  * The element
17233                  * @property el
17234                  */
17235                 this.el = el || null;
17236                 /**
17237                  * The element id
17238                  * @property id
17239                  */
17240                 this.id = this.el && el.id;
17241                 /**
17242                  * A reference to the style property
17243                  * @property css
17244                  */
17245                 this.css = this.el && el.style;
17246             },
17247
17248         /**
17249          * Returns the X position of an html element
17250          * @method getPosX
17251          * @param el the element for which to get the position
17252          * @return {int} the X coordinate
17253          * @for DragDropMgr
17254          * @deprecated use Roo.lib.Dom.getX instead
17255          * @static
17256          */
17257         getPosX: function(el) {
17258             return Roo.lib.Dom.getX(el);
17259         },
17260
17261         /**
17262          * Returns the Y position of an html element
17263          * @method getPosY
17264          * @param el the element for which to get the position
17265          * @return {int} the Y coordinate
17266          * @deprecated use Roo.lib.Dom.getY instead
17267          * @static
17268          */
17269         getPosY: function(el) {
17270             return Roo.lib.Dom.getY(el);
17271         },
17272
17273         /**
17274          * Swap two nodes.  In IE, we use the native method, for others we
17275          * emulate the IE behavior
17276          * @method swapNode
17277          * @param n1 the first node to swap
17278          * @param n2 the other node to swap
17279          * @static
17280          */
17281         swapNode: function(n1, n2) {
17282             if (n1.swapNode) {
17283                 n1.swapNode(n2);
17284             } else {
17285                 var p = n2.parentNode;
17286                 var s = n2.nextSibling;
17287
17288                 if (s == n1) {
17289                     p.insertBefore(n1, n2);
17290                 } else if (n2 == n1.nextSibling) {
17291                     p.insertBefore(n2, n1);
17292                 } else {
17293                     n1.parentNode.replaceChild(n2, n1);
17294                     p.insertBefore(n1, s);
17295                 }
17296             }
17297         },
17298
17299         /**
17300          * Returns the current scroll position
17301          * @method getScroll
17302          * @private
17303          * @static
17304          */
17305         getScroll: function () {
17306             var t, l, dde=document.documentElement, db=document.body;
17307             if (dde && (dde.scrollTop || dde.scrollLeft)) {
17308                 t = dde.scrollTop;
17309                 l = dde.scrollLeft;
17310             } else if (db) {
17311                 t = db.scrollTop;
17312                 l = db.scrollLeft;
17313             } else {
17314
17315             }
17316             return { top: t, left: l };
17317         },
17318
17319         /**
17320          * Returns the specified element style property
17321          * @method getStyle
17322          * @param {HTMLElement} el          the element
17323          * @param {string}      styleProp   the style property
17324          * @return {string} The value of the style property
17325          * @deprecated use Roo.lib.Dom.getStyle
17326          * @static
17327          */
17328         getStyle: function(el, styleProp) {
17329             return Roo.fly(el).getStyle(styleProp);
17330         },
17331
17332         /**
17333          * Gets the scrollTop
17334          * @method getScrollTop
17335          * @return {int} the document's scrollTop
17336          * @static
17337          */
17338         getScrollTop: function () { return this.getScroll().top; },
17339
17340         /**
17341          * Gets the scrollLeft
17342          * @method getScrollLeft
17343          * @return {int} the document's scrollTop
17344          * @static
17345          */
17346         getScrollLeft: function () { return this.getScroll().left; },
17347
17348         /**
17349          * Sets the x/y position of an element to the location of the
17350          * target element.
17351          * @method moveToEl
17352          * @param {HTMLElement} moveEl      The element to move
17353          * @param {HTMLElement} targetEl    The position reference element
17354          * @static
17355          */
17356         moveToEl: function (moveEl, targetEl) {
17357             var aCoord = Roo.lib.Dom.getXY(targetEl);
17358             Roo.lib.Dom.setXY(moveEl, aCoord);
17359         },
17360
17361         /**
17362          * Numeric array sort function
17363          * @method numericSort
17364          * @static
17365          */
17366         numericSort: function(a, b) { return (a - b); },
17367
17368         /**
17369          * Internal counter
17370          * @property _timeoutCount
17371          * @private
17372          * @static
17373          */
17374         _timeoutCount: 0,
17375
17376         /**
17377          * Trying to make the load order less important.  Without this we get
17378          * an error if this file is loaded before the Event Utility.
17379          * @method _addListeners
17380          * @private
17381          * @static
17382          */
17383         _addListeners: function() {
17384             var DDM = Roo.dd.DDM;
17385             if ( Roo.lib.Event && document ) {
17386                 DDM._onLoad();
17387             } else {
17388                 if (DDM._timeoutCount > 2000) {
17389                 } else {
17390                     setTimeout(DDM._addListeners, 10);
17391                     if (document && document.body) {
17392                         DDM._timeoutCount += 1;
17393                     }
17394                 }
17395             }
17396         },
17397
17398         /**
17399          * Recursively searches the immediate parent and all child nodes for
17400          * the handle element in order to determine wheter or not it was
17401          * clicked.
17402          * @method handleWasClicked
17403          * @param node the html element to inspect
17404          * @static
17405          */
17406         handleWasClicked: function(node, id) {
17407             if (this.isHandle(id, node.id)) {
17408                 return true;
17409             } else {
17410                 // check to see if this is a text node child of the one we want
17411                 var p = node.parentNode;
17412
17413                 while (p) {
17414                     if (this.isHandle(id, p.id)) {
17415                         return true;
17416                     } else {
17417                         p = p.parentNode;
17418                     }
17419                 }
17420             }
17421
17422             return false;
17423         }
17424
17425     };
17426
17427 }();
17428
17429 // shorter alias, save a few bytes
17430 Roo.dd.DDM = Roo.dd.DragDropMgr;
17431 Roo.dd.DDM._addListeners();
17432
17433 }/*
17434  * Based on:
17435  * Ext JS Library 1.1.1
17436  * Copyright(c) 2006-2007, Ext JS, LLC.
17437  *
17438  * Originally Released Under LGPL - original licence link has changed is not relivant.
17439  *
17440  * Fork - LGPL
17441  * <script type="text/javascript">
17442  */
17443
17444 /**
17445  * @class Roo.dd.DD
17446  * A DragDrop implementation where the linked element follows the
17447  * mouse cursor during a drag.
17448  * @extends Roo.dd.DragDrop
17449  * @constructor
17450  * @param {String} id the id of the linked element
17451  * @param {String} sGroup the group of related DragDrop items
17452  * @param {object} config an object containing configurable attributes
17453  *                Valid properties for DD:
17454  *                    scroll
17455  */
17456 Roo.dd.DD = function(id, sGroup, config) {
17457     if (id) {
17458         this.init(id, sGroup, config);
17459     }
17460 };
17461
17462 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
17463
17464     /**
17465      * When set to true, the utility automatically tries to scroll the browser
17466      * window wehn a drag and drop element is dragged near the viewport boundary.
17467      * Defaults to true.
17468      * @property scroll
17469      * @type boolean
17470      */
17471     scroll: true,
17472
17473     /**
17474      * Sets the pointer offset to the distance between the linked element's top
17475      * left corner and the location the element was clicked
17476      * @method autoOffset
17477      * @param {int} iPageX the X coordinate of the click
17478      * @param {int} iPageY the Y coordinate of the click
17479      */
17480     autoOffset: function(iPageX, iPageY) {
17481         var x = iPageX - this.startPageX;
17482         var y = iPageY - this.startPageY;
17483         this.setDelta(x, y);
17484     },
17485
17486     /**
17487      * Sets the pointer offset.  You can call this directly to force the
17488      * offset to be in a particular location (e.g., pass in 0,0 to set it
17489      * to the center of the object)
17490      * @method setDelta
17491      * @param {int} iDeltaX the distance from the left
17492      * @param {int} iDeltaY the distance from the top
17493      */
17494     setDelta: function(iDeltaX, iDeltaY) {
17495         this.deltaX = iDeltaX;
17496         this.deltaY = iDeltaY;
17497     },
17498
17499     /**
17500      * Sets the drag element to the location of the mousedown or click event,
17501      * maintaining the cursor location relative to the location on the element
17502      * that was clicked.  Override this if you want to place the element in a
17503      * location other than where the cursor is.
17504      * @method setDragElPos
17505      * @param {int} iPageX the X coordinate of the mousedown or drag event
17506      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17507      */
17508     setDragElPos: function(iPageX, iPageY) {
17509         // the first time we do this, we are going to check to make sure
17510         // the element has css positioning
17511
17512         var el = this.getDragEl();
17513         this.alignElWithMouse(el, iPageX, iPageY);
17514     },
17515
17516     /**
17517      * Sets the element to the location of the mousedown or click event,
17518      * maintaining the cursor location relative to the location on the element
17519      * that was clicked.  Override this if you want to place the element in a
17520      * location other than where the cursor is.
17521      * @method alignElWithMouse
17522      * @param {HTMLElement} el the element to move
17523      * @param {int} iPageX the X coordinate of the mousedown or drag event
17524      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17525      */
17526     alignElWithMouse: function(el, iPageX, iPageY) {
17527         var oCoord = this.getTargetCoord(iPageX, iPageY);
17528         var fly = el.dom ? el : Roo.fly(el);
17529         if (!this.deltaSetXY) {
17530             var aCoord = [oCoord.x, oCoord.y];
17531             fly.setXY(aCoord);
17532             var newLeft = fly.getLeft(true);
17533             var newTop  = fly.getTop(true);
17534             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17535         } else {
17536             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17537         }
17538
17539         this.cachePosition(oCoord.x, oCoord.y);
17540         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17541         return oCoord;
17542     },
17543
17544     /**
17545      * Saves the most recent position so that we can reset the constraints and
17546      * tick marks on-demand.  We need to know this so that we can calculate the
17547      * number of pixels the element is offset from its original position.
17548      * @method cachePosition
17549      * @param iPageX the current x position (optional, this just makes it so we
17550      * don't have to look it up again)
17551      * @param iPageY the current y position (optional, this just makes it so we
17552      * don't have to look it up again)
17553      */
17554     cachePosition: function(iPageX, iPageY) {
17555         if (iPageX) {
17556             this.lastPageX = iPageX;
17557             this.lastPageY = iPageY;
17558         } else {
17559             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17560             this.lastPageX = aCoord[0];
17561             this.lastPageY = aCoord[1];
17562         }
17563     },
17564
17565     /**
17566      * Auto-scroll the window if the dragged object has been moved beyond the
17567      * visible window boundary.
17568      * @method autoScroll
17569      * @param {int} x the drag element's x position
17570      * @param {int} y the drag element's y position
17571      * @param {int} h the height of the drag element
17572      * @param {int} w the width of the drag element
17573      * @private
17574      */
17575     autoScroll: function(x, y, h, w) {
17576
17577         if (this.scroll) {
17578             // The client height
17579             var clientH = Roo.lib.Dom.getViewWidth();
17580
17581             // The client width
17582             var clientW = Roo.lib.Dom.getViewHeight();
17583
17584             // The amt scrolled down
17585             var st = this.DDM.getScrollTop();
17586
17587             // The amt scrolled right
17588             var sl = this.DDM.getScrollLeft();
17589
17590             // Location of the bottom of the element
17591             var bot = h + y;
17592
17593             // Location of the right of the element
17594             var right = w + x;
17595
17596             // The distance from the cursor to the bottom of the visible area,
17597             // adjusted so that we don't scroll if the cursor is beyond the
17598             // element drag constraints
17599             var toBot = (clientH + st - y - this.deltaY);
17600
17601             // The distance from the cursor to the right of the visible area
17602             var toRight = (clientW + sl - x - this.deltaX);
17603
17604
17605             // How close to the edge the cursor must be before we scroll
17606             // var thresh = (document.all) ? 100 : 40;
17607             var thresh = 40;
17608
17609             // How many pixels to scroll per autoscroll op.  This helps to reduce
17610             // clunky scrolling. IE is more sensitive about this ... it needs this
17611             // value to be higher.
17612             var scrAmt = (document.all) ? 80 : 30;
17613
17614             // Scroll down if we are near the bottom of the visible page and the
17615             // obj extends below the crease
17616             if ( bot > clientH && toBot < thresh ) {
17617                 window.scrollTo(sl, st + scrAmt);
17618             }
17619
17620             // Scroll up if the window is scrolled down and the top of the object
17621             // goes above the top border
17622             if ( y < st && st > 0 && y - st < thresh ) {
17623                 window.scrollTo(sl, st - scrAmt);
17624             }
17625
17626             // Scroll right if the obj is beyond the right border and the cursor is
17627             // near the border.
17628             if ( right > clientW && toRight < thresh ) {
17629                 window.scrollTo(sl + scrAmt, st);
17630             }
17631
17632             // Scroll left if the window has been scrolled to the right and the obj
17633             // extends past the left border
17634             if ( x < sl && sl > 0 && x - sl < thresh ) {
17635                 window.scrollTo(sl - scrAmt, st);
17636             }
17637         }
17638     },
17639
17640     /**
17641      * Finds the location the element should be placed if we want to move
17642      * it to where the mouse location less the click offset would place us.
17643      * @method getTargetCoord
17644      * @param {int} iPageX the X coordinate of the click
17645      * @param {int} iPageY the Y coordinate of the click
17646      * @return an object that contains the coordinates (Object.x and Object.y)
17647      * @private
17648      */
17649     getTargetCoord: function(iPageX, iPageY) {
17650
17651
17652         var x = iPageX - this.deltaX;
17653         var y = iPageY - this.deltaY;
17654
17655         if (this.constrainX) {
17656             if (x < this.minX) { x = this.minX; }
17657             if (x > this.maxX) { x = this.maxX; }
17658         }
17659
17660         if (this.constrainY) {
17661             if (y < this.minY) { y = this.minY; }
17662             if (y > this.maxY) { y = this.maxY; }
17663         }
17664
17665         x = this.getTick(x, this.xTicks);
17666         y = this.getTick(y, this.yTicks);
17667
17668
17669         return {x:x, y:y};
17670     },
17671
17672     /*
17673      * Sets up config options specific to this class. Overrides
17674      * Roo.dd.DragDrop, but all versions of this method through the
17675      * inheritance chain are called
17676      */
17677     applyConfig: function() {
17678         Roo.dd.DD.superclass.applyConfig.call(this);
17679         this.scroll = (this.config.scroll !== false);
17680     },
17681
17682     /*
17683      * Event that fires prior to the onMouseDown event.  Overrides
17684      * Roo.dd.DragDrop.
17685      */
17686     b4MouseDown: function(e) {
17687         // this.resetConstraints();
17688         this.autoOffset(e.getPageX(),
17689                             e.getPageY());
17690     },
17691
17692     /*
17693      * Event that fires prior to the onDrag event.  Overrides
17694      * Roo.dd.DragDrop.
17695      */
17696     b4Drag: function(e) {
17697         this.setDragElPos(e.getPageX(),
17698                             e.getPageY());
17699     },
17700
17701     toString: function() {
17702         return ("DD " + this.id);
17703     }
17704
17705     //////////////////////////////////////////////////////////////////////////
17706     // Debugging ygDragDrop events that can be overridden
17707     //////////////////////////////////////////////////////////////////////////
17708     /*
17709     startDrag: function(x, y) {
17710     },
17711
17712     onDrag: function(e) {
17713     },
17714
17715     onDragEnter: function(e, id) {
17716     },
17717
17718     onDragOver: function(e, id) {
17719     },
17720
17721     onDragOut: function(e, id) {
17722     },
17723
17724     onDragDrop: function(e, id) {
17725     },
17726
17727     endDrag: function(e) {
17728     }
17729
17730     */
17731
17732 });/*
17733  * Based on:
17734  * Ext JS Library 1.1.1
17735  * Copyright(c) 2006-2007, Ext JS, LLC.
17736  *
17737  * Originally Released Under LGPL - original licence link has changed is not relivant.
17738  *
17739  * Fork - LGPL
17740  * <script type="text/javascript">
17741  */
17742
17743 /**
17744  * @class Roo.dd.DDProxy
17745  * A DragDrop implementation that inserts an empty, bordered div into
17746  * the document that follows the cursor during drag operations.  At the time of
17747  * the click, the frame div is resized to the dimensions of the linked html
17748  * element, and moved to the exact location of the linked element.
17749  *
17750  * References to the "frame" element refer to the single proxy element that
17751  * was created to be dragged in place of all DDProxy elements on the
17752  * page.
17753  *
17754  * @extends Roo.dd.DD
17755  * @constructor
17756  * @param {String} id the id of the linked html element
17757  * @param {String} sGroup the group of related DragDrop objects
17758  * @param {object} config an object containing configurable attributes
17759  *                Valid properties for DDProxy in addition to those in DragDrop:
17760  *                   resizeFrame, centerFrame, dragElId
17761  */
17762 Roo.dd.DDProxy = function(id, sGroup, config) {
17763     if (id) {
17764         this.init(id, sGroup, config);
17765         this.initFrame();
17766     }
17767 };
17768
17769 /**
17770  * The default drag frame div id
17771  * @property Roo.dd.DDProxy.dragElId
17772  * @type String
17773  * @static
17774  */
17775 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17776
17777 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17778
17779     /**
17780      * By default we resize the drag frame to be the same size as the element
17781      * we want to drag (this is to get the frame effect).  We can turn it off
17782      * if we want a different behavior.
17783      * @property resizeFrame
17784      * @type boolean
17785      */
17786     resizeFrame: true,
17787
17788     /**
17789      * By default the frame is positioned exactly where the drag element is, so
17790      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17791      * you do not have constraints on the obj is to have the drag frame centered
17792      * around the cursor.  Set centerFrame to true for this effect.
17793      * @property centerFrame
17794      * @type boolean
17795      */
17796     centerFrame: false,
17797
17798     /**
17799      * Creates the proxy element if it does not yet exist
17800      * @method createFrame
17801      */
17802     createFrame: function() {
17803         var self = this;
17804         var body = document.body;
17805
17806         if (!body || !body.firstChild) {
17807             setTimeout( function() { self.createFrame(); }, 50 );
17808             return;
17809         }
17810
17811         var div = this.getDragEl();
17812
17813         if (!div) {
17814             div    = document.createElement("div");
17815             div.id = this.dragElId;
17816             var s  = div.style;
17817
17818             s.position   = "absolute";
17819             s.visibility = "hidden";
17820             s.cursor     = "move";
17821             s.border     = "2px solid #aaa";
17822             s.zIndex     = 999;
17823
17824             // appendChild can blow up IE if invoked prior to the window load event
17825             // while rendering a table.  It is possible there are other scenarios
17826             // that would cause this to happen as well.
17827             body.insertBefore(div, body.firstChild);
17828         }
17829     },
17830
17831     /**
17832      * Initialization for the drag frame element.  Must be called in the
17833      * constructor of all subclasses
17834      * @method initFrame
17835      */
17836     initFrame: function() {
17837         this.createFrame();
17838     },
17839
17840     applyConfig: function() {
17841         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17842
17843         this.resizeFrame = (this.config.resizeFrame !== false);
17844         this.centerFrame = (this.config.centerFrame);
17845         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17846     },
17847
17848     /**
17849      * Resizes the drag frame to the dimensions of the clicked object, positions
17850      * it over the object, and finally displays it
17851      * @method showFrame
17852      * @param {int} iPageX X click position
17853      * @param {int} iPageY Y click position
17854      * @private
17855      */
17856     showFrame: function(iPageX, iPageY) {
17857         var el = this.getEl();
17858         var dragEl = this.getDragEl();
17859         var s = dragEl.style;
17860
17861         this._resizeProxy();
17862
17863         if (this.centerFrame) {
17864             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17865                            Math.round(parseInt(s.height, 10)/2) );
17866         }
17867
17868         this.setDragElPos(iPageX, iPageY);
17869
17870         Roo.fly(dragEl).show();
17871     },
17872
17873     /**
17874      * The proxy is automatically resized to the dimensions of the linked
17875      * element when a drag is initiated, unless resizeFrame is set to false
17876      * @method _resizeProxy
17877      * @private
17878      */
17879     _resizeProxy: function() {
17880         if (this.resizeFrame) {
17881             var el = this.getEl();
17882             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17883         }
17884     },
17885
17886     // overrides Roo.dd.DragDrop
17887     b4MouseDown: function(e) {
17888         var x = e.getPageX();
17889         var y = e.getPageY();
17890         this.autoOffset(x, y);
17891         this.setDragElPos(x, y);
17892     },
17893
17894     // overrides Roo.dd.DragDrop
17895     b4StartDrag: function(x, y) {
17896         // show the drag frame
17897         this.showFrame(x, y);
17898     },
17899
17900     // overrides Roo.dd.DragDrop
17901     b4EndDrag: function(e) {
17902         Roo.fly(this.getDragEl()).hide();
17903     },
17904
17905     // overrides Roo.dd.DragDrop
17906     // By default we try to move the element to the last location of the frame.
17907     // This is so that the default behavior mirrors that of Roo.dd.DD.
17908     endDrag: function(e) {
17909
17910         var lel = this.getEl();
17911         var del = this.getDragEl();
17912
17913         // Show the drag frame briefly so we can get its position
17914         del.style.visibility = "";
17915
17916         this.beforeMove();
17917         // Hide the linked element before the move to get around a Safari
17918         // rendering bug.
17919         lel.style.visibility = "hidden";
17920         Roo.dd.DDM.moveToEl(lel, del);
17921         del.style.visibility = "hidden";
17922         lel.style.visibility = "";
17923
17924         this.afterDrag();
17925     },
17926
17927     beforeMove : function(){
17928
17929     },
17930
17931     afterDrag : function(){
17932
17933     },
17934
17935     toString: function() {
17936         return ("DDProxy " + this.id);
17937     }
17938
17939 });
17940 /*
17941  * Based on:
17942  * Ext JS Library 1.1.1
17943  * Copyright(c) 2006-2007, Ext JS, LLC.
17944  *
17945  * Originally Released Under LGPL - original licence link has changed is not relivant.
17946  *
17947  * Fork - LGPL
17948  * <script type="text/javascript">
17949  */
17950
17951  /**
17952  * @class Roo.dd.DDTarget
17953  * A DragDrop implementation that does not move, but can be a drop
17954  * target.  You would get the same result by simply omitting implementation
17955  * for the event callbacks, but this way we reduce the processing cost of the
17956  * event listener and the callbacks.
17957  * @extends Roo.dd.DragDrop
17958  * @constructor
17959  * @param {String} id the id of the element that is a drop target
17960  * @param {String} sGroup the group of related DragDrop objects
17961  * @param {object} config an object containing configurable attributes
17962  *                 Valid properties for DDTarget in addition to those in
17963  *                 DragDrop:
17964  *                    none
17965  */
17966 Roo.dd.DDTarget = function(id, sGroup, config) {
17967     if (id) {
17968         this.initTarget(id, sGroup, config);
17969     }
17970     if (config.listeners || config.events) { 
17971        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17972             listeners : config.listeners || {}, 
17973             events : config.events || {} 
17974         });    
17975     }
17976 };
17977
17978 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17979 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17980     toString: function() {
17981         return ("DDTarget " + this.id);
17982     }
17983 });
17984 /*
17985  * Based on:
17986  * Ext JS Library 1.1.1
17987  * Copyright(c) 2006-2007, Ext JS, LLC.
17988  *
17989  * Originally Released Under LGPL - original licence link has changed is not relivant.
17990  *
17991  * Fork - LGPL
17992  * <script type="text/javascript">
17993  */
17994  
17995
17996 /**
17997  * @class Roo.dd.ScrollManager
17998  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17999  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
18000  * @singleton
18001  */
18002 Roo.dd.ScrollManager = function(){
18003     var ddm = Roo.dd.DragDropMgr;
18004     var els = {};
18005     var dragEl = null;
18006     var proc = {};
18007     
18008     
18009     
18010     var onStop = function(e){
18011         dragEl = null;
18012         clearProc();
18013     };
18014     
18015     var triggerRefresh = function(){
18016         if(ddm.dragCurrent){
18017              ddm.refreshCache(ddm.dragCurrent.groups);
18018         }
18019     };
18020     
18021     var doScroll = function(){
18022         if(ddm.dragCurrent){
18023             var dds = Roo.dd.ScrollManager;
18024             if(!dds.animate){
18025                 if(proc.el.scroll(proc.dir, dds.increment)){
18026                     triggerRefresh();
18027                 }
18028             }else{
18029                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
18030             }
18031         }
18032     };
18033     
18034     var clearProc = function(){
18035         if(proc.id){
18036             clearInterval(proc.id);
18037         }
18038         proc.id = 0;
18039         proc.el = null;
18040         proc.dir = "";
18041     };
18042     
18043     var startProc = function(el, dir){
18044          Roo.log('scroll startproc');
18045         clearProc();
18046         proc.el = el;
18047         proc.dir = dir;
18048         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
18049     };
18050     
18051     var onFire = function(e, isDrop){
18052        
18053         if(isDrop || !ddm.dragCurrent){ return; }
18054         var dds = Roo.dd.ScrollManager;
18055         if(!dragEl || dragEl != ddm.dragCurrent){
18056             dragEl = ddm.dragCurrent;
18057             // refresh regions on drag start
18058             dds.refreshCache();
18059         }
18060         
18061         var xy = Roo.lib.Event.getXY(e);
18062         var pt = new Roo.lib.Point(xy[0], xy[1]);
18063         for(var id in els){
18064             var el = els[id], r = el._region;
18065             if(r && r.contains(pt) && el.isScrollable()){
18066                 if(r.bottom - pt.y <= dds.thresh){
18067                     if(proc.el != el){
18068                         startProc(el, "down");
18069                     }
18070                     return;
18071                 }else if(r.right - pt.x <= dds.thresh){
18072                     if(proc.el != el){
18073                         startProc(el, "left");
18074                     }
18075                     return;
18076                 }else if(pt.y - r.top <= dds.thresh){
18077                     if(proc.el != el){
18078                         startProc(el, "up");
18079                     }
18080                     return;
18081                 }else if(pt.x - r.left <= dds.thresh){
18082                     if(proc.el != el){
18083                         startProc(el, "right");
18084                     }
18085                     return;
18086                 }
18087             }
18088         }
18089         clearProc();
18090     };
18091     
18092     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
18093     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
18094     
18095     return {
18096         /**
18097          * Registers new overflow element(s) to auto scroll
18098          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
18099          */
18100         register : function(el){
18101             if(el instanceof Array){
18102                 for(var i = 0, len = el.length; i < len; i++) {
18103                         this.register(el[i]);
18104                 }
18105             }else{
18106                 el = Roo.get(el);
18107                 els[el.id] = el;
18108             }
18109             Roo.dd.ScrollManager.els = els;
18110         },
18111         
18112         /**
18113          * Unregisters overflow element(s) so they are no longer scrolled
18114          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
18115          */
18116         unregister : function(el){
18117             if(el instanceof Array){
18118                 for(var i = 0, len = el.length; i < len; i++) {
18119                         this.unregister(el[i]);
18120                 }
18121             }else{
18122                 el = Roo.get(el);
18123                 delete els[el.id];
18124             }
18125         },
18126         
18127         /**
18128          * The number of pixels from the edge of a container the pointer needs to be to 
18129          * trigger scrolling (defaults to 25)
18130          * @type Number
18131          */
18132         thresh : 25,
18133         
18134         /**
18135          * The number of pixels to scroll in each scroll increment (defaults to 50)
18136          * @type Number
18137          */
18138         increment : 100,
18139         
18140         /**
18141          * The frequency of scrolls in milliseconds (defaults to 500)
18142          * @type Number
18143          */
18144         frequency : 500,
18145         
18146         /**
18147          * True to animate the scroll (defaults to true)
18148          * @type Boolean
18149          */
18150         animate: true,
18151         
18152         /**
18153          * The animation duration in seconds - 
18154          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
18155          * @type Number
18156          */
18157         animDuration: .4,
18158         
18159         /**
18160          * Manually trigger a cache refresh.
18161          */
18162         refreshCache : function(){
18163             for(var id in els){
18164                 if(typeof els[id] == 'object'){ // for people extending the object prototype
18165                     els[id]._region = els[id].getRegion();
18166                 }
18167             }
18168         }
18169     };
18170 }();/*
18171  * Based on:
18172  * Ext JS Library 1.1.1
18173  * Copyright(c) 2006-2007, Ext JS, LLC.
18174  *
18175  * Originally Released Under LGPL - original licence link has changed is not relivant.
18176  *
18177  * Fork - LGPL
18178  * <script type="text/javascript">
18179  */
18180  
18181
18182 /**
18183  * @class Roo.dd.Registry
18184  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
18185  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
18186  * @singleton
18187  */
18188 Roo.dd.Registry = function(){
18189     var elements = {}; 
18190     var handles = {}; 
18191     var autoIdSeed = 0;
18192
18193     var getId = function(el, autogen){
18194         if(typeof el == "string"){
18195             return el;
18196         }
18197         var id = el.id;
18198         if(!id && autogen !== false){
18199             id = "roodd-" + (++autoIdSeed);
18200             el.id = id;
18201         }
18202         return id;
18203     };
18204     
18205     return {
18206     /**
18207      * Register a drag drop element
18208      * @param {String|HTMLElement} element The id or DOM node to register
18209      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
18210      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
18211      * knows how to interpret, plus there are some specific properties known to the Registry that should be
18212      * populated in the data object (if applicable):
18213      * <pre>
18214 Value      Description<br />
18215 ---------  ------------------------------------------<br />
18216 handles    Array of DOM nodes that trigger dragging<br />
18217            for the element being registered<br />
18218 isHandle   True if the element passed in triggers<br />
18219            dragging itself, else false
18220 </pre>
18221      */
18222         register : function(el, data){
18223             data = data || {};
18224             if(typeof el == "string"){
18225                 el = document.getElementById(el);
18226             }
18227             data.ddel = el;
18228             elements[getId(el)] = data;
18229             if(data.isHandle !== false){
18230                 handles[data.ddel.id] = data;
18231             }
18232             if(data.handles){
18233                 var hs = data.handles;
18234                 for(var i = 0, len = hs.length; i < len; i++){
18235                         handles[getId(hs[i])] = data;
18236                 }
18237             }
18238         },
18239
18240     /**
18241      * Unregister a drag drop element
18242      * @param {String|HTMLElement}  element The id or DOM node to unregister
18243      */
18244         unregister : function(el){
18245             var id = getId(el, false);
18246             var data = elements[id];
18247             if(data){
18248                 delete elements[id];
18249                 if(data.handles){
18250                     var hs = data.handles;
18251                     for(var i = 0, len = hs.length; i < len; i++){
18252                         delete handles[getId(hs[i], false)];
18253                     }
18254                 }
18255             }
18256         },
18257
18258     /**
18259      * Returns the handle registered for a DOM Node by id
18260      * @param {String|HTMLElement} id The DOM node or id to look up
18261      * @return {Object} handle The custom handle data
18262      */
18263         getHandle : function(id){
18264             if(typeof id != "string"){ // must be element?
18265                 id = id.id;
18266             }
18267             return handles[id];
18268         },
18269
18270     /**
18271      * Returns the handle that is registered for the DOM node that is the target of the event
18272      * @param {Event} e The event
18273      * @return {Object} handle The custom handle data
18274      */
18275         getHandleFromEvent : function(e){
18276             var t = Roo.lib.Event.getTarget(e);
18277             return t ? handles[t.id] : null;
18278         },
18279
18280     /**
18281      * Returns a custom data object that is registered for a DOM node by id
18282      * @param {String|HTMLElement} id The DOM node or id to look up
18283      * @return {Object} data The custom data
18284      */
18285         getTarget : function(id){
18286             if(typeof id != "string"){ // must be element?
18287                 id = id.id;
18288             }
18289             return elements[id];
18290         },
18291
18292     /**
18293      * Returns a custom data object that is registered for the DOM node that is the target of the event
18294      * @param {Event} e The event
18295      * @return {Object} data The custom data
18296      */
18297         getTargetFromEvent : function(e){
18298             var t = Roo.lib.Event.getTarget(e);
18299             return t ? elements[t.id] || handles[t.id] : null;
18300         }
18301     };
18302 }();/*
18303  * Based on:
18304  * Ext JS Library 1.1.1
18305  * Copyright(c) 2006-2007, Ext JS, LLC.
18306  *
18307  * Originally Released Under LGPL - original licence link has changed is not relivant.
18308  *
18309  * Fork - LGPL
18310  * <script type="text/javascript">
18311  */
18312  
18313
18314 /**
18315  * @class Roo.dd.StatusProxy
18316  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
18317  * default drag proxy used by all Roo.dd components.
18318  * @constructor
18319  * @param {Object} config
18320  */
18321 Roo.dd.StatusProxy = function(config){
18322     Roo.apply(this, config);
18323     this.id = this.id || Roo.id();
18324     this.el = new Roo.Layer({
18325         dh: {
18326             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
18327                 {tag: "div", cls: "x-dd-drop-icon"},
18328                 {tag: "div", cls: "x-dd-drag-ghost"}
18329             ]
18330         }, 
18331         shadow: !config || config.shadow !== false
18332     });
18333     this.ghost = Roo.get(this.el.dom.childNodes[1]);
18334     this.dropStatus = this.dropNotAllowed;
18335 };
18336
18337 Roo.dd.StatusProxy.prototype = {
18338     /**
18339      * @cfg {String} dropAllowed
18340      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
18341      */
18342     dropAllowed : "x-dd-drop-ok",
18343     /**
18344      * @cfg {String} dropNotAllowed
18345      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
18346      */
18347     dropNotAllowed : "x-dd-drop-nodrop",
18348
18349     /**
18350      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
18351      * over the current target element.
18352      * @param {String} cssClass The css class for the new drop status indicator image
18353      */
18354     setStatus : function(cssClass){
18355         cssClass = cssClass || this.dropNotAllowed;
18356         if(this.dropStatus != cssClass){
18357             this.el.replaceClass(this.dropStatus, cssClass);
18358             this.dropStatus = cssClass;
18359         }
18360     },
18361
18362     /**
18363      * Resets the status indicator to the default dropNotAllowed value
18364      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
18365      */
18366     reset : function(clearGhost){
18367         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
18368         this.dropStatus = this.dropNotAllowed;
18369         if(clearGhost){
18370             this.ghost.update("");
18371         }
18372     },
18373
18374     /**
18375      * Updates the contents of the ghost element
18376      * @param {String} html The html that will replace the current innerHTML of the ghost element
18377      */
18378     update : function(html){
18379         if(typeof html == "string"){
18380             this.ghost.update(html);
18381         }else{
18382             this.ghost.update("");
18383             html.style.margin = "0";
18384             this.ghost.dom.appendChild(html);
18385         }
18386         // ensure float = none set?? cant remember why though.
18387         var el = this.ghost.dom.firstChild;
18388                 if(el){
18389                         Roo.fly(el).setStyle('float', 'none');
18390                 }
18391     },
18392     
18393     /**
18394      * Returns the underlying proxy {@link Roo.Layer}
18395      * @return {Roo.Layer} el
18396     */
18397     getEl : function(){
18398         return this.el;
18399     },
18400
18401     /**
18402      * Returns the ghost element
18403      * @return {Roo.Element} el
18404      */
18405     getGhost : function(){
18406         return this.ghost;
18407     },
18408
18409     /**
18410      * Hides the proxy
18411      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
18412      */
18413     hide : function(clear){
18414         this.el.hide();
18415         if(clear){
18416             this.reset(true);
18417         }
18418     },
18419
18420     /**
18421      * Stops the repair animation if it's currently running
18422      */
18423     stop : function(){
18424         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
18425             this.anim.stop();
18426         }
18427     },
18428
18429     /**
18430      * Displays this proxy
18431      */
18432     show : function(){
18433         this.el.show();
18434     },
18435
18436     /**
18437      * Force the Layer to sync its shadow and shim positions to the element
18438      */
18439     sync : function(){
18440         this.el.sync();
18441     },
18442
18443     /**
18444      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
18445      * invalid drop operation by the item being dragged.
18446      * @param {Array} xy The XY position of the element ([x, y])
18447      * @param {Function} callback The function to call after the repair is complete
18448      * @param {Object} scope The scope in which to execute the callback
18449      */
18450     repair : function(xy, callback, scope){
18451         this.callback = callback;
18452         this.scope = scope;
18453         if(xy && this.animRepair !== false){
18454             this.el.addClass("x-dd-drag-repair");
18455             this.el.hideUnders(true);
18456             this.anim = this.el.shift({
18457                 duration: this.repairDuration || .5,
18458                 easing: 'easeOut',
18459                 xy: xy,
18460                 stopFx: true,
18461                 callback: this.afterRepair,
18462                 scope: this
18463             });
18464         }else{
18465             this.afterRepair();
18466         }
18467     },
18468
18469     // private
18470     afterRepair : function(){
18471         this.hide(true);
18472         if(typeof this.callback == "function"){
18473             this.callback.call(this.scope || this);
18474         }
18475         this.callback = null;
18476         this.scope = null;
18477     }
18478 };/*
18479  * Based on:
18480  * Ext JS Library 1.1.1
18481  * Copyright(c) 2006-2007, Ext JS, LLC.
18482  *
18483  * Originally Released Under LGPL - original licence link has changed is not relivant.
18484  *
18485  * Fork - LGPL
18486  * <script type="text/javascript">
18487  */
18488
18489 /**
18490  * @class Roo.dd.DragSource
18491  * @extends Roo.dd.DDProxy
18492  * A simple class that provides the basic implementation needed to make any element draggable.
18493  * @constructor
18494  * @param {String/HTMLElement/Element} el The container element
18495  * @param {Object} config
18496  */
18497 Roo.dd.DragSource = function(el, config){
18498     this.el = Roo.get(el);
18499     this.dragData = {};
18500     
18501     Roo.apply(this, config);
18502     
18503     if(!this.proxy){
18504         this.proxy = new Roo.dd.StatusProxy();
18505     }
18506
18507     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18508           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18509     
18510     this.dragging = false;
18511 };
18512
18513 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18514     /**
18515      * @cfg {String} dropAllowed
18516      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18517      */
18518     dropAllowed : "x-dd-drop-ok",
18519     /**
18520      * @cfg {String} dropNotAllowed
18521      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18522      */
18523     dropNotAllowed : "x-dd-drop-nodrop",
18524
18525     /**
18526      * Returns the data object associated with this drag source
18527      * @return {Object} data An object containing arbitrary data
18528      */
18529     getDragData : function(e){
18530         return this.dragData;
18531     },
18532
18533     // private
18534     onDragEnter : function(e, id){
18535         var target = Roo.dd.DragDropMgr.getDDById(id);
18536         this.cachedTarget = target;
18537         if(this.beforeDragEnter(target, e, id) !== false){
18538             if(target.isNotifyTarget){
18539                 var status = target.notifyEnter(this, e, this.dragData);
18540                 this.proxy.setStatus(status);
18541             }else{
18542                 this.proxy.setStatus(this.dropAllowed);
18543             }
18544             
18545             if(this.afterDragEnter){
18546                 /**
18547                  * An empty function by default, but provided so that you can perform a custom action
18548                  * when the dragged item enters the drop target by providing an implementation.
18549                  * @param {Roo.dd.DragDrop} target The drop target
18550                  * @param {Event} e The event object
18551                  * @param {String} id The id of the dragged element
18552                  * @method afterDragEnter
18553                  */
18554                 this.afterDragEnter(target, e, id);
18555             }
18556         }
18557     },
18558
18559     /**
18560      * An empty function by default, but provided so that you can perform a custom action
18561      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18562      * @param {Roo.dd.DragDrop} target The drop target
18563      * @param {Event} e The event object
18564      * @param {String} id The id of the dragged element
18565      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18566      */
18567     beforeDragEnter : function(target, e, id){
18568         return true;
18569     },
18570
18571     // private
18572     alignElWithMouse: function() {
18573         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18574         this.proxy.sync();
18575     },
18576
18577     // private
18578     onDragOver : function(e, id){
18579         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18580         if(this.beforeDragOver(target, e, id) !== false){
18581             if(target.isNotifyTarget){
18582                 var status = target.notifyOver(this, e, this.dragData);
18583                 this.proxy.setStatus(status);
18584             }
18585
18586             if(this.afterDragOver){
18587                 /**
18588                  * An empty function by default, but provided so that you can perform a custom action
18589                  * while the dragged item is over the drop target by providing an implementation.
18590                  * @param {Roo.dd.DragDrop} target The drop target
18591                  * @param {Event} e The event object
18592                  * @param {String} id The id of the dragged element
18593                  * @method afterDragOver
18594                  */
18595                 this.afterDragOver(target, e, id);
18596             }
18597         }
18598     },
18599
18600     /**
18601      * An empty function by default, but provided so that you can perform a custom action
18602      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18603      * @param {Roo.dd.DragDrop} target The drop target
18604      * @param {Event} e The event object
18605      * @param {String} id The id of the dragged element
18606      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18607      */
18608     beforeDragOver : function(target, e, id){
18609         return true;
18610     },
18611
18612     // private
18613     onDragOut : function(e, id){
18614         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18615         if(this.beforeDragOut(target, e, id) !== false){
18616             if(target.isNotifyTarget){
18617                 target.notifyOut(this, e, this.dragData);
18618             }
18619             this.proxy.reset();
18620             if(this.afterDragOut){
18621                 /**
18622                  * An empty function by default, but provided so that you can perform a custom action
18623                  * after the dragged item is dragged out of the target without dropping.
18624                  * @param {Roo.dd.DragDrop} target The drop target
18625                  * @param {Event} e The event object
18626                  * @param {String} id The id of the dragged element
18627                  * @method afterDragOut
18628                  */
18629                 this.afterDragOut(target, e, id);
18630             }
18631         }
18632         this.cachedTarget = null;
18633     },
18634
18635     /**
18636      * An empty function by default, but provided so that you can perform a custom action before the dragged
18637      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18638      * @param {Roo.dd.DragDrop} target The drop target
18639      * @param {Event} e The event object
18640      * @param {String} id The id of the dragged element
18641      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18642      */
18643     beforeDragOut : function(target, e, id){
18644         return true;
18645     },
18646     
18647     // private
18648     onDragDrop : function(e, id){
18649         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18650         if(this.beforeDragDrop(target, e, id) !== false){
18651             if(target.isNotifyTarget){
18652                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18653                     this.onValidDrop(target, e, id);
18654                 }else{
18655                     this.onInvalidDrop(target, e, id);
18656                 }
18657             }else{
18658                 this.onValidDrop(target, e, id);
18659             }
18660             
18661             if(this.afterDragDrop){
18662                 /**
18663                  * An empty function by default, but provided so that you can perform a custom action
18664                  * after a valid drag drop has occurred by providing an implementation.
18665                  * @param {Roo.dd.DragDrop} target The drop target
18666                  * @param {Event} e The event object
18667                  * @param {String} id The id of the dropped element
18668                  * @method afterDragDrop
18669                  */
18670                 this.afterDragDrop(target, e, id);
18671             }
18672         }
18673         delete this.cachedTarget;
18674     },
18675
18676     /**
18677      * An empty function by default, but provided so that you can perform a custom action before the dragged
18678      * item is dropped onto the target and optionally cancel the onDragDrop.
18679      * @param {Roo.dd.DragDrop} target The drop target
18680      * @param {Event} e The event object
18681      * @param {String} id The id of the dragged element
18682      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18683      */
18684     beforeDragDrop : function(target, e, id){
18685         return true;
18686     },
18687
18688     // private
18689     onValidDrop : function(target, e, id){
18690         this.hideProxy();
18691         if(this.afterValidDrop){
18692             /**
18693              * An empty function by default, but provided so that you can perform a custom action
18694              * after a valid drop has occurred by providing an implementation.
18695              * @param {Object} target The target DD 
18696              * @param {Event} e The event object
18697              * @param {String} id The id of the dropped element
18698              * @method afterInvalidDrop
18699              */
18700             this.afterValidDrop(target, e, id);
18701         }
18702     },
18703
18704     // private
18705     getRepairXY : function(e, data){
18706         return this.el.getXY();  
18707     },
18708
18709     // private
18710     onInvalidDrop : function(target, e, id){
18711         this.beforeInvalidDrop(target, e, id);
18712         if(this.cachedTarget){
18713             if(this.cachedTarget.isNotifyTarget){
18714                 this.cachedTarget.notifyOut(this, e, this.dragData);
18715             }
18716             this.cacheTarget = null;
18717         }
18718         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18719
18720         if(this.afterInvalidDrop){
18721             /**
18722              * An empty function by default, but provided so that you can perform a custom action
18723              * after an invalid drop has occurred by providing an implementation.
18724              * @param {Event} e The event object
18725              * @param {String} id The id of the dropped element
18726              * @method afterInvalidDrop
18727              */
18728             this.afterInvalidDrop(e, id);
18729         }
18730     },
18731
18732     // private
18733     afterRepair : function(){
18734         if(Roo.enableFx){
18735             this.el.highlight(this.hlColor || "c3daf9");
18736         }
18737         this.dragging = false;
18738     },
18739
18740     /**
18741      * An empty function by default, but provided so that you can perform a custom action after an invalid
18742      * drop has occurred.
18743      * @param {Roo.dd.DragDrop} target The drop target
18744      * @param {Event} e The event object
18745      * @param {String} id The id of the dragged element
18746      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18747      */
18748     beforeInvalidDrop : function(target, e, id){
18749         return true;
18750     },
18751
18752     // private
18753     handleMouseDown : function(e){
18754         if(this.dragging) {
18755             return;
18756         }
18757         var data = this.getDragData(e);
18758         if(data && this.onBeforeDrag(data, e) !== false){
18759             this.dragData = data;
18760             this.proxy.stop();
18761             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18762         } 
18763     },
18764
18765     /**
18766      * An empty function by default, but provided so that you can perform a custom action before the initial
18767      * drag event begins and optionally cancel it.
18768      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18769      * @param {Event} e The event object
18770      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18771      */
18772     onBeforeDrag : function(data, e){
18773         return true;
18774     },
18775
18776     /**
18777      * An empty function by default, but provided so that you can perform a custom action once the initial
18778      * drag event has begun.  The drag cannot be canceled from this function.
18779      * @param {Number} x The x position of the click on the dragged object
18780      * @param {Number} y The y position of the click on the dragged object
18781      */
18782     onStartDrag : Roo.emptyFn,
18783
18784     // private - YUI override
18785     startDrag : function(x, y){
18786         this.proxy.reset();
18787         this.dragging = true;
18788         this.proxy.update("");
18789         this.onInitDrag(x, y);
18790         this.proxy.show();
18791     },
18792
18793     // private
18794     onInitDrag : function(x, y){
18795         var clone = this.el.dom.cloneNode(true);
18796         clone.id = Roo.id(); // prevent duplicate ids
18797         this.proxy.update(clone);
18798         this.onStartDrag(x, y);
18799         return true;
18800     },
18801
18802     /**
18803      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18804      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18805      */
18806     getProxy : function(){
18807         return this.proxy;  
18808     },
18809
18810     /**
18811      * Hides the drag source's {@link Roo.dd.StatusProxy}
18812      */
18813     hideProxy : function(){
18814         this.proxy.hide();  
18815         this.proxy.reset(true);
18816         this.dragging = false;
18817     },
18818
18819     // private
18820     triggerCacheRefresh : function(){
18821         Roo.dd.DDM.refreshCache(this.groups);
18822     },
18823
18824     // private - override to prevent hiding
18825     b4EndDrag: function(e) {
18826     },
18827
18828     // private - override to prevent moving
18829     endDrag : function(e){
18830         this.onEndDrag(this.dragData, e);
18831     },
18832
18833     // private
18834     onEndDrag : function(data, e){
18835     },
18836     
18837     // private - pin to cursor
18838     autoOffset : function(x, y) {
18839         this.setDelta(-12, -20);
18840     }    
18841 });/*
18842  * Based on:
18843  * Ext JS Library 1.1.1
18844  * Copyright(c) 2006-2007, Ext JS, LLC.
18845  *
18846  * Originally Released Under LGPL - original licence link has changed is not relivant.
18847  *
18848  * Fork - LGPL
18849  * <script type="text/javascript">
18850  */
18851
18852
18853 /**
18854  * @class Roo.dd.DropTarget
18855  * @extends Roo.dd.DDTarget
18856  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18857  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18858  * @constructor
18859  * @param {String/HTMLElement/Element} el The container element
18860  * @param {Object} config
18861  */
18862 Roo.dd.DropTarget = function(el, config){
18863     this.el = Roo.get(el);
18864     
18865     var listeners = false; ;
18866     if (config && config.listeners) {
18867         listeners= config.listeners;
18868         delete config.listeners;
18869     }
18870     Roo.apply(this, config);
18871     
18872     if(this.containerScroll){
18873         Roo.dd.ScrollManager.register(this.el);
18874     }
18875     this.addEvents( {
18876          /**
18877          * @scope Roo.dd.DropTarget
18878          */
18879          
18880          /**
18881          * @event enter
18882          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18883          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18884          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18885          * 
18886          * IMPORTANT : it should set this.overClass and this.dropAllowed
18887          * 
18888          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18889          * @param {Event} e The event
18890          * @param {Object} data An object containing arbitrary data supplied by the drag source
18891          */
18892         "enter" : true,
18893         
18894          /**
18895          * @event over
18896          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18897          * This method will be called on every mouse movement while the drag source is over the drop target.
18898          * This default implementation simply returns the dropAllowed config value.
18899          * 
18900          * IMPORTANT : it should set this.dropAllowed
18901          * 
18902          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18903          * @param {Event} e The event
18904          * @param {Object} data An object containing arbitrary data supplied by the drag source
18905          
18906          */
18907         "over" : true,
18908         /**
18909          * @event out
18910          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18911          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18912          * overClass (if any) from the drop element.
18913          * 
18914          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18915          * @param {Event} e The event
18916          * @param {Object} data An object containing arbitrary data supplied by the drag source
18917          */
18918          "out" : true,
18919          
18920         /**
18921          * @event drop
18922          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18923          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18924          * implementation that does something to process the drop event and returns true so that the drag source's
18925          * repair action does not run.
18926          * 
18927          * IMPORTANT : it should set this.success
18928          * 
18929          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18930          * @param {Event} e The event
18931          * @param {Object} data An object containing arbitrary data supplied by the drag source
18932         */
18933          "drop" : true
18934     });
18935             
18936      
18937     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18938         this.el.dom, 
18939         this.ddGroup || this.group,
18940         {
18941             isTarget: true,
18942             listeners : listeners || {} 
18943            
18944         
18945         }
18946     );
18947
18948 };
18949
18950 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18951     /**
18952      * @cfg {String} overClass
18953      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18954      */
18955      /**
18956      * @cfg {String} ddGroup
18957      * The drag drop group to handle drop events for
18958      */
18959      
18960     /**
18961      * @cfg {String} dropAllowed
18962      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18963      */
18964     dropAllowed : "x-dd-drop-ok",
18965     /**
18966      * @cfg {String} dropNotAllowed
18967      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18968      */
18969     dropNotAllowed : "x-dd-drop-nodrop",
18970     /**
18971      * @cfg {boolean} success
18972      * set this after drop listener.. 
18973      */
18974     success : false,
18975     /**
18976      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18977      * if the drop point is valid for over/enter..
18978      */
18979     valid : false,
18980     // private
18981     isTarget : true,
18982
18983     // private
18984     isNotifyTarget : true,
18985     
18986     /**
18987      * @hide
18988      */
18989     notifyEnter : function(dd, e, data)
18990     {
18991         this.valid = true;
18992         this.fireEvent('enter', dd, e, data);
18993         if(this.overClass){
18994             this.el.addClass(this.overClass);
18995         }
18996         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18997             this.valid ? this.dropAllowed : this.dropNotAllowed
18998         );
18999     },
19000
19001     /**
19002      * @hide
19003      */
19004     notifyOver : function(dd, e, data)
19005     {
19006         this.valid = true;
19007         this.fireEvent('over', dd, e, data);
19008         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
19009             this.valid ? this.dropAllowed : this.dropNotAllowed
19010         );
19011     },
19012
19013     /**
19014      * @hide
19015      */
19016     notifyOut : function(dd, e, data)
19017     {
19018         this.fireEvent('out', dd, e, data);
19019         if(this.overClass){
19020             this.el.removeClass(this.overClass);
19021         }
19022     },
19023
19024     /**
19025      * @hide
19026      */
19027     notifyDrop : function(dd, e, data)
19028     {
19029         this.success = false;
19030         this.fireEvent('drop', dd, e, data);
19031         return this.success;
19032     }
19033 });/*
19034  * Based on:
19035  * Ext JS Library 1.1.1
19036  * Copyright(c) 2006-2007, Ext JS, LLC.
19037  *
19038  * Originally Released Under LGPL - original licence link has changed is not relivant.
19039  *
19040  * Fork - LGPL
19041  * <script type="text/javascript">
19042  */
19043
19044
19045 /**
19046  * @class Roo.dd.DragZone
19047  * @extends Roo.dd.DragSource
19048  * This class provides a container DD instance that proxies for multiple child node sources.<br />
19049  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
19050  * @constructor
19051  * @param {String/HTMLElement/Element} el The container element
19052  * @param {Object} config
19053  */
19054 Roo.dd.DragZone = function(el, config){
19055     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
19056     if(this.containerScroll){
19057         Roo.dd.ScrollManager.register(this.el);
19058     }
19059 };
19060
19061 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
19062     /**
19063      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
19064      * for auto scrolling during drag operations.
19065      */
19066     /**
19067      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
19068      * method after a failed drop (defaults to "c3daf9" - light blue)
19069      */
19070
19071     /**
19072      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
19073      * for a valid target to drag based on the mouse down. Override this method
19074      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
19075      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
19076      * @param {EventObject} e The mouse down event
19077      * @return {Object} The dragData
19078      */
19079     getDragData : function(e){
19080         return Roo.dd.Registry.getHandleFromEvent(e);
19081     },
19082     
19083     /**
19084      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
19085      * this.dragData.ddel
19086      * @param {Number} x The x position of the click on the dragged object
19087      * @param {Number} y The y position of the click on the dragged object
19088      * @return {Boolean} true to continue the drag, false to cancel
19089      */
19090     onInitDrag : function(x, y){
19091         this.proxy.update(this.dragData.ddel.cloneNode(true));
19092         this.onStartDrag(x, y);
19093         return true;
19094     },
19095     
19096     /**
19097      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
19098      */
19099     afterRepair : function(){
19100         if(Roo.enableFx){
19101             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
19102         }
19103         this.dragging = false;
19104     },
19105
19106     /**
19107      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
19108      * the XY of this.dragData.ddel
19109      * @param {EventObject} e The mouse up event
19110      * @return {Array} The xy location (e.g. [100, 200])
19111      */
19112     getRepairXY : function(e){
19113         return Roo.Element.fly(this.dragData.ddel).getXY();  
19114     }
19115 });/*
19116  * Based on:
19117  * Ext JS Library 1.1.1
19118  * Copyright(c) 2006-2007, Ext JS, LLC.
19119  *
19120  * Originally Released Under LGPL - original licence link has changed is not relivant.
19121  *
19122  * Fork - LGPL
19123  * <script type="text/javascript">
19124  */
19125 /**
19126  * @class Roo.dd.DropZone
19127  * @extends Roo.dd.DropTarget
19128  * This class provides a container DD instance that proxies for multiple child node targets.<br />
19129  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
19130  * @constructor
19131  * @param {String/HTMLElement/Element} el The container element
19132  * @param {Object} config
19133  */
19134 Roo.dd.DropZone = function(el, config){
19135     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
19136 };
19137
19138 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
19139     /**
19140      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
19141      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
19142      * provide your own custom lookup.
19143      * @param {Event} e The event
19144      * @return {Object} data The custom data
19145      */
19146     getTargetFromEvent : function(e){
19147         return Roo.dd.Registry.getTargetFromEvent(e);
19148     },
19149
19150     /**
19151      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
19152      * that it has registered.  This method has no default implementation and should be overridden to provide
19153      * node-specific processing if necessary.
19154      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
19155      * {@link #getTargetFromEvent} for this node)
19156      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19157      * @param {Event} e The event
19158      * @param {Object} data An object containing arbitrary data supplied by the drag source
19159      */
19160     onNodeEnter : function(n, dd, e, data){
19161         
19162     },
19163
19164     /**
19165      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
19166      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
19167      * overridden to provide the proper feedback.
19168      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19169      * {@link #getTargetFromEvent} for this node)
19170      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19171      * @param {Event} e The event
19172      * @param {Object} data An object containing arbitrary data supplied by the drag source
19173      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19174      * underlying {@link Roo.dd.StatusProxy} can be updated
19175      */
19176     onNodeOver : function(n, dd, e, data){
19177         return this.dropAllowed;
19178     },
19179
19180     /**
19181      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
19182      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
19183      * node-specific processing if necessary.
19184      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19185      * {@link #getTargetFromEvent} for this node)
19186      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19187      * @param {Event} e The event
19188      * @param {Object} data An object containing arbitrary data supplied by the drag source
19189      */
19190     onNodeOut : function(n, dd, e, data){
19191         
19192     },
19193
19194     /**
19195      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
19196      * the drop node.  The default implementation returns false, so it should be overridden to provide the
19197      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
19198      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
19199      * {@link #getTargetFromEvent} for this node)
19200      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19201      * @param {Event} e The event
19202      * @param {Object} data An object containing arbitrary data supplied by the drag source
19203      * @return {Boolean} True if the drop was valid, else false
19204      */
19205     onNodeDrop : function(n, dd, e, data){
19206         return false;
19207     },
19208
19209     /**
19210      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
19211      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
19212      * it should be overridden to provide the proper feedback if necessary.
19213      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19214      * @param {Event} e The event
19215      * @param {Object} data An object containing arbitrary data supplied by the drag source
19216      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19217      * underlying {@link Roo.dd.StatusProxy} can be updated
19218      */
19219     onContainerOver : function(dd, e, data){
19220         return this.dropNotAllowed;
19221     },
19222
19223     /**
19224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
19225      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
19226      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
19227      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
19228      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19229      * @param {Event} e The event
19230      * @param {Object} data An object containing arbitrary data supplied by the drag source
19231      * @return {Boolean} True if the drop was valid, else false
19232      */
19233     onContainerDrop : function(dd, e, data){
19234         return false;
19235     },
19236
19237     /**
19238      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
19239      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
19240      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
19241      * you should override this method and provide a custom implementation.
19242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19243      * @param {Event} e The event
19244      * @param {Object} data An object containing arbitrary data supplied by the drag source
19245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19246      * underlying {@link Roo.dd.StatusProxy} can be updated
19247      */
19248     notifyEnter : function(dd, e, data){
19249         return this.dropNotAllowed;
19250     },
19251
19252     /**
19253      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
19254      * This method will be called on every mouse movement while the drag source is over the drop zone.
19255      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
19256      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
19257      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
19258      * registered node, it will call {@link #onContainerOver}.
19259      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19260      * @param {Event} e The event
19261      * @param {Object} data An object containing arbitrary data supplied by the drag source
19262      * @return {String} status The CSS class that communicates the drop status back to the source so that the
19263      * underlying {@link Roo.dd.StatusProxy} can be updated
19264      */
19265     notifyOver : function(dd, e, data){
19266         var n = this.getTargetFromEvent(e);
19267         if(!n){ // not over valid drop target
19268             if(this.lastOverNode){
19269                 this.onNodeOut(this.lastOverNode, dd, e, data);
19270                 this.lastOverNode = null;
19271             }
19272             return this.onContainerOver(dd, e, data);
19273         }
19274         if(this.lastOverNode != n){
19275             if(this.lastOverNode){
19276                 this.onNodeOut(this.lastOverNode, dd, e, data);
19277             }
19278             this.onNodeEnter(n, dd, e, data);
19279             this.lastOverNode = n;
19280         }
19281         return this.onNodeOver(n, dd, e, data);
19282     },
19283
19284     /**
19285      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
19286      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
19287      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
19288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
19289      * @param {Event} e The event
19290      * @param {Object} data An object containing arbitrary data supplied by the drag zone
19291      */
19292     notifyOut : function(dd, e, data){
19293         if(this.lastOverNode){
19294             this.onNodeOut(this.lastOverNode, dd, e, data);
19295             this.lastOverNode = null;
19296         }
19297     },
19298
19299     /**
19300      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
19301      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
19302      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
19303      * otherwise it will call {@link #onContainerDrop}.
19304      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
19305      * @param {Event} e The event
19306      * @param {Object} data An object containing arbitrary data supplied by the drag source
19307      * @return {Boolean} True if the drop was valid, else false
19308      */
19309     notifyDrop : function(dd, e, data){
19310         if(this.lastOverNode){
19311             this.onNodeOut(this.lastOverNode, dd, e, data);
19312             this.lastOverNode = null;
19313         }
19314         var n = this.getTargetFromEvent(e);
19315         return n ?
19316             this.onNodeDrop(n, dd, e, data) :
19317             this.onContainerDrop(dd, e, data);
19318     },
19319
19320     // private
19321     triggerCacheRefresh : function(){
19322         Roo.dd.DDM.refreshCache(this.groups);
19323     }  
19324 });/*
19325  * Based on:
19326  * Ext JS Library 1.1.1
19327  * Copyright(c) 2006-2007, Ext JS, LLC.
19328  *
19329  * Originally Released Under LGPL - original licence link has changed is not relivant.
19330  *
19331  * Fork - LGPL
19332  * <script type="text/javascript">
19333  */
19334
19335
19336 /**
19337  * @class Roo.data.SortTypes
19338  * @singleton
19339  * Defines the default sorting (casting?) comparison functions used when sorting data.
19340  */
19341 Roo.data.SortTypes = {
19342     /**
19343      * Default sort that does nothing
19344      * @param {Mixed} s The value being converted
19345      * @return {Mixed} The comparison value
19346      */
19347     none : function(s){
19348         return s;
19349     },
19350     
19351     /**
19352      * The regular expression used to strip tags
19353      * @type {RegExp}
19354      * @property
19355      */
19356     stripTagsRE : /<\/?[^>]+>/gi,
19357     
19358     /**
19359      * Strips all HTML tags to sort on text only
19360      * @param {Mixed} s The value being converted
19361      * @return {String} The comparison value
19362      */
19363     asText : function(s){
19364         return String(s).replace(this.stripTagsRE, "");
19365     },
19366     
19367     /**
19368      * Strips all HTML tags to sort on text only - Case insensitive
19369      * @param {Mixed} s The value being converted
19370      * @return {String} The comparison value
19371      */
19372     asUCText : function(s){
19373         return String(s).toUpperCase().replace(this.stripTagsRE, "");
19374     },
19375     
19376     /**
19377      * Case insensitive string
19378      * @param {Mixed} s The value being converted
19379      * @return {String} The comparison value
19380      */
19381     asUCString : function(s) {
19382         return String(s).toUpperCase();
19383     },
19384     
19385     /**
19386      * Date sorting
19387      * @param {Mixed} s The value being converted
19388      * @return {Number} The comparison value
19389      */
19390     asDate : function(s) {
19391         if(!s){
19392             return 0;
19393         }
19394         if(s instanceof Date){
19395             return s.getTime();
19396         }
19397         return Date.parse(String(s));
19398     },
19399     
19400     /**
19401      * Float sorting
19402      * @param {Mixed} s The value being converted
19403      * @return {Float} The comparison value
19404      */
19405     asFloat : function(s) {
19406         var val = parseFloat(String(s).replace(/,/g, ""));
19407         if(isNaN(val)) val = 0;
19408         return val;
19409     },
19410     
19411     /**
19412      * Integer sorting
19413      * @param {Mixed} s The value being converted
19414      * @return {Number} The comparison value
19415      */
19416     asInt : function(s) {
19417         var val = parseInt(String(s).replace(/,/g, ""));
19418         if(isNaN(val)) val = 0;
19419         return val;
19420     }
19421 };/*
19422  * Based on:
19423  * Ext JS Library 1.1.1
19424  * Copyright(c) 2006-2007, Ext JS, LLC.
19425  *
19426  * Originally Released Under LGPL - original licence link has changed is not relivant.
19427  *
19428  * Fork - LGPL
19429  * <script type="text/javascript">
19430  */
19431
19432 /**
19433 * @class Roo.data.Record
19434  * Instances of this class encapsulate both record <em>definition</em> information, and record
19435  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
19436  * to access Records cached in an {@link Roo.data.Store} object.<br>
19437  * <p>
19438  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
19439  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
19440  * objects.<br>
19441  * <p>
19442  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
19443  * @constructor
19444  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
19445  * {@link #create}. The parameters are the same.
19446  * @param {Array} data An associative Array of data values keyed by the field name.
19447  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
19448  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
19449  * not specified an integer id is generated.
19450  */
19451 Roo.data.Record = function(data, id){
19452     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
19453     this.data = data;
19454 };
19455
19456 /**
19457  * Generate a constructor for a specific record layout.
19458  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
19459  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
19460  * Each field definition object may contain the following properties: <ul>
19461  * <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,
19462  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
19463  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
19464  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
19465  * is being used, then this is a string containing the javascript expression to reference the data relative to 
19466  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
19467  * to the data item relative to the record element. If the mapping expression is the same as the field name,
19468  * this may be omitted.</p></li>
19469  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
19470  * <ul><li>auto (Default, implies no conversion)</li>
19471  * <li>string</li>
19472  * <li>int</li>
19473  * <li>float</li>
19474  * <li>boolean</li>
19475  * <li>date</li></ul></p></li>
19476  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
19477  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19478  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19479  * by the Reader into an object that will be stored in the Record. It is passed the
19480  * following parameters:<ul>
19481  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19482  * </ul></p></li>
19483  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19484  * </ul>
19485  * <br>usage:<br><pre><code>
19486 var TopicRecord = Roo.data.Record.create(
19487     {name: 'title', mapping: 'topic_title'},
19488     {name: 'author', mapping: 'username'},
19489     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19490     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19491     {name: 'lastPoster', mapping: 'user2'},
19492     {name: 'excerpt', mapping: 'post_text'}
19493 );
19494
19495 var myNewRecord = new TopicRecord({
19496     title: 'Do my job please',
19497     author: 'noobie',
19498     totalPosts: 1,
19499     lastPost: new Date(),
19500     lastPoster: 'Animal',
19501     excerpt: 'No way dude!'
19502 });
19503 myStore.add(myNewRecord);
19504 </code></pre>
19505  * @method create
19506  * @static
19507  */
19508 Roo.data.Record.create = function(o){
19509     var f = function(){
19510         f.superclass.constructor.apply(this, arguments);
19511     };
19512     Roo.extend(f, Roo.data.Record);
19513     var p = f.prototype;
19514     p.fields = new Roo.util.MixedCollection(false, function(field){
19515         return field.name;
19516     });
19517     for(var i = 0, len = o.length; i < len; i++){
19518         p.fields.add(new Roo.data.Field(o[i]));
19519     }
19520     f.getField = function(name){
19521         return p.fields.get(name);  
19522     };
19523     return f;
19524 };
19525
19526 Roo.data.Record.AUTO_ID = 1000;
19527 Roo.data.Record.EDIT = 'edit';
19528 Roo.data.Record.REJECT = 'reject';
19529 Roo.data.Record.COMMIT = 'commit';
19530
19531 Roo.data.Record.prototype = {
19532     /**
19533      * Readonly flag - true if this record has been modified.
19534      * @type Boolean
19535      */
19536     dirty : false,
19537     editing : false,
19538     error: null,
19539     modified: null,
19540
19541     // private
19542     join : function(store){
19543         this.store = store;
19544     },
19545
19546     /**
19547      * Set the named field to the specified value.
19548      * @param {String} name The name of the field to set.
19549      * @param {Object} value The value to set the field to.
19550      */
19551     set : function(name, value){
19552         if(this.data[name] == value){
19553             return;
19554         }
19555         this.dirty = true;
19556         if(!this.modified){
19557             this.modified = {};
19558         }
19559         if(typeof this.modified[name] == 'undefined'){
19560             this.modified[name] = this.data[name];
19561         }
19562         this.data[name] = value;
19563         if(!this.editing && this.store){
19564             this.store.afterEdit(this);
19565         }       
19566     },
19567
19568     /**
19569      * Get the value of the named field.
19570      * @param {String} name The name of the field to get the value of.
19571      * @return {Object} The value of the field.
19572      */
19573     get : function(name){
19574         return this.data[name]; 
19575     },
19576
19577     // private
19578     beginEdit : function(){
19579         this.editing = true;
19580         this.modified = {}; 
19581     },
19582
19583     // private
19584     cancelEdit : function(){
19585         this.editing = false;
19586         delete this.modified;
19587     },
19588
19589     // private
19590     endEdit : function(){
19591         this.editing = false;
19592         if(this.dirty && this.store){
19593             this.store.afterEdit(this);
19594         }
19595     },
19596
19597     /**
19598      * Usually called by the {@link Roo.data.Store} which owns the Record.
19599      * Rejects all changes made to the Record since either creation, or the last commit operation.
19600      * Modified fields are reverted to their original values.
19601      * <p>
19602      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19603      * of reject operations.
19604      */
19605     reject : function(){
19606         var m = this.modified;
19607         for(var n in m){
19608             if(typeof m[n] != "function"){
19609                 this.data[n] = m[n];
19610             }
19611         }
19612         this.dirty = false;
19613         delete this.modified;
19614         this.editing = false;
19615         if(this.store){
19616             this.store.afterReject(this);
19617         }
19618     },
19619
19620     /**
19621      * Usually called by the {@link Roo.data.Store} which owns the Record.
19622      * Commits all changes made to the Record since either creation, or the last commit operation.
19623      * <p>
19624      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19625      * of commit operations.
19626      */
19627     commit : function(){
19628         this.dirty = false;
19629         delete this.modified;
19630         this.editing = false;
19631         if(this.store){
19632             this.store.afterCommit(this);
19633         }
19634     },
19635
19636     // private
19637     hasError : function(){
19638         return this.error != null;
19639     },
19640
19641     // private
19642     clearError : function(){
19643         this.error = null;
19644     },
19645
19646     /**
19647      * Creates a copy of this record.
19648      * @param {String} id (optional) A new record id if you don't want to use this record's id
19649      * @return {Record}
19650      */
19651     copy : function(newId) {
19652         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19653     }
19654 };/*
19655  * Based on:
19656  * Ext JS Library 1.1.1
19657  * Copyright(c) 2006-2007, Ext JS, LLC.
19658  *
19659  * Originally Released Under LGPL - original licence link has changed is not relivant.
19660  *
19661  * Fork - LGPL
19662  * <script type="text/javascript">
19663  */
19664
19665
19666
19667 /**
19668  * @class Roo.data.Store
19669  * @extends Roo.util.Observable
19670  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19671  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19672  * <p>
19673  * 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
19674  * has no knowledge of the format of the data returned by the Proxy.<br>
19675  * <p>
19676  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19677  * instances from the data object. These records are cached and made available through accessor functions.
19678  * @constructor
19679  * Creates a new Store.
19680  * @param {Object} config A config object containing the objects needed for the Store to access data,
19681  * and read the data into Records.
19682  */
19683 Roo.data.Store = function(config){
19684     this.data = new Roo.util.MixedCollection(false);
19685     this.data.getKey = function(o){
19686         return o.id;
19687     };
19688     this.baseParams = {};
19689     // private
19690     this.paramNames = {
19691         "start" : "start",
19692         "limit" : "limit",
19693         "sort" : "sort",
19694         "dir" : "dir",
19695         "multisort" : "_multisort"
19696     };
19697
19698     if(config && config.data){
19699         this.inlineData = config.data;
19700         delete config.data;
19701     }
19702
19703     Roo.apply(this, config);
19704     
19705     if(this.reader){ // reader passed
19706         this.reader = Roo.factory(this.reader, Roo.data);
19707         this.reader.xmodule = this.xmodule || false;
19708         if(!this.recordType){
19709             this.recordType = this.reader.recordType;
19710         }
19711         if(this.reader.onMetaChange){
19712             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19713         }
19714     }
19715
19716     if(this.recordType){
19717         this.fields = this.recordType.prototype.fields;
19718     }
19719     this.modified = [];
19720
19721     this.addEvents({
19722         /**
19723          * @event datachanged
19724          * Fires when the data cache has changed, and a widget which is using this Store
19725          * as a Record cache should refresh its view.
19726          * @param {Store} this
19727          */
19728         datachanged : true,
19729         /**
19730          * @event metachange
19731          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19732          * @param {Store} this
19733          * @param {Object} meta The JSON metadata
19734          */
19735         metachange : true,
19736         /**
19737          * @event add
19738          * Fires when Records have been added to the Store
19739          * @param {Store} this
19740          * @param {Roo.data.Record[]} records The array of Records added
19741          * @param {Number} index The index at which the record(s) were added
19742          */
19743         add : true,
19744         /**
19745          * @event remove
19746          * Fires when a Record has been removed from the Store
19747          * @param {Store} this
19748          * @param {Roo.data.Record} record The Record that was removed
19749          * @param {Number} index The index at which the record was removed
19750          */
19751         remove : true,
19752         /**
19753          * @event update
19754          * Fires when a Record has been updated
19755          * @param {Store} this
19756          * @param {Roo.data.Record} record The Record that was updated
19757          * @param {String} operation The update operation being performed.  Value may be one of:
19758          * <pre><code>
19759  Roo.data.Record.EDIT
19760  Roo.data.Record.REJECT
19761  Roo.data.Record.COMMIT
19762          * </code></pre>
19763          */
19764         update : true,
19765         /**
19766          * @event clear
19767          * Fires when the data cache has been cleared.
19768          * @param {Store} this
19769          */
19770         clear : true,
19771         /**
19772          * @event beforeload
19773          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19774          * the load action will be canceled.
19775          * @param {Store} this
19776          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19777          */
19778         beforeload : true,
19779         /**
19780          * @event beforeloadadd
19781          * Fires after a new set of Records has been loaded.
19782          * @param {Store} this
19783          * @param {Roo.data.Record[]} records The Records that were loaded
19784          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19785          */
19786         beforeloadadd : true,
19787         /**
19788          * @event load
19789          * Fires after a new set of Records has been loaded, before they are added to the store.
19790          * @param {Store} this
19791          * @param {Roo.data.Record[]} records The Records that were loaded
19792          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19793          * @params {Object} return from reader
19794          */
19795         load : true,
19796         /**
19797          * @event loadexception
19798          * Fires if an exception occurs in the Proxy during loading.
19799          * Called with the signature of the Proxy's "loadexception" event.
19800          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19801          * 
19802          * @param {Proxy} 
19803          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19804          * @param {Object} load options 
19805          * @param {Object} jsonData from your request (normally this contains the Exception)
19806          */
19807         loadexception : true
19808     });
19809     
19810     if(this.proxy){
19811         this.proxy = Roo.factory(this.proxy, Roo.data);
19812         this.proxy.xmodule = this.xmodule || false;
19813         this.relayEvents(this.proxy,  ["loadexception"]);
19814     }
19815     this.sortToggle = {};
19816     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19817
19818     Roo.data.Store.superclass.constructor.call(this);
19819
19820     if(this.inlineData){
19821         this.loadData(this.inlineData);
19822         delete this.inlineData;
19823     }
19824 };
19825
19826 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19827      /**
19828     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19829     * without a remote query - used by combo/forms at present.
19830     */
19831     
19832     /**
19833     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19834     */
19835     /**
19836     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19837     */
19838     /**
19839     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19840     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19841     */
19842     /**
19843     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19844     * on any HTTP request
19845     */
19846     /**
19847     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19848     */
19849     /**
19850     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19851     */
19852     multiSort: false,
19853     /**
19854     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19855     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19856     */
19857     remoteSort : false,
19858
19859     /**
19860     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19861      * loaded or when a record is removed. (defaults to false).
19862     */
19863     pruneModifiedRecords : false,
19864
19865     // private
19866     lastOptions : null,
19867
19868     /**
19869      * Add Records to the Store and fires the add event.
19870      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19871      */
19872     add : function(records){
19873         records = [].concat(records);
19874         for(var i = 0, len = records.length; i < len; i++){
19875             records[i].join(this);
19876         }
19877         var index = this.data.length;
19878         this.data.addAll(records);
19879         this.fireEvent("add", this, records, index);
19880     },
19881
19882     /**
19883      * Remove a Record from the Store and fires the remove event.
19884      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19885      */
19886     remove : function(record){
19887         var index = this.data.indexOf(record);
19888         this.data.removeAt(index);
19889         if(this.pruneModifiedRecords){
19890             this.modified.remove(record);
19891         }
19892         this.fireEvent("remove", this, record, index);
19893     },
19894
19895     /**
19896      * Remove all Records from the Store and fires the clear event.
19897      */
19898     removeAll : function(){
19899         this.data.clear();
19900         if(this.pruneModifiedRecords){
19901             this.modified = [];
19902         }
19903         this.fireEvent("clear", this);
19904     },
19905
19906     /**
19907      * Inserts Records to the Store at the given index and fires the add event.
19908      * @param {Number} index The start index at which to insert the passed Records.
19909      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19910      */
19911     insert : function(index, records){
19912         records = [].concat(records);
19913         for(var i = 0, len = records.length; i < len; i++){
19914             this.data.insert(index, records[i]);
19915             records[i].join(this);
19916         }
19917         this.fireEvent("add", this, records, index);
19918     },
19919
19920     /**
19921      * Get the index within the cache of the passed Record.
19922      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19923      * @return {Number} The index of the passed Record. Returns -1 if not found.
19924      */
19925     indexOf : function(record){
19926         return this.data.indexOf(record);
19927     },
19928
19929     /**
19930      * Get the index within the cache of the Record with the passed id.
19931      * @param {String} id The id of the Record to find.
19932      * @return {Number} The index of the Record. Returns -1 if not found.
19933      */
19934     indexOfId : function(id){
19935         return this.data.indexOfKey(id);
19936     },
19937
19938     /**
19939      * Get the Record with the specified id.
19940      * @param {String} id The id of the Record to find.
19941      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19942      */
19943     getById : function(id){
19944         return this.data.key(id);
19945     },
19946
19947     /**
19948      * Get the Record at the specified index.
19949      * @param {Number} index The index of the Record to find.
19950      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19951      */
19952     getAt : function(index){
19953         return this.data.itemAt(index);
19954     },
19955
19956     /**
19957      * Returns a range of Records between specified indices.
19958      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19959      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19960      * @return {Roo.data.Record[]} An array of Records
19961      */
19962     getRange : function(start, end){
19963         return this.data.getRange(start, end);
19964     },
19965
19966     // private
19967     storeOptions : function(o){
19968         o = Roo.apply({}, o);
19969         delete o.callback;
19970         delete o.scope;
19971         this.lastOptions = o;
19972     },
19973
19974     /**
19975      * Loads the Record cache from the configured Proxy using the configured Reader.
19976      * <p>
19977      * If using remote paging, then the first load call must specify the <em>start</em>
19978      * and <em>limit</em> properties in the options.params property to establish the initial
19979      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19980      * <p>
19981      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19982      * and this call will return before the new data has been loaded. Perform any post-processing
19983      * in a callback function, or in a "load" event handler.</strong>
19984      * <p>
19985      * @param {Object} options An object containing properties which control loading options:<ul>
19986      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19987      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19988      * passed the following arguments:<ul>
19989      * <li>r : Roo.data.Record[]</li>
19990      * <li>options: Options object from the load call</li>
19991      * <li>success: Boolean success indicator</li></ul></li>
19992      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19993      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19994      * </ul>
19995      */
19996     load : function(options){
19997         options = options || {};
19998         if(this.fireEvent("beforeload", this, options) !== false){
19999             this.storeOptions(options);
20000             var p = Roo.apply(options.params || {}, this.baseParams);
20001             // if meta was not loaded from remote source.. try requesting it.
20002             if (!this.reader.metaFromRemote) {
20003                 p._requestMeta = 1;
20004             }
20005             if(this.sortInfo && this.remoteSort){
20006                 var pn = this.paramNames;
20007                 p[pn["sort"]] = this.sortInfo.field;
20008                 p[pn["dir"]] = this.sortInfo.direction;
20009             }
20010             if (this.multiSort) {
20011                 var pn = this.paramNames;
20012                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
20013             }
20014             
20015             this.proxy.load(p, this.reader, this.loadRecords, this, options);
20016         }
20017     },
20018
20019     /**
20020      * Reloads the Record cache from the configured Proxy using the configured Reader and
20021      * the options from the last load operation performed.
20022      * @param {Object} options (optional) An object containing properties which may override the options
20023      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
20024      * the most recently used options are reused).
20025      */
20026     reload : function(options){
20027         this.load(Roo.applyIf(options||{}, this.lastOptions));
20028     },
20029
20030     // private
20031     // Called as a callback by the Reader during a load operation.
20032     loadRecords : function(o, options, success){
20033         if(!o || success === false){
20034             if(success !== false){
20035                 this.fireEvent("load", this, [], options, o);
20036             }
20037             if(options.callback){
20038                 options.callback.call(options.scope || this, [], options, false);
20039             }
20040             return;
20041         }
20042         // if data returned failure - throw an exception.
20043         if (o.success === false) {
20044             // show a message if no listener is registered.
20045             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
20046                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
20047             }
20048             // loadmask wil be hooked into this..
20049             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
20050             return;
20051         }
20052         var r = o.records, t = o.totalRecords || r.length;
20053         
20054         this.fireEvent("beforeloadadd", this, r, options, o);
20055         
20056         if(!options || options.add !== true){
20057             if(this.pruneModifiedRecords){
20058                 this.modified = [];
20059             }
20060             for(var i = 0, len = r.length; i < len; i++){
20061                 r[i].join(this);
20062             }
20063             if(this.snapshot){
20064                 this.data = this.snapshot;
20065                 delete this.snapshot;
20066             }
20067             this.data.clear();
20068             this.data.addAll(r);
20069             this.totalLength = t;
20070             this.applySort();
20071             this.fireEvent("datachanged", this);
20072         }else{
20073             this.totalLength = Math.max(t, this.data.length+r.length);
20074             this.add(r);
20075         }
20076         this.fireEvent("load", this, r, options, o);
20077         if(options.callback){
20078             options.callback.call(options.scope || this, r, options, true);
20079         }
20080     },
20081
20082
20083     /**
20084      * Loads data from a passed data block. A Reader which understands the format of the data
20085      * must have been configured in the constructor.
20086      * @param {Object} data The data block from which to read the Records.  The format of the data expected
20087      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
20088      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
20089      */
20090     loadData : function(o, append){
20091         var r = this.reader.readRecords(o);
20092         this.loadRecords(r, {add: append}, true);
20093     },
20094
20095     /**
20096      * Gets the number of cached records.
20097      * <p>
20098      * <em>If using paging, this may not be the total size of the dataset. If the data object
20099      * used by the Reader contains the dataset size, then the getTotalCount() function returns
20100      * the data set size</em>
20101      */
20102     getCount : function(){
20103         return this.data.length || 0;
20104     },
20105
20106     /**
20107      * Gets the total number of records in the dataset as returned by the server.
20108      * <p>
20109      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
20110      * the dataset size</em>
20111      */
20112     getTotalCount : function(){
20113         return this.totalLength || 0;
20114     },
20115
20116     /**
20117      * Returns the sort state of the Store as an object with two properties:
20118      * <pre><code>
20119  field {String} The name of the field by which the Records are sorted
20120  direction {String} The sort order, "ASC" or "DESC"
20121      * </code></pre>
20122      */
20123     getSortState : function(){
20124         return this.sortInfo;
20125     },
20126
20127     // private
20128     applySort : function(){
20129         if(this.sortInfo && !this.remoteSort){
20130             var s = this.sortInfo, f = s.field;
20131             var st = this.fields.get(f).sortType;
20132             var fn = function(r1, r2){
20133                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
20134                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
20135             };
20136             this.data.sort(s.direction, fn);
20137             if(this.snapshot && this.snapshot != this.data){
20138                 this.snapshot.sort(s.direction, fn);
20139             }
20140         }
20141     },
20142
20143     /**
20144      * Sets the default sort column and order to be used by the next load operation.
20145      * @param {String} fieldName The name of the field to sort by.
20146      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
20147      */
20148     setDefaultSort : function(field, dir){
20149         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
20150     },
20151
20152     /**
20153      * Sort the Records.
20154      * If remote sorting is used, the sort is performed on the server, and the cache is
20155      * reloaded. If local sorting is used, the cache is sorted internally.
20156      * @param {String} fieldName The name of the field to sort by.
20157      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
20158      */
20159     sort : function(fieldName, dir){
20160         var f = this.fields.get(fieldName);
20161         if(!dir){
20162             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
20163             
20164             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
20165                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
20166             }else{
20167                 dir = f.sortDir;
20168             }
20169         }
20170         this.sortToggle[f.name] = dir;
20171         this.sortInfo = {field: f.name, direction: dir};
20172         if(!this.remoteSort){
20173             this.applySort();
20174             this.fireEvent("datachanged", this);
20175         }else{
20176             this.load(this.lastOptions);
20177         }
20178     },
20179
20180     /**
20181      * Calls the specified function for each of the Records in the cache.
20182      * @param {Function} fn The function to call. The Record is passed as the first parameter.
20183      * Returning <em>false</em> aborts and exits the iteration.
20184      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
20185      */
20186     each : function(fn, scope){
20187         this.data.each(fn, scope);
20188     },
20189
20190     /**
20191      * Gets all records modified since the last commit.  Modified records are persisted across load operations
20192      * (e.g., during paging).
20193      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
20194      */
20195     getModifiedRecords : function(){
20196         return this.modified;
20197     },
20198
20199     // private
20200     createFilterFn : function(property, value, anyMatch){
20201         if(!value.exec){ // not a regex
20202             value = String(value);
20203             if(value.length == 0){
20204                 return false;
20205             }
20206             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
20207         }
20208         return function(r){
20209             return value.test(r.data[property]);
20210         };
20211     },
20212
20213     /**
20214      * Sums the value of <i>property</i> for each record between start and end and returns the result.
20215      * @param {String} property A field on your records
20216      * @param {Number} start The record index to start at (defaults to 0)
20217      * @param {Number} end The last record index to include (defaults to length - 1)
20218      * @return {Number} The sum
20219      */
20220     sum : function(property, start, end){
20221         var rs = this.data.items, v = 0;
20222         start = start || 0;
20223         end = (end || end === 0) ? end : rs.length-1;
20224
20225         for(var i = start; i <= end; i++){
20226             v += (rs[i].data[property] || 0);
20227         }
20228         return v;
20229     },
20230
20231     /**
20232      * Filter the records by a specified property.
20233      * @param {String} field A field on your records
20234      * @param {String/RegExp} value Either a string that the field
20235      * should start with or a RegExp to test against the field
20236      * @param {Boolean} anyMatch True to match any part not just the beginning
20237      */
20238     filter : function(property, value, anyMatch){
20239         var fn = this.createFilterFn(property, value, anyMatch);
20240         return fn ? this.filterBy(fn) : this.clearFilter();
20241     },
20242
20243     /**
20244      * Filter by a function. The specified function will be called with each
20245      * record in this data source. If the function returns true the record is included,
20246      * otherwise it is filtered.
20247      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
20248      * @param {Object} scope (optional) The scope of the function (defaults to this)
20249      */
20250     filterBy : function(fn, scope){
20251         this.snapshot = this.snapshot || this.data;
20252         this.data = this.queryBy(fn, scope||this);
20253         this.fireEvent("datachanged", this);
20254     },
20255
20256     /**
20257      * Query the records by a specified property.
20258      * @param {String} field A field on your records
20259      * @param {String/RegExp} value Either a string that the field
20260      * should start with or a RegExp to test against the field
20261      * @param {Boolean} anyMatch True to match any part not just the beginning
20262      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
20263      */
20264     query : function(property, value, anyMatch){
20265         var fn = this.createFilterFn(property, value, anyMatch);
20266         return fn ? this.queryBy(fn) : this.data.clone();
20267     },
20268
20269     /**
20270      * Query by a function. The specified function will be called with each
20271      * record in this data source. If the function returns true the record is included
20272      * in the results.
20273      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
20274      * @param {Object} scope (optional) The scope of the function (defaults to this)
20275       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
20276      **/
20277     queryBy : function(fn, scope){
20278         var data = this.snapshot || this.data;
20279         return data.filterBy(fn, scope||this);
20280     },
20281
20282     /**
20283      * Collects unique values for a particular dataIndex from this store.
20284      * @param {String} dataIndex The property to collect
20285      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
20286      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
20287      * @return {Array} An array of the unique values
20288      **/
20289     collect : function(dataIndex, allowNull, bypassFilter){
20290         var d = (bypassFilter === true && this.snapshot) ?
20291                 this.snapshot.items : this.data.items;
20292         var v, sv, r = [], l = {};
20293         for(var i = 0, len = d.length; i < len; i++){
20294             v = d[i].data[dataIndex];
20295             sv = String(v);
20296             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
20297                 l[sv] = true;
20298                 r[r.length] = v;
20299             }
20300         }
20301         return r;
20302     },
20303
20304     /**
20305      * Revert to a view of the Record cache with no filtering applied.
20306      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
20307      */
20308     clearFilter : function(suppressEvent){
20309         if(this.snapshot && this.snapshot != this.data){
20310             this.data = this.snapshot;
20311             delete this.snapshot;
20312             if(suppressEvent !== true){
20313                 this.fireEvent("datachanged", this);
20314             }
20315         }
20316     },
20317
20318     // private
20319     afterEdit : function(record){
20320         if(this.modified.indexOf(record) == -1){
20321             this.modified.push(record);
20322         }
20323         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
20324     },
20325     
20326     // private
20327     afterReject : function(record){
20328         this.modified.remove(record);
20329         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
20330     },
20331
20332     // private
20333     afterCommit : function(record){
20334         this.modified.remove(record);
20335         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
20336     },
20337
20338     /**
20339      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
20340      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
20341      */
20342     commitChanges : function(){
20343         var m = this.modified.slice(0);
20344         this.modified = [];
20345         for(var i = 0, len = m.length; i < len; i++){
20346             m[i].commit();
20347         }
20348     },
20349
20350     /**
20351      * Cancel outstanding changes on all changed records.
20352      */
20353     rejectChanges : function(){
20354         var m = this.modified.slice(0);
20355         this.modified = [];
20356         for(var i = 0, len = m.length; i < len; i++){
20357             m[i].reject();
20358         }
20359     },
20360
20361     onMetaChange : function(meta, rtype, o){
20362         this.recordType = rtype;
20363         this.fields = rtype.prototype.fields;
20364         delete this.snapshot;
20365         this.sortInfo = meta.sortInfo || this.sortInfo;
20366         this.modified = [];
20367         this.fireEvent('metachange', this, this.reader.meta);
20368     }
20369 });/*
20370  * Based on:
20371  * Ext JS Library 1.1.1
20372  * Copyright(c) 2006-2007, Ext JS, LLC.
20373  *
20374  * Originally Released Under LGPL - original licence link has changed is not relivant.
20375  *
20376  * Fork - LGPL
20377  * <script type="text/javascript">
20378  */
20379
20380 /**
20381  * @class Roo.data.SimpleStore
20382  * @extends Roo.data.Store
20383  * Small helper class to make creating Stores from Array data easier.
20384  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
20385  * @cfg {Array} fields An array of field definition objects, or field name strings.
20386  * @cfg {Array} data The multi-dimensional array of data
20387  * @constructor
20388  * @param {Object} config
20389  */
20390 Roo.data.SimpleStore = function(config){
20391     Roo.data.SimpleStore.superclass.constructor.call(this, {
20392         isLocal : true,
20393         reader: new Roo.data.ArrayReader({
20394                 id: config.id
20395             },
20396             Roo.data.Record.create(config.fields)
20397         ),
20398         proxy : new Roo.data.MemoryProxy(config.data)
20399     });
20400     this.load();
20401 };
20402 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
20403  * Based on:
20404  * Ext JS Library 1.1.1
20405  * Copyright(c) 2006-2007, Ext JS, LLC.
20406  *
20407  * Originally Released Under LGPL - original licence link has changed is not relivant.
20408  *
20409  * Fork - LGPL
20410  * <script type="text/javascript">
20411  */
20412
20413 /**
20414 /**
20415  * @extends Roo.data.Store
20416  * @class Roo.data.JsonStore
20417  * Small helper class to make creating Stores for JSON data easier. <br/>
20418 <pre><code>
20419 var store = new Roo.data.JsonStore({
20420     url: 'get-images.php',
20421     root: 'images',
20422     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
20423 });
20424 </code></pre>
20425  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
20426  * JsonReader and HttpProxy (unless inline data is provided).</b>
20427  * @cfg {Array} fields An array of field definition objects, or field name strings.
20428  * @constructor
20429  * @param {Object} config
20430  */
20431 Roo.data.JsonStore = function(c){
20432     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
20433         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
20434         reader: new Roo.data.JsonReader(c, c.fields)
20435     }));
20436 };
20437 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
20438  * Based on:
20439  * Ext JS Library 1.1.1
20440  * Copyright(c) 2006-2007, Ext JS, LLC.
20441  *
20442  * Originally Released Under LGPL - original licence link has changed is not relivant.
20443  *
20444  * Fork - LGPL
20445  * <script type="text/javascript">
20446  */
20447
20448  
20449 Roo.data.Field = function(config){
20450     if(typeof config == "string"){
20451         config = {name: config};
20452     }
20453     Roo.apply(this, config);
20454     
20455     if(!this.type){
20456         this.type = "auto";
20457     }
20458     
20459     var st = Roo.data.SortTypes;
20460     // named sortTypes are supported, here we look them up
20461     if(typeof this.sortType == "string"){
20462         this.sortType = st[this.sortType];
20463     }
20464     
20465     // set default sortType for strings and dates
20466     if(!this.sortType){
20467         switch(this.type){
20468             case "string":
20469                 this.sortType = st.asUCString;
20470                 break;
20471             case "date":
20472                 this.sortType = st.asDate;
20473                 break;
20474             default:
20475                 this.sortType = st.none;
20476         }
20477     }
20478
20479     // define once
20480     var stripRe = /[\$,%]/g;
20481
20482     // prebuilt conversion function for this field, instead of
20483     // switching every time we're reading a value
20484     if(!this.convert){
20485         var cv, dateFormat = this.dateFormat;
20486         switch(this.type){
20487             case "":
20488             case "auto":
20489             case undefined:
20490                 cv = function(v){ return v; };
20491                 break;
20492             case "string":
20493                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20494                 break;
20495             case "int":
20496                 cv = function(v){
20497                     return v !== undefined && v !== null && v !== '' ?
20498                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20499                     };
20500                 break;
20501             case "float":
20502                 cv = function(v){
20503                     return v !== undefined && v !== null && v !== '' ?
20504                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20505                     };
20506                 break;
20507             case "bool":
20508             case "boolean":
20509                 cv = function(v){ return v === true || v === "true" || v == 1; };
20510                 break;
20511             case "date":
20512                 cv = function(v){
20513                     if(!v){
20514                         return '';
20515                     }
20516                     if(v instanceof Date){
20517                         return v;
20518                     }
20519                     if(dateFormat){
20520                         if(dateFormat == "timestamp"){
20521                             return new Date(v*1000);
20522                         }
20523                         return Date.parseDate(v, dateFormat);
20524                     }
20525                     var parsed = Date.parse(v);
20526                     return parsed ? new Date(parsed) : null;
20527                 };
20528              break;
20529             
20530         }
20531         this.convert = cv;
20532     }
20533 };
20534
20535 Roo.data.Field.prototype = {
20536     dateFormat: null,
20537     defaultValue: "",
20538     mapping: null,
20539     sortType : null,
20540     sortDir : "ASC"
20541 };/*
20542  * Based on:
20543  * Ext JS Library 1.1.1
20544  * Copyright(c) 2006-2007, Ext JS, LLC.
20545  *
20546  * Originally Released Under LGPL - original licence link has changed is not relivant.
20547  *
20548  * Fork - LGPL
20549  * <script type="text/javascript">
20550  */
20551  
20552 // Base class for reading structured data from a data source.  This class is intended to be
20553 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20554
20555 /**
20556  * @class Roo.data.DataReader
20557  * Base class for reading structured data from a data source.  This class is intended to be
20558  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20559  */
20560
20561 Roo.data.DataReader = function(meta, recordType){
20562     
20563     this.meta = meta;
20564     
20565     this.recordType = recordType instanceof Array ? 
20566         Roo.data.Record.create(recordType) : recordType;
20567 };
20568
20569 Roo.data.DataReader.prototype = {
20570      /**
20571      * Create an empty record
20572      * @param {Object} data (optional) - overlay some values
20573      * @return {Roo.data.Record} record created.
20574      */
20575     newRow :  function(d) {
20576         var da =  {};
20577         this.recordType.prototype.fields.each(function(c) {
20578             switch( c.type) {
20579                 case 'int' : da[c.name] = 0; break;
20580                 case 'date' : da[c.name] = new Date(); break;
20581                 case 'float' : da[c.name] = 0.0; break;
20582                 case 'boolean' : da[c.name] = false; break;
20583                 default : da[c.name] = ""; break;
20584             }
20585             
20586         });
20587         return new this.recordType(Roo.apply(da, d));
20588     }
20589     
20590 };/*
20591  * Based on:
20592  * Ext JS Library 1.1.1
20593  * Copyright(c) 2006-2007, Ext JS, LLC.
20594  *
20595  * Originally Released Under LGPL - original licence link has changed is not relivant.
20596  *
20597  * Fork - LGPL
20598  * <script type="text/javascript">
20599  */
20600
20601 /**
20602  * @class Roo.data.DataProxy
20603  * @extends Roo.data.Observable
20604  * This class is an abstract base class for implementations which provide retrieval of
20605  * unformatted data objects.<br>
20606  * <p>
20607  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20608  * (of the appropriate type which knows how to parse the data object) to provide a block of
20609  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20610  * <p>
20611  * Custom implementations must implement the load method as described in
20612  * {@link Roo.data.HttpProxy#load}.
20613  */
20614 Roo.data.DataProxy = function(){
20615     this.addEvents({
20616         /**
20617          * @event beforeload
20618          * Fires before a network request is made to retrieve a data object.
20619          * @param {Object} This DataProxy object.
20620          * @param {Object} params The params parameter to the load function.
20621          */
20622         beforeload : true,
20623         /**
20624          * @event load
20625          * Fires before the load method's callback is called.
20626          * @param {Object} This DataProxy object.
20627          * @param {Object} o The data object.
20628          * @param {Object} arg The callback argument object passed to the load function.
20629          */
20630         load : true,
20631         /**
20632          * @event loadexception
20633          * Fires if an Exception occurs during data retrieval.
20634          * @param {Object} This DataProxy object.
20635          * @param {Object} o The data object.
20636          * @param {Object} arg The callback argument object passed to the load function.
20637          * @param {Object} e The Exception.
20638          */
20639         loadexception : true
20640     });
20641     Roo.data.DataProxy.superclass.constructor.call(this);
20642 };
20643
20644 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20645
20646     /**
20647      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20648      */
20649 /*
20650  * Based on:
20651  * Ext JS Library 1.1.1
20652  * Copyright(c) 2006-2007, Ext JS, LLC.
20653  *
20654  * Originally Released Under LGPL - original licence link has changed is not relivant.
20655  *
20656  * Fork - LGPL
20657  * <script type="text/javascript">
20658  */
20659 /**
20660  * @class Roo.data.MemoryProxy
20661  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20662  * to the Reader when its load method is called.
20663  * @constructor
20664  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20665  */
20666 Roo.data.MemoryProxy = function(data){
20667     if (data.data) {
20668         data = data.data;
20669     }
20670     Roo.data.MemoryProxy.superclass.constructor.call(this);
20671     this.data = data;
20672 };
20673
20674 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20675     /**
20676      * Load data from the requested source (in this case an in-memory
20677      * data object passed to the constructor), read the data object into
20678      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20679      * process that block using the passed callback.
20680      * @param {Object} params This parameter is not used by the MemoryProxy class.
20681      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20682      * object into a block of Roo.data.Records.
20683      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20684      * The function must be passed <ul>
20685      * <li>The Record block object</li>
20686      * <li>The "arg" argument from the load function</li>
20687      * <li>A boolean success indicator</li>
20688      * </ul>
20689      * @param {Object} scope The scope in which to call the callback
20690      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20691      */
20692     load : function(params, reader, callback, scope, arg){
20693         params = params || {};
20694         var result;
20695         try {
20696             result = reader.readRecords(this.data);
20697         }catch(e){
20698             this.fireEvent("loadexception", this, arg, null, e);
20699             callback.call(scope, null, arg, false);
20700             return;
20701         }
20702         callback.call(scope, result, arg, true);
20703     },
20704     
20705     // private
20706     update : function(params, records){
20707         
20708     }
20709 });/*
20710  * Based on:
20711  * Ext JS Library 1.1.1
20712  * Copyright(c) 2006-2007, Ext JS, LLC.
20713  *
20714  * Originally Released Under LGPL - original licence link has changed is not relivant.
20715  *
20716  * Fork - LGPL
20717  * <script type="text/javascript">
20718  */
20719 /**
20720  * @class Roo.data.HttpProxy
20721  * @extends Roo.data.DataProxy
20722  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20723  * configured to reference a certain URL.<br><br>
20724  * <p>
20725  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20726  * from which the running page was served.<br><br>
20727  * <p>
20728  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20729  * <p>
20730  * Be aware that to enable the browser to parse an XML document, the server must set
20731  * the Content-Type header in the HTTP response to "text/xml".
20732  * @constructor
20733  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20734  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20735  * will be used to make the request.
20736  */
20737 Roo.data.HttpProxy = function(conn){
20738     Roo.data.HttpProxy.superclass.constructor.call(this);
20739     // is conn a conn config or a real conn?
20740     this.conn = conn;
20741     this.useAjax = !conn || !conn.events;
20742   
20743 };
20744
20745 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20746     // thse are take from connection...
20747     
20748     /**
20749      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20750      */
20751     /**
20752      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20753      * extra parameters to each request made by this object. (defaults to undefined)
20754      */
20755     /**
20756      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20757      *  to each request made by this object. (defaults to undefined)
20758      */
20759     /**
20760      * @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)
20761      */
20762     /**
20763      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20764      */
20765      /**
20766      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20767      * @type Boolean
20768      */
20769   
20770
20771     /**
20772      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20773      * @type Boolean
20774      */
20775     /**
20776      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20777      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20778      * a finer-grained basis than the DataProxy events.
20779      */
20780     getConnection : function(){
20781         return this.useAjax ? Roo.Ajax : this.conn;
20782     },
20783
20784     /**
20785      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20786      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20787      * process that block using the passed callback.
20788      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20789      * for the request to the remote server.
20790      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20791      * object into a block of Roo.data.Records.
20792      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20793      * The function must be passed <ul>
20794      * <li>The Record block object</li>
20795      * <li>The "arg" argument from the load function</li>
20796      * <li>A boolean success indicator</li>
20797      * </ul>
20798      * @param {Object} scope The scope in which to call the callback
20799      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20800      */
20801     load : function(params, reader, callback, scope, arg){
20802         if(this.fireEvent("beforeload", this, params) !== false){
20803             var  o = {
20804                 params : params || {},
20805                 request: {
20806                     callback : callback,
20807                     scope : scope,
20808                     arg : arg
20809                 },
20810                 reader: reader,
20811                 callback : this.loadResponse,
20812                 scope: this
20813             };
20814             if(this.useAjax){
20815                 Roo.applyIf(o, this.conn);
20816                 if(this.activeRequest){
20817                     Roo.Ajax.abort(this.activeRequest);
20818                 }
20819                 this.activeRequest = Roo.Ajax.request(o);
20820             }else{
20821                 this.conn.request(o);
20822             }
20823         }else{
20824             callback.call(scope||this, null, arg, false);
20825         }
20826     },
20827
20828     // private
20829     loadResponse : function(o, success, response){
20830         delete this.activeRequest;
20831         if(!success){
20832             this.fireEvent("loadexception", this, o, response);
20833             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20834             return;
20835         }
20836         var result;
20837         try {
20838             result = o.reader.read(response);
20839         }catch(e){
20840             this.fireEvent("loadexception", this, o, response, e);
20841             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20842             return;
20843         }
20844         
20845         this.fireEvent("load", this, o, o.request.arg);
20846         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20847     },
20848
20849     // private
20850     update : function(dataSet){
20851
20852     },
20853
20854     // private
20855     updateResponse : function(dataSet){
20856
20857     }
20858 });/*
20859  * Based on:
20860  * Ext JS Library 1.1.1
20861  * Copyright(c) 2006-2007, Ext JS, LLC.
20862  *
20863  * Originally Released Under LGPL - original licence link has changed is not relivant.
20864  *
20865  * Fork - LGPL
20866  * <script type="text/javascript">
20867  */
20868
20869 /**
20870  * @class Roo.data.ScriptTagProxy
20871  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20872  * other than the originating domain of the running page.<br><br>
20873  * <p>
20874  * <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
20875  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20876  * <p>
20877  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20878  * source code that is used as the source inside a &lt;script> tag.<br><br>
20879  * <p>
20880  * In order for the browser to process the returned data, the server must wrap the data object
20881  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20882  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20883  * depending on whether the callback name was passed:
20884  * <p>
20885  * <pre><code>
20886 boolean scriptTag = false;
20887 String cb = request.getParameter("callback");
20888 if (cb != null) {
20889     scriptTag = true;
20890     response.setContentType("text/javascript");
20891 } else {
20892     response.setContentType("application/x-json");
20893 }
20894 Writer out = response.getWriter();
20895 if (scriptTag) {
20896     out.write(cb + "(");
20897 }
20898 out.print(dataBlock.toJsonString());
20899 if (scriptTag) {
20900     out.write(");");
20901 }
20902 </pre></code>
20903  *
20904  * @constructor
20905  * @param {Object} config A configuration object.
20906  */
20907 Roo.data.ScriptTagProxy = function(config){
20908     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20909     Roo.apply(this, config);
20910     this.head = document.getElementsByTagName("head")[0];
20911 };
20912
20913 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20914
20915 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20916     /**
20917      * @cfg {String} url The URL from which to request the data object.
20918      */
20919     /**
20920      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20921      */
20922     timeout : 30000,
20923     /**
20924      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20925      * the server the name of the callback function set up by the load call to process the returned data object.
20926      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20927      * javascript output which calls this named function passing the data object as its only parameter.
20928      */
20929     callbackParam : "callback",
20930     /**
20931      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20932      * name to the request.
20933      */
20934     nocache : true,
20935
20936     /**
20937      * Load data from the configured URL, read the data object into
20938      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20939      * process that block using the passed callback.
20940      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20941      * for the request to the remote server.
20942      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20943      * object into a block of Roo.data.Records.
20944      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20945      * The function must be passed <ul>
20946      * <li>The Record block object</li>
20947      * <li>The "arg" argument from the load function</li>
20948      * <li>A boolean success indicator</li>
20949      * </ul>
20950      * @param {Object} scope The scope in which to call the callback
20951      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20952      */
20953     load : function(params, reader, callback, scope, arg){
20954         if(this.fireEvent("beforeload", this, params) !== false){
20955
20956             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20957
20958             var url = this.url;
20959             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20960             if(this.nocache){
20961                 url += "&_dc=" + (new Date().getTime());
20962             }
20963             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20964             var trans = {
20965                 id : transId,
20966                 cb : "stcCallback"+transId,
20967                 scriptId : "stcScript"+transId,
20968                 params : params,
20969                 arg : arg,
20970                 url : url,
20971                 callback : callback,
20972                 scope : scope,
20973                 reader : reader
20974             };
20975             var conn = this;
20976
20977             window[trans.cb] = function(o){
20978                 conn.handleResponse(o, trans);
20979             };
20980
20981             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20982
20983             if(this.autoAbort !== false){
20984                 this.abort();
20985             }
20986
20987             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20988
20989             var script = document.createElement("script");
20990             script.setAttribute("src", url);
20991             script.setAttribute("type", "text/javascript");
20992             script.setAttribute("id", trans.scriptId);
20993             this.head.appendChild(script);
20994
20995             this.trans = trans;
20996         }else{
20997             callback.call(scope||this, null, arg, false);
20998         }
20999     },
21000
21001     // private
21002     isLoading : function(){
21003         return this.trans ? true : false;
21004     },
21005
21006     /**
21007      * Abort the current server request.
21008      */
21009     abort : function(){
21010         if(this.isLoading()){
21011             this.destroyTrans(this.trans);
21012         }
21013     },
21014
21015     // private
21016     destroyTrans : function(trans, isLoaded){
21017         this.head.removeChild(document.getElementById(trans.scriptId));
21018         clearTimeout(trans.timeoutId);
21019         if(isLoaded){
21020             window[trans.cb] = undefined;
21021             try{
21022                 delete window[trans.cb];
21023             }catch(e){}
21024         }else{
21025             // if hasn't been loaded, wait for load to remove it to prevent script error
21026             window[trans.cb] = function(){
21027                 window[trans.cb] = undefined;
21028                 try{
21029                     delete window[trans.cb];
21030                 }catch(e){}
21031             };
21032         }
21033     },
21034
21035     // private
21036     handleResponse : function(o, trans){
21037         this.trans = false;
21038         this.destroyTrans(trans, true);
21039         var result;
21040         try {
21041             result = trans.reader.readRecords(o);
21042         }catch(e){
21043             this.fireEvent("loadexception", this, o, trans.arg, e);
21044             trans.callback.call(trans.scope||window, null, trans.arg, false);
21045             return;
21046         }
21047         this.fireEvent("load", this, o, trans.arg);
21048         trans.callback.call(trans.scope||window, result, trans.arg, true);
21049     },
21050
21051     // private
21052     handleFailure : function(trans){
21053         this.trans = false;
21054         this.destroyTrans(trans, false);
21055         this.fireEvent("loadexception", this, null, trans.arg);
21056         trans.callback.call(trans.scope||window, null, trans.arg, false);
21057     }
21058 });/*
21059  * Based on:
21060  * Ext JS Library 1.1.1
21061  * Copyright(c) 2006-2007, Ext JS, LLC.
21062  *
21063  * Originally Released Under LGPL - original licence link has changed is not relivant.
21064  *
21065  * Fork - LGPL
21066  * <script type="text/javascript">
21067  */
21068
21069 /**
21070  * @class Roo.data.JsonReader
21071  * @extends Roo.data.DataReader
21072  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
21073  * based on mappings in a provided Roo.data.Record constructor.
21074  * 
21075  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
21076  * in the reply previously. 
21077  * 
21078  * <p>
21079  * Example code:
21080  * <pre><code>
21081 var RecordDef = Roo.data.Record.create([
21082     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
21083     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
21084 ]);
21085 var myReader = new Roo.data.JsonReader({
21086     totalProperty: "results",    // The property which contains the total dataset size (optional)
21087     root: "rows",                // The property which contains an Array of row objects
21088     id: "id"                     // The property within each row object that provides an ID for the record (optional)
21089 }, RecordDef);
21090 </code></pre>
21091  * <p>
21092  * This would consume a JSON file like this:
21093  * <pre><code>
21094 { 'results': 2, 'rows': [
21095     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
21096     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
21097 }
21098 </code></pre>
21099  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
21100  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
21101  * paged from the remote server.
21102  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
21103  * @cfg {String} root name of the property which contains the Array of row objects.
21104  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
21105  * @constructor
21106  * Create a new JsonReader
21107  * @param {Object} meta Metadata configuration options
21108  * @param {Object} recordType Either an Array of field definition objects,
21109  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
21110  */
21111 Roo.data.JsonReader = function(meta, recordType){
21112     
21113     meta = meta || {};
21114     // set some defaults:
21115     Roo.applyIf(meta, {
21116         totalProperty: 'total',
21117         successProperty : 'success',
21118         root : 'data',
21119         id : 'id'
21120     });
21121     
21122     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
21123 };
21124 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
21125     
21126     /**
21127      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
21128      * Used by Store query builder to append _requestMeta to params.
21129      * 
21130      */
21131     metaFromRemote : false,
21132     /**
21133      * This method is only used by a DataProxy which has retrieved data from a remote server.
21134      * @param {Object} response The XHR object which contains the JSON data in its responseText.
21135      * @return {Object} data A data block which is used by an Roo.data.Store object as
21136      * a cache of Roo.data.Records.
21137      */
21138     read : function(response){
21139         var json = response.responseText;
21140        
21141         var o = /* eval:var:o */ eval("("+json+")");
21142         if(!o) {
21143             throw {message: "JsonReader.read: Json object not found"};
21144         }
21145         
21146         if(o.metaData){
21147             
21148             delete this.ef;
21149             this.metaFromRemote = true;
21150             this.meta = o.metaData;
21151             this.recordType = Roo.data.Record.create(o.metaData.fields);
21152             this.onMetaChange(this.meta, this.recordType, o);
21153         }
21154         return this.readRecords(o);
21155     },
21156
21157     // private function a store will implement
21158     onMetaChange : function(meta, recordType, o){
21159
21160     },
21161
21162     /**
21163          * @ignore
21164          */
21165     simpleAccess: function(obj, subsc) {
21166         return obj[subsc];
21167     },
21168
21169         /**
21170          * @ignore
21171          */
21172     getJsonAccessor: function(){
21173         var re = /[\[\.]/;
21174         return function(expr) {
21175             try {
21176                 return(re.test(expr))
21177                     ? new Function("obj", "return obj." + expr)
21178                     : function(obj){
21179                         return obj[expr];
21180                     };
21181             } catch(e){}
21182             return Roo.emptyFn;
21183         };
21184     }(),
21185
21186     /**
21187      * Create a data block containing Roo.data.Records from an XML document.
21188      * @param {Object} o An object which contains an Array of row objects in the property specified
21189      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
21190      * which contains the total size of the dataset.
21191      * @return {Object} data A data block which is used by an Roo.data.Store object as
21192      * a cache of Roo.data.Records.
21193      */
21194     readRecords : function(o){
21195         /**
21196          * After any data loads, the raw JSON data is available for further custom processing.
21197          * @type Object
21198          */
21199         this.o = o;
21200         var s = this.meta, Record = this.recordType,
21201             f = Record.prototype.fields, fi = f.items, fl = f.length;
21202
21203 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
21204         if (!this.ef) {
21205             if(s.totalProperty) {
21206                     this.getTotal = this.getJsonAccessor(s.totalProperty);
21207                 }
21208                 if(s.successProperty) {
21209                     this.getSuccess = this.getJsonAccessor(s.successProperty);
21210                 }
21211                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
21212                 if (s.id) {
21213                         var g = this.getJsonAccessor(s.id);
21214                         this.getId = function(rec) {
21215                                 var r = g(rec);
21216                                 return (r === undefined || r === "") ? null : r;
21217                         };
21218                 } else {
21219                         this.getId = function(){return null;};
21220                 }
21221             this.ef = [];
21222             for(var jj = 0; jj < fl; jj++){
21223                 f = fi[jj];
21224                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
21225                 this.ef[jj] = this.getJsonAccessor(map);
21226             }
21227         }
21228
21229         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
21230         if(s.totalProperty){
21231             var vt = parseInt(this.getTotal(o), 10);
21232             if(!isNaN(vt)){
21233                 totalRecords = vt;
21234             }
21235         }
21236         if(s.successProperty){
21237             var vs = this.getSuccess(o);
21238             if(vs === false || vs === 'false'){
21239                 success = false;
21240             }
21241         }
21242         var records = [];
21243             for(var i = 0; i < c; i++){
21244                     var n = root[i];
21245                 var values = {};
21246                 var id = this.getId(n);
21247                 for(var j = 0; j < fl; j++){
21248                     f = fi[j];
21249                 var v = this.ef[j](n);
21250                 if (!f.convert) {
21251                     Roo.log('missing convert for ' + f.name);
21252                     Roo.log(f);
21253                     continue;
21254                 }
21255                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
21256                 }
21257                 var record = new Record(values, id);
21258                 record.json = n;
21259                 records[i] = record;
21260             }
21261             return {
21262             raw : o,
21263                 success : success,
21264                 records : records,
21265                 totalRecords : totalRecords
21266             };
21267     }
21268 });/*
21269  * Based on:
21270  * Ext JS Library 1.1.1
21271  * Copyright(c) 2006-2007, Ext JS, LLC.
21272  *
21273  * Originally Released Under LGPL - original licence link has changed is not relivant.
21274  *
21275  * Fork - LGPL
21276  * <script type="text/javascript">
21277  */
21278
21279 /**
21280  * @class Roo.data.XmlReader
21281  * @extends Roo.data.DataReader
21282  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
21283  * based on mappings in a provided Roo.data.Record constructor.<br><br>
21284  * <p>
21285  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
21286  * header in the HTTP response must be set to "text/xml".</em>
21287  * <p>
21288  * Example code:
21289  * <pre><code>
21290 var RecordDef = Roo.data.Record.create([
21291    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
21292    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
21293 ]);
21294 var myReader = new Roo.data.XmlReader({
21295    totalRecords: "results", // The element which contains the total dataset size (optional)
21296    record: "row",           // The repeated element which contains row information
21297    id: "id"                 // The element within the row that provides an ID for the record (optional)
21298 }, RecordDef);
21299 </code></pre>
21300  * <p>
21301  * This would consume an XML file like this:
21302  * <pre><code>
21303 &lt;?xml?>
21304 &lt;dataset>
21305  &lt;results>2&lt;/results>
21306  &lt;row>
21307    &lt;id>1&lt;/id>
21308    &lt;name>Bill&lt;/name>
21309    &lt;occupation>Gardener&lt;/occupation>
21310  &lt;/row>
21311  &lt;row>
21312    &lt;id>2&lt;/id>
21313    &lt;name>Ben&lt;/name>
21314    &lt;occupation>Horticulturalist&lt;/occupation>
21315  &lt;/row>
21316 &lt;/dataset>
21317 </code></pre>
21318  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
21319  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
21320  * paged from the remote server.
21321  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
21322  * @cfg {String} success The DomQuery path to the success attribute used by forms.
21323  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
21324  * a record identifier value.
21325  * @constructor
21326  * Create a new XmlReader
21327  * @param {Object} meta Metadata configuration options
21328  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
21329  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
21330  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
21331  */
21332 Roo.data.XmlReader = function(meta, recordType){
21333     meta = meta || {};
21334     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
21335 };
21336 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
21337     /**
21338      * This method is only used by a DataProxy which has retrieved data from a remote server.
21339          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
21340          * to contain a method called 'responseXML' that returns an XML document object.
21341      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
21342      * a cache of Roo.data.Records.
21343      */
21344     read : function(response){
21345         var doc = response.responseXML;
21346         if(!doc) {
21347             throw {message: "XmlReader.read: XML Document not available"};
21348         }
21349         return this.readRecords(doc);
21350     },
21351
21352     /**
21353      * Create a data block containing Roo.data.Records from an XML document.
21354          * @param {Object} doc A parsed XML document.
21355      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
21356      * a cache of Roo.data.Records.
21357      */
21358     readRecords : function(doc){
21359         /**
21360          * After any data loads/reads, the raw XML Document is available for further custom processing.
21361          * @type XMLDocument
21362          */
21363         this.xmlData = doc;
21364         var root = doc.documentElement || doc;
21365         var q = Roo.DomQuery;
21366         var recordType = this.recordType, fields = recordType.prototype.fields;
21367         var sid = this.meta.id;
21368         var totalRecords = 0, success = true;
21369         if(this.meta.totalRecords){
21370             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
21371         }
21372         
21373         if(this.meta.success){
21374             var sv = q.selectValue(this.meta.success, root, true);
21375             success = sv !== false && sv !== 'false';
21376         }
21377         var records = [];
21378         var ns = q.select(this.meta.record, root);
21379         for(var i = 0, len = ns.length; i < len; i++) {
21380                 var n = ns[i];
21381                 var values = {};
21382                 var id = sid ? q.selectValue(sid, n) : undefined;
21383                 for(var j = 0, jlen = fields.length; j < jlen; j++){
21384                     var f = fields.items[j];
21385                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
21386                     v = f.convert(v);
21387                     values[f.name] = v;
21388                 }
21389                 var record = new recordType(values, id);
21390                 record.node = n;
21391                 records[records.length] = record;
21392             }
21393
21394             return {
21395                 success : success,
21396                 records : records,
21397                 totalRecords : totalRecords || records.length
21398             };
21399     }
21400 });/*
21401  * Based on:
21402  * Ext JS Library 1.1.1
21403  * Copyright(c) 2006-2007, Ext JS, LLC.
21404  *
21405  * Originally Released Under LGPL - original licence link has changed is not relivant.
21406  *
21407  * Fork - LGPL
21408  * <script type="text/javascript">
21409  */
21410
21411 /**
21412  * @class Roo.data.ArrayReader
21413  * @extends Roo.data.DataReader
21414  * Data reader class to create an Array of Roo.data.Record objects from an Array.
21415  * Each element of that Array represents a row of data fields. The
21416  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
21417  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
21418  * <p>
21419  * Example code:.
21420  * <pre><code>
21421 var RecordDef = Roo.data.Record.create([
21422     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
21423     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
21424 ]);
21425 var myReader = new Roo.data.ArrayReader({
21426     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
21427 }, RecordDef);
21428 </code></pre>
21429  * <p>
21430  * This would consume an Array like this:
21431  * <pre><code>
21432 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
21433   </code></pre>
21434  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
21435  * @constructor
21436  * Create a new JsonReader
21437  * @param {Object} meta Metadata configuration options.
21438  * @param {Object} recordType Either an Array of field definition objects
21439  * as specified to {@link Roo.data.Record#create},
21440  * or an {@link Roo.data.Record} object
21441  * created using {@link Roo.data.Record#create}.
21442  */
21443 Roo.data.ArrayReader = function(meta, recordType){
21444     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
21445 };
21446
21447 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
21448     /**
21449      * Create a data block containing Roo.data.Records from an XML document.
21450      * @param {Object} o An Array of row objects which represents the dataset.
21451      * @return {Object} data A data block which is used by an Roo.data.Store object as
21452      * a cache of Roo.data.Records.
21453      */
21454     readRecords : function(o){
21455         var sid = this.meta ? this.meta.id : null;
21456         var recordType = this.recordType, fields = recordType.prototype.fields;
21457         var records = [];
21458         var root = o;
21459             for(var i = 0; i < root.length; i++){
21460                     var n = root[i];
21461                 var values = {};
21462                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
21463                 for(var j = 0, jlen = fields.length; j < jlen; j++){
21464                 var f = fields.items[j];
21465                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
21466                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
21467                 v = f.convert(v);
21468                 values[f.name] = v;
21469             }
21470                 var record = new recordType(values, id);
21471                 record.json = n;
21472                 records[records.length] = record;
21473             }
21474             return {
21475                 records : records,
21476                 totalRecords : records.length
21477             };
21478     }
21479 });/*
21480  * Based on:
21481  * Ext JS Library 1.1.1
21482  * Copyright(c) 2006-2007, Ext JS, LLC.
21483  *
21484  * Originally Released Under LGPL - original licence link has changed is not relivant.
21485  *
21486  * Fork - LGPL
21487  * <script type="text/javascript">
21488  */
21489
21490
21491 /**
21492  * @class Roo.data.Tree
21493  * @extends Roo.util.Observable
21494  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21495  * in the tree have most standard DOM functionality.
21496  * @constructor
21497  * @param {Node} root (optional) The root node
21498  */
21499 Roo.data.Tree = function(root){
21500    this.nodeHash = {};
21501    /**
21502     * The root node for this tree
21503     * @type Node
21504     */
21505    this.root = null;
21506    if(root){
21507        this.setRootNode(root);
21508    }
21509    this.addEvents({
21510        /**
21511         * @event append
21512         * Fires when a new child node is appended to a node in this tree.
21513         * @param {Tree} tree The owner tree
21514         * @param {Node} parent The parent node
21515         * @param {Node} node The newly appended node
21516         * @param {Number} index The index of the newly appended node
21517         */
21518        "append" : true,
21519        /**
21520         * @event remove
21521         * Fires when a child node is removed from a node in this tree.
21522         * @param {Tree} tree The owner tree
21523         * @param {Node} parent The parent node
21524         * @param {Node} node The child node removed
21525         */
21526        "remove" : true,
21527        /**
21528         * @event move
21529         * Fires when a node is moved to a new location in the tree
21530         * @param {Tree} tree The owner tree
21531         * @param {Node} node The node moved
21532         * @param {Node} oldParent The old parent of this node
21533         * @param {Node} newParent The new parent of this node
21534         * @param {Number} index The index it was moved to
21535         */
21536        "move" : true,
21537        /**
21538         * @event insert
21539         * Fires when a new child node is inserted in a node in this tree.
21540         * @param {Tree} tree The owner tree
21541         * @param {Node} parent The parent node
21542         * @param {Node} node The child node inserted
21543         * @param {Node} refNode The child node the node was inserted before
21544         */
21545        "insert" : true,
21546        /**
21547         * @event beforeappend
21548         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21549         * @param {Tree} tree The owner tree
21550         * @param {Node} parent The parent node
21551         * @param {Node} node The child node to be appended
21552         */
21553        "beforeappend" : true,
21554        /**
21555         * @event beforeremove
21556         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21557         * @param {Tree} tree The owner tree
21558         * @param {Node} parent The parent node
21559         * @param {Node} node The child node to be removed
21560         */
21561        "beforeremove" : true,
21562        /**
21563         * @event beforemove
21564         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21565         * @param {Tree} tree The owner tree
21566         * @param {Node} node The node being moved
21567         * @param {Node} oldParent The parent of the node
21568         * @param {Node} newParent The new parent the node is moving to
21569         * @param {Number} index The index it is being moved to
21570         */
21571        "beforemove" : true,
21572        /**
21573         * @event beforeinsert
21574         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21575         * @param {Tree} tree The owner tree
21576         * @param {Node} parent The parent node
21577         * @param {Node} node The child node to be inserted
21578         * @param {Node} refNode The child node the node is being inserted before
21579         */
21580        "beforeinsert" : true
21581    });
21582
21583     Roo.data.Tree.superclass.constructor.call(this);
21584 };
21585
21586 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21587     pathSeparator: "/",
21588
21589     proxyNodeEvent : function(){
21590         return this.fireEvent.apply(this, arguments);
21591     },
21592
21593     /**
21594      * Returns the root node for this tree.
21595      * @return {Node}
21596      */
21597     getRootNode : function(){
21598         return this.root;
21599     },
21600
21601     /**
21602      * Sets the root node for this tree.
21603      * @param {Node} node
21604      * @return {Node}
21605      */
21606     setRootNode : function(node){
21607         this.root = node;
21608         node.ownerTree = this;
21609         node.isRoot = true;
21610         this.registerNode(node);
21611         return node;
21612     },
21613
21614     /**
21615      * Gets a node in this tree by its id.
21616      * @param {String} id
21617      * @return {Node}
21618      */
21619     getNodeById : function(id){
21620         return this.nodeHash[id];
21621     },
21622
21623     registerNode : function(node){
21624         this.nodeHash[node.id] = node;
21625     },
21626
21627     unregisterNode : function(node){
21628         delete this.nodeHash[node.id];
21629     },
21630
21631     toString : function(){
21632         return "[Tree"+(this.id?" "+this.id:"")+"]";
21633     }
21634 });
21635
21636 /**
21637  * @class Roo.data.Node
21638  * @extends Roo.util.Observable
21639  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21640  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21641  * @constructor
21642  * @param {Object} attributes The attributes/config for the node
21643  */
21644 Roo.data.Node = function(attributes){
21645     /**
21646      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21647      * @type {Object}
21648      */
21649     this.attributes = attributes || {};
21650     this.leaf = this.attributes.leaf;
21651     /**
21652      * The node id. @type String
21653      */
21654     this.id = this.attributes.id;
21655     if(!this.id){
21656         this.id = Roo.id(null, "ynode-");
21657         this.attributes.id = this.id;
21658     }
21659      
21660     
21661     /**
21662      * All child nodes of this node. @type Array
21663      */
21664     this.childNodes = [];
21665     if(!this.childNodes.indexOf){ // indexOf is a must
21666         this.childNodes.indexOf = function(o){
21667             for(var i = 0, len = this.length; i < len; i++){
21668                 if(this[i] == o) {
21669                     return i;
21670                 }
21671             }
21672             return -1;
21673         };
21674     }
21675     /**
21676      * The parent node for this node. @type Node
21677      */
21678     this.parentNode = null;
21679     /**
21680      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21681      */
21682     this.firstChild = null;
21683     /**
21684      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21685      */
21686     this.lastChild = null;
21687     /**
21688      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21689      */
21690     this.previousSibling = null;
21691     /**
21692      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21693      */
21694     this.nextSibling = null;
21695
21696     this.addEvents({
21697        /**
21698         * @event append
21699         * Fires when a new child node is appended
21700         * @param {Tree} tree The owner tree
21701         * @param {Node} this This node
21702         * @param {Node} node The newly appended node
21703         * @param {Number} index The index of the newly appended node
21704         */
21705        "append" : true,
21706        /**
21707         * @event remove
21708         * Fires when a child node is removed
21709         * @param {Tree} tree The owner tree
21710         * @param {Node} this This node
21711         * @param {Node} node The removed node
21712         */
21713        "remove" : true,
21714        /**
21715         * @event move
21716         * Fires when this node is moved to a new location in the tree
21717         * @param {Tree} tree The owner tree
21718         * @param {Node} this This node
21719         * @param {Node} oldParent The old parent of this node
21720         * @param {Node} newParent The new parent of this node
21721         * @param {Number} index The index it was moved to
21722         */
21723        "move" : true,
21724        /**
21725         * @event insert
21726         * Fires when a new child node is inserted.
21727         * @param {Tree} tree The owner tree
21728         * @param {Node} this This node
21729         * @param {Node} node The child node inserted
21730         * @param {Node} refNode The child node the node was inserted before
21731         */
21732        "insert" : true,
21733        /**
21734         * @event beforeappend
21735         * Fires before a new child is appended, return false to cancel the append.
21736         * @param {Tree} tree The owner tree
21737         * @param {Node} this This node
21738         * @param {Node} node The child node to be appended
21739         */
21740        "beforeappend" : true,
21741        /**
21742         * @event beforeremove
21743         * Fires before a child is removed, return false to cancel the remove.
21744         * @param {Tree} tree The owner tree
21745         * @param {Node} this This node
21746         * @param {Node} node The child node to be removed
21747         */
21748        "beforeremove" : true,
21749        /**
21750         * @event beforemove
21751         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21752         * @param {Tree} tree The owner tree
21753         * @param {Node} this This node
21754         * @param {Node} oldParent The parent of this node
21755         * @param {Node} newParent The new parent this node is moving to
21756         * @param {Number} index The index it is being moved to
21757         */
21758        "beforemove" : true,
21759        /**
21760         * @event beforeinsert
21761         * Fires before a new child is inserted, return false to cancel the insert.
21762         * @param {Tree} tree The owner tree
21763         * @param {Node} this This node
21764         * @param {Node} node The child node to be inserted
21765         * @param {Node} refNode The child node the node is being inserted before
21766         */
21767        "beforeinsert" : true
21768    });
21769     this.listeners = this.attributes.listeners;
21770     Roo.data.Node.superclass.constructor.call(this);
21771 };
21772
21773 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21774     fireEvent : function(evtName){
21775         // first do standard event for this node
21776         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21777             return false;
21778         }
21779         // then bubble it up to the tree if the event wasn't cancelled
21780         var ot = this.getOwnerTree();
21781         if(ot){
21782             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21783                 return false;
21784             }
21785         }
21786         return true;
21787     },
21788
21789     /**
21790      * Returns true if this node is a leaf
21791      * @return {Boolean}
21792      */
21793     isLeaf : function(){
21794         return this.leaf === true;
21795     },
21796
21797     // private
21798     setFirstChild : function(node){
21799         this.firstChild = node;
21800     },
21801
21802     //private
21803     setLastChild : function(node){
21804         this.lastChild = node;
21805     },
21806
21807
21808     /**
21809      * Returns true if this node is the last child of its parent
21810      * @return {Boolean}
21811      */
21812     isLast : function(){
21813        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21814     },
21815
21816     /**
21817      * Returns true if this node is the first child of its parent
21818      * @return {Boolean}
21819      */
21820     isFirst : function(){
21821        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21822     },
21823
21824     hasChildNodes : function(){
21825         return !this.isLeaf() && this.childNodes.length > 0;
21826     },
21827
21828     /**
21829      * Insert node(s) as the last child node of this node.
21830      * @param {Node/Array} node The node or Array of nodes to append
21831      * @return {Node} The appended node if single append, or null if an array was passed
21832      */
21833     appendChild : function(node){
21834         var multi = false;
21835         if(node instanceof Array){
21836             multi = node;
21837         }else if(arguments.length > 1){
21838             multi = arguments;
21839         }
21840         // if passed an array or multiple args do them one by one
21841         if(multi){
21842             for(var i = 0, len = multi.length; i < len; i++) {
21843                 this.appendChild(multi[i]);
21844             }
21845         }else{
21846             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21847                 return false;
21848             }
21849             var index = this.childNodes.length;
21850             var oldParent = node.parentNode;
21851             // it's a move, make sure we move it cleanly
21852             if(oldParent){
21853                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21854                     return false;
21855                 }
21856                 oldParent.removeChild(node);
21857             }
21858             index = this.childNodes.length;
21859             if(index == 0){
21860                 this.setFirstChild(node);
21861             }
21862             this.childNodes.push(node);
21863             node.parentNode = this;
21864             var ps = this.childNodes[index-1];
21865             if(ps){
21866                 node.previousSibling = ps;
21867                 ps.nextSibling = node;
21868             }else{
21869                 node.previousSibling = null;
21870             }
21871             node.nextSibling = null;
21872             this.setLastChild(node);
21873             node.setOwnerTree(this.getOwnerTree());
21874             this.fireEvent("append", this.ownerTree, this, node, index);
21875             if(oldParent){
21876                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21877             }
21878             return node;
21879         }
21880     },
21881
21882     /**
21883      * Removes a child node from this node.
21884      * @param {Node} node The node to remove
21885      * @return {Node} The removed node
21886      */
21887     removeChild : function(node){
21888         var index = this.childNodes.indexOf(node);
21889         if(index == -1){
21890             return false;
21891         }
21892         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21893             return false;
21894         }
21895
21896         // remove it from childNodes collection
21897         this.childNodes.splice(index, 1);
21898
21899         // update siblings
21900         if(node.previousSibling){
21901             node.previousSibling.nextSibling = node.nextSibling;
21902         }
21903         if(node.nextSibling){
21904             node.nextSibling.previousSibling = node.previousSibling;
21905         }
21906
21907         // update child refs
21908         if(this.firstChild == node){
21909             this.setFirstChild(node.nextSibling);
21910         }
21911         if(this.lastChild == node){
21912             this.setLastChild(node.previousSibling);
21913         }
21914
21915         node.setOwnerTree(null);
21916         // clear any references from the node
21917         node.parentNode = null;
21918         node.previousSibling = null;
21919         node.nextSibling = null;
21920         this.fireEvent("remove", this.ownerTree, this, node);
21921         return node;
21922     },
21923
21924     /**
21925      * Inserts the first node before the second node in this nodes childNodes collection.
21926      * @param {Node} node The node to insert
21927      * @param {Node} refNode The node to insert before (if null the node is appended)
21928      * @return {Node} The inserted node
21929      */
21930     insertBefore : function(node, refNode){
21931         if(!refNode){ // like standard Dom, refNode can be null for append
21932             return this.appendChild(node);
21933         }
21934         // nothing to do
21935         if(node == refNode){
21936             return false;
21937         }
21938
21939         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21940             return false;
21941         }
21942         var index = this.childNodes.indexOf(refNode);
21943         var oldParent = node.parentNode;
21944         var refIndex = index;
21945
21946         // when moving internally, indexes will change after remove
21947         if(oldParent == this && this.childNodes.indexOf(node) < index){
21948             refIndex--;
21949         }
21950
21951         // it's a move, make sure we move it cleanly
21952         if(oldParent){
21953             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21954                 return false;
21955             }
21956             oldParent.removeChild(node);
21957         }
21958         if(refIndex == 0){
21959             this.setFirstChild(node);
21960         }
21961         this.childNodes.splice(refIndex, 0, node);
21962         node.parentNode = this;
21963         var ps = this.childNodes[refIndex-1];
21964         if(ps){
21965             node.previousSibling = ps;
21966             ps.nextSibling = node;
21967         }else{
21968             node.previousSibling = null;
21969         }
21970         node.nextSibling = refNode;
21971         refNode.previousSibling = node;
21972         node.setOwnerTree(this.getOwnerTree());
21973         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21974         if(oldParent){
21975             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21976         }
21977         return node;
21978     },
21979
21980     /**
21981      * Returns the child node at the specified index.
21982      * @param {Number} index
21983      * @return {Node}
21984      */
21985     item : function(index){
21986         return this.childNodes[index];
21987     },
21988
21989     /**
21990      * Replaces one child node in this node with another.
21991      * @param {Node} newChild The replacement node
21992      * @param {Node} oldChild The node to replace
21993      * @return {Node} The replaced node
21994      */
21995     replaceChild : function(newChild, oldChild){
21996         this.insertBefore(newChild, oldChild);
21997         this.removeChild(oldChild);
21998         return oldChild;
21999     },
22000
22001     /**
22002      * Returns the index of a child node
22003      * @param {Node} node
22004      * @return {Number} The index of the node or -1 if it was not found
22005      */
22006     indexOf : function(child){
22007         return this.childNodes.indexOf(child);
22008     },
22009
22010     /**
22011      * Returns the tree this node is in.
22012      * @return {Tree}
22013      */
22014     getOwnerTree : function(){
22015         // if it doesn't have one, look for one
22016         if(!this.ownerTree){
22017             var p = this;
22018             while(p){
22019                 if(p.ownerTree){
22020                     this.ownerTree = p.ownerTree;
22021                     break;
22022                 }
22023                 p = p.parentNode;
22024             }
22025         }
22026         return this.ownerTree;
22027     },
22028
22029     /**
22030      * Returns depth of this node (the root node has a depth of 0)
22031      * @return {Number}
22032      */
22033     getDepth : function(){
22034         var depth = 0;
22035         var p = this;
22036         while(p.parentNode){
22037             ++depth;
22038             p = p.parentNode;
22039         }
22040         return depth;
22041     },
22042
22043     // private
22044     setOwnerTree : function(tree){
22045         // if it's move, we need to update everyone
22046         if(tree != this.ownerTree){
22047             if(this.ownerTree){
22048                 this.ownerTree.unregisterNode(this);
22049             }
22050             this.ownerTree = tree;
22051             var cs = this.childNodes;
22052             for(var i = 0, len = cs.length; i < len; i++) {
22053                 cs[i].setOwnerTree(tree);
22054             }
22055             if(tree){
22056                 tree.registerNode(this);
22057             }
22058         }
22059     },
22060
22061     /**
22062      * Returns the path for this node. The path can be used to expand or select this node programmatically.
22063      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
22064      * @return {String} The path
22065      */
22066     getPath : function(attr){
22067         attr = attr || "id";
22068         var p = this.parentNode;
22069         var b = [this.attributes[attr]];
22070         while(p){
22071             b.unshift(p.attributes[attr]);
22072             p = p.parentNode;
22073         }
22074         var sep = this.getOwnerTree().pathSeparator;
22075         return sep + b.join(sep);
22076     },
22077
22078     /**
22079      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
22080      * function call will be the scope provided or the current node. The arguments to the function
22081      * will be the args provided or the current node. If the function returns false at any point,
22082      * the bubble is stopped.
22083      * @param {Function} fn The function to call
22084      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22085      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22086      */
22087     bubble : function(fn, scope, args){
22088         var p = this;
22089         while(p){
22090             if(fn.call(scope || p, args || p) === false){
22091                 break;
22092             }
22093             p = p.parentNode;
22094         }
22095     },
22096
22097     /**
22098      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
22099      * function call will be the scope provided or the current node. The arguments to the function
22100      * will be the args provided or the current node. If the function returns false at any point,
22101      * the cascade is stopped on that branch.
22102      * @param {Function} fn The function to call
22103      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22104      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22105      */
22106     cascade : function(fn, scope, args){
22107         if(fn.call(scope || this, args || this) !== false){
22108             var cs = this.childNodes;
22109             for(var i = 0, len = cs.length; i < len; i++) {
22110                 cs[i].cascade(fn, scope, args);
22111             }
22112         }
22113     },
22114
22115     /**
22116      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
22117      * function call will be the scope provided or the current node. The arguments to the function
22118      * will be the args provided or the current node. If the function returns false at any point,
22119      * the iteration stops.
22120      * @param {Function} fn The function to call
22121      * @param {Object} scope (optional) The scope of the function (defaults to current node)
22122      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
22123      */
22124     eachChild : function(fn, scope, args){
22125         var cs = this.childNodes;
22126         for(var i = 0, len = cs.length; i < len; i++) {
22127                 if(fn.call(scope || this, args || cs[i]) === false){
22128                     break;
22129                 }
22130         }
22131     },
22132
22133     /**
22134      * Finds the first child that has the attribute with the specified value.
22135      * @param {String} attribute The attribute name
22136      * @param {Mixed} value The value to search for
22137      * @return {Node} The found child or null if none was found
22138      */
22139     findChild : function(attribute, value){
22140         var cs = this.childNodes;
22141         for(var i = 0, len = cs.length; i < len; i++) {
22142                 if(cs[i].attributes[attribute] == value){
22143                     return cs[i];
22144                 }
22145         }
22146         return null;
22147     },
22148
22149     /**
22150      * Finds the first child by a custom function. The child matches if the function passed
22151      * returns true.
22152      * @param {Function} fn
22153      * @param {Object} scope (optional)
22154      * @return {Node} The found child or null if none was found
22155      */
22156     findChildBy : function(fn, scope){
22157         var cs = this.childNodes;
22158         for(var i = 0, len = cs.length; i < len; i++) {
22159                 if(fn.call(scope||cs[i], cs[i]) === true){
22160                     return cs[i];
22161                 }
22162         }
22163         return null;
22164     },
22165
22166     /**
22167      * Sorts this nodes children using the supplied sort function
22168      * @param {Function} fn
22169      * @param {Object} scope (optional)
22170      */
22171     sort : function(fn, scope){
22172         var cs = this.childNodes;
22173         var len = cs.length;
22174         if(len > 0){
22175             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
22176             cs.sort(sortFn);
22177             for(var i = 0; i < len; i++){
22178                 var n = cs[i];
22179                 n.previousSibling = cs[i-1];
22180                 n.nextSibling = cs[i+1];
22181                 if(i == 0){
22182                     this.setFirstChild(n);
22183                 }
22184                 if(i == len-1){
22185                     this.setLastChild(n);
22186                 }
22187             }
22188         }
22189     },
22190
22191     /**
22192      * Returns true if this node is an ancestor (at any point) of the passed node.
22193      * @param {Node} node
22194      * @return {Boolean}
22195      */
22196     contains : function(node){
22197         return node.isAncestor(this);
22198     },
22199
22200     /**
22201      * Returns true if the passed node is an ancestor (at any point) of this node.
22202      * @param {Node} node
22203      * @return {Boolean}
22204      */
22205     isAncestor : function(node){
22206         var p = this.parentNode;
22207         while(p){
22208             if(p == node){
22209                 return true;
22210             }
22211             p = p.parentNode;
22212         }
22213         return false;
22214     },
22215
22216     toString : function(){
22217         return "[Node"+(this.id?" "+this.id:"")+"]";
22218     }
22219 });/*
22220  * Based on:
22221  * Ext JS Library 1.1.1
22222  * Copyright(c) 2006-2007, Ext JS, LLC.
22223  *
22224  * Originally Released Under LGPL - original licence link has changed is not relivant.
22225  *
22226  * Fork - LGPL
22227  * <script type="text/javascript">
22228  */
22229  
22230
22231 /**
22232  * @class Roo.ComponentMgr
22233  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
22234  * @singleton
22235  */
22236 Roo.ComponentMgr = function(){
22237     var all = new Roo.util.MixedCollection();
22238
22239     return {
22240         /**
22241          * Registers a component.
22242          * @param {Roo.Component} c The component
22243          */
22244         register : function(c){
22245             all.add(c);
22246         },
22247
22248         /**
22249          * Unregisters a component.
22250          * @param {Roo.Component} c The component
22251          */
22252         unregister : function(c){
22253             all.remove(c);
22254         },
22255
22256         /**
22257          * Returns a component by id
22258          * @param {String} id The component id
22259          */
22260         get : function(id){
22261             return all.get(id);
22262         },
22263
22264         /**
22265          * Registers a function that will be called when a specified component is added to ComponentMgr
22266          * @param {String} id The component id
22267          * @param {Funtction} fn The callback function
22268          * @param {Object} scope The scope of the callback
22269          */
22270         onAvailable : function(id, fn, scope){
22271             all.on("add", function(index, o){
22272                 if(o.id == id){
22273                     fn.call(scope || o, o);
22274                     all.un("add", fn, scope);
22275                 }
22276             });
22277         }
22278     };
22279 }();/*
22280  * Based on:
22281  * Ext JS Library 1.1.1
22282  * Copyright(c) 2006-2007, Ext JS, LLC.
22283  *
22284  * Originally Released Under LGPL - original licence link has changed is not relivant.
22285  *
22286  * Fork - LGPL
22287  * <script type="text/javascript">
22288  */
22289  
22290 /**
22291  * @class Roo.Component
22292  * @extends Roo.util.Observable
22293  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
22294  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
22295  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
22296  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
22297  * All visual components (widgets) that require rendering into a layout should subclass Component.
22298  * @constructor
22299  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
22300  * 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
22301  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
22302  */
22303 Roo.Component = function(config){
22304     config = config || {};
22305     if(config.tagName || config.dom || typeof config == "string"){ // element object
22306         config = {el: config, id: config.id || config};
22307     }
22308     this.initialConfig = config;
22309
22310     Roo.apply(this, config);
22311     this.addEvents({
22312         /**
22313          * @event disable
22314          * Fires after the component is disabled.
22315              * @param {Roo.Component} this
22316              */
22317         disable : true,
22318         /**
22319          * @event enable
22320          * Fires after the component is enabled.
22321              * @param {Roo.Component} this
22322              */
22323         enable : true,
22324         /**
22325          * @event beforeshow
22326          * Fires before the component is shown.  Return false to stop the show.
22327              * @param {Roo.Component} this
22328              */
22329         beforeshow : true,
22330         /**
22331          * @event show
22332          * Fires after the component is shown.
22333              * @param {Roo.Component} this
22334              */
22335         show : true,
22336         /**
22337          * @event beforehide
22338          * Fires before the component is hidden. Return false to stop the hide.
22339              * @param {Roo.Component} this
22340              */
22341         beforehide : true,
22342         /**
22343          * @event hide
22344          * Fires after the component is hidden.
22345              * @param {Roo.Component} this
22346              */
22347         hide : true,
22348         /**
22349          * @event beforerender
22350          * Fires before the component is rendered. Return false to stop the render.
22351              * @param {Roo.Component} this
22352              */
22353         beforerender : true,
22354         /**
22355          * @event render
22356          * Fires after the component is rendered.
22357              * @param {Roo.Component} this
22358              */
22359         render : true,
22360         /**
22361          * @event beforedestroy
22362          * Fires before the component is destroyed. Return false to stop the destroy.
22363              * @param {Roo.Component} this
22364              */
22365         beforedestroy : true,
22366         /**
22367          * @event destroy
22368          * Fires after the component is destroyed.
22369              * @param {Roo.Component} this
22370              */
22371         destroy : true
22372     });
22373     if(!this.id){
22374         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
22375     }
22376     Roo.ComponentMgr.register(this);
22377     Roo.Component.superclass.constructor.call(this);
22378     this.initComponent();
22379     if(this.renderTo){ // not supported by all components yet. use at your own risk!
22380         this.render(this.renderTo);
22381         delete this.renderTo;
22382     }
22383 };
22384
22385 /** @private */
22386 Roo.Component.AUTO_ID = 1000;
22387
22388 Roo.extend(Roo.Component, Roo.util.Observable, {
22389     /**
22390      * @scope Roo.Component.prototype
22391      * @type {Boolean}
22392      * true if this component is hidden. Read-only.
22393      */
22394     hidden : false,
22395     /**
22396      * @type {Boolean}
22397      * true if this component is disabled. Read-only.
22398      */
22399     disabled : false,
22400     /**
22401      * @type {Boolean}
22402      * true if this component has been rendered. Read-only.
22403      */
22404     rendered : false,
22405     
22406     /** @cfg {String} disableClass
22407      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
22408      */
22409     disabledClass : "x-item-disabled",
22410         /** @cfg {Boolean} allowDomMove
22411          * Whether the component can move the Dom node when rendering (defaults to true).
22412          */
22413     allowDomMove : true,
22414     /** @cfg {String} hideMode
22415      * How this component should hidden. Supported values are
22416      * "visibility" (css visibility), "offsets" (negative offset position) and
22417      * "display" (css display) - defaults to "display".
22418      */
22419     hideMode: 'display',
22420
22421     /** @private */
22422     ctype : "Roo.Component",
22423
22424     /**
22425      * @cfg {String} actionMode 
22426      * which property holds the element that used for  hide() / show() / disable() / enable()
22427      * default is 'el' 
22428      */
22429     actionMode : "el",
22430
22431     /** @private */
22432     getActionEl : function(){
22433         return this[this.actionMode];
22434     },
22435
22436     initComponent : Roo.emptyFn,
22437     /**
22438      * If this is a lazy rendering component, render it to its container element.
22439      * @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.
22440      */
22441     render : function(container, position){
22442         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
22443             if(!container && this.el){
22444                 this.el = Roo.get(this.el);
22445                 container = this.el.dom.parentNode;
22446                 this.allowDomMove = false;
22447             }
22448             this.container = Roo.get(container);
22449             this.rendered = true;
22450             if(position !== undefined){
22451                 if(typeof position == 'number'){
22452                     position = this.container.dom.childNodes[position];
22453                 }else{
22454                     position = Roo.getDom(position);
22455                 }
22456             }
22457             this.onRender(this.container, position || null);
22458             if(this.cls){
22459                 this.el.addClass(this.cls);
22460                 delete this.cls;
22461             }
22462             if(this.style){
22463                 this.el.applyStyles(this.style);
22464                 delete this.style;
22465             }
22466             this.fireEvent("render", this);
22467             this.afterRender(this.container);
22468             if(this.hidden){
22469                 this.hide();
22470             }
22471             if(this.disabled){
22472                 this.disable();
22473             }
22474         }
22475         return this;
22476     },
22477
22478     /** @private */
22479     // default function is not really useful
22480     onRender : function(ct, position){
22481         if(this.el){
22482             this.el = Roo.get(this.el);
22483             if(this.allowDomMove !== false){
22484                 ct.dom.insertBefore(this.el.dom, position);
22485             }
22486         }
22487     },
22488
22489     /** @private */
22490     getAutoCreate : function(){
22491         var cfg = typeof this.autoCreate == "object" ?
22492                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
22493         if(this.id && !cfg.id){
22494             cfg.id = this.id;
22495         }
22496         return cfg;
22497     },
22498
22499     /** @private */
22500     afterRender : Roo.emptyFn,
22501
22502     /**
22503      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22504      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22505      */
22506     destroy : function(){
22507         if(this.fireEvent("beforedestroy", this) !== false){
22508             this.purgeListeners();
22509             this.beforeDestroy();
22510             if(this.rendered){
22511                 this.el.removeAllListeners();
22512                 this.el.remove();
22513                 if(this.actionMode == "container"){
22514                     this.container.remove();
22515                 }
22516             }
22517             this.onDestroy();
22518             Roo.ComponentMgr.unregister(this);
22519             this.fireEvent("destroy", this);
22520         }
22521     },
22522
22523         /** @private */
22524     beforeDestroy : function(){
22525
22526     },
22527
22528         /** @private */
22529         onDestroy : function(){
22530
22531     },
22532
22533     /**
22534      * Returns the underlying {@link Roo.Element}.
22535      * @return {Roo.Element} The element
22536      */
22537     getEl : function(){
22538         return this.el;
22539     },
22540
22541     /**
22542      * Returns the id of this component.
22543      * @return {String}
22544      */
22545     getId : function(){
22546         return this.id;
22547     },
22548
22549     /**
22550      * Try to focus this component.
22551      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22552      * @return {Roo.Component} this
22553      */
22554     focus : function(selectText){
22555         if(this.rendered){
22556             this.el.focus();
22557             if(selectText === true){
22558                 this.el.dom.select();
22559             }
22560         }
22561         return this;
22562     },
22563
22564     /** @private */
22565     blur : function(){
22566         if(this.rendered){
22567             this.el.blur();
22568         }
22569         return this;
22570     },
22571
22572     /**
22573      * Disable this component.
22574      * @return {Roo.Component} this
22575      */
22576     disable : function(){
22577         if(this.rendered){
22578             this.onDisable();
22579         }
22580         this.disabled = true;
22581         this.fireEvent("disable", this);
22582         return this;
22583     },
22584
22585         // private
22586     onDisable : function(){
22587         this.getActionEl().addClass(this.disabledClass);
22588         this.el.dom.disabled = true;
22589     },
22590
22591     /**
22592      * Enable this component.
22593      * @return {Roo.Component} this
22594      */
22595     enable : function(){
22596         if(this.rendered){
22597             this.onEnable();
22598         }
22599         this.disabled = false;
22600         this.fireEvent("enable", this);
22601         return this;
22602     },
22603
22604         // private
22605     onEnable : function(){
22606         this.getActionEl().removeClass(this.disabledClass);
22607         this.el.dom.disabled = false;
22608     },
22609
22610     /**
22611      * Convenience function for setting disabled/enabled by boolean.
22612      * @param {Boolean} disabled
22613      */
22614     setDisabled : function(disabled){
22615         this[disabled ? "disable" : "enable"]();
22616     },
22617
22618     /**
22619      * Show this component.
22620      * @return {Roo.Component} this
22621      */
22622     show: function(){
22623         if(this.fireEvent("beforeshow", this) !== false){
22624             this.hidden = false;
22625             if(this.rendered){
22626                 this.onShow();
22627             }
22628             this.fireEvent("show", this);
22629         }
22630         return this;
22631     },
22632
22633     // private
22634     onShow : function(){
22635         var ae = this.getActionEl();
22636         if(this.hideMode == 'visibility'){
22637             ae.dom.style.visibility = "visible";
22638         }else if(this.hideMode == 'offsets'){
22639             ae.removeClass('x-hidden');
22640         }else{
22641             ae.dom.style.display = "";
22642         }
22643     },
22644
22645     /**
22646      * Hide this component.
22647      * @return {Roo.Component} this
22648      */
22649     hide: function(){
22650         if(this.fireEvent("beforehide", this) !== false){
22651             this.hidden = true;
22652             if(this.rendered){
22653                 this.onHide();
22654             }
22655             this.fireEvent("hide", this);
22656         }
22657         return this;
22658     },
22659
22660     // private
22661     onHide : function(){
22662         var ae = this.getActionEl();
22663         if(this.hideMode == 'visibility'){
22664             ae.dom.style.visibility = "hidden";
22665         }else if(this.hideMode == 'offsets'){
22666             ae.addClass('x-hidden');
22667         }else{
22668             ae.dom.style.display = "none";
22669         }
22670     },
22671
22672     /**
22673      * Convenience function to hide or show this component by boolean.
22674      * @param {Boolean} visible True to show, false to hide
22675      * @return {Roo.Component} this
22676      */
22677     setVisible: function(visible){
22678         if(visible) {
22679             this.show();
22680         }else{
22681             this.hide();
22682         }
22683         return this;
22684     },
22685
22686     /**
22687      * Returns true if this component is visible.
22688      */
22689     isVisible : function(){
22690         return this.getActionEl().isVisible();
22691     },
22692
22693     cloneConfig : function(overrides){
22694         overrides = overrides || {};
22695         var id = overrides.id || Roo.id();
22696         var cfg = Roo.applyIf(overrides, this.initialConfig);
22697         cfg.id = id; // prevent dup id
22698         return new this.constructor(cfg);
22699     }
22700 });/*
22701  * Based on:
22702  * Ext JS Library 1.1.1
22703  * Copyright(c) 2006-2007, Ext JS, LLC.
22704  *
22705  * Originally Released Under LGPL - original licence link has changed is not relivant.
22706  *
22707  * Fork - LGPL
22708  * <script type="text/javascript">
22709  */
22710  (function(){ 
22711 /**
22712  * @class Roo.Layer
22713  * @extends Roo.Element
22714  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22715  * automatic maintaining of shadow/shim positions.
22716  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22717  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22718  * you can pass a string with a CSS class name. False turns off the shadow.
22719  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22720  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22721  * @cfg {String} cls CSS class to add to the element
22722  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22723  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22724  * @constructor
22725  * @param {Object} config An object with config options.
22726  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22727  */
22728
22729 Roo.Layer = function(config, existingEl){
22730     config = config || {};
22731     var dh = Roo.DomHelper;
22732     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22733     if(existingEl){
22734         this.dom = Roo.getDom(existingEl);
22735     }
22736     if(!this.dom){
22737         var o = config.dh || {tag: "div", cls: "x-layer"};
22738         this.dom = dh.append(pel, o);
22739     }
22740     if(config.cls){
22741         this.addClass(config.cls);
22742     }
22743     this.constrain = config.constrain !== false;
22744     this.visibilityMode = Roo.Element.VISIBILITY;
22745     if(config.id){
22746         this.id = this.dom.id = config.id;
22747     }else{
22748         this.id = Roo.id(this.dom);
22749     }
22750     this.zindex = config.zindex || this.getZIndex();
22751     this.position("absolute", this.zindex);
22752     if(config.shadow){
22753         this.shadowOffset = config.shadowOffset || 4;
22754         this.shadow = new Roo.Shadow({
22755             offset : this.shadowOffset,
22756             mode : config.shadow
22757         });
22758     }else{
22759         this.shadowOffset = 0;
22760     }
22761     this.useShim = config.shim !== false && Roo.useShims;
22762     this.useDisplay = config.useDisplay;
22763     this.hide();
22764 };
22765
22766 var supr = Roo.Element.prototype;
22767
22768 // shims are shared among layer to keep from having 100 iframes
22769 var shims = [];
22770
22771 Roo.extend(Roo.Layer, Roo.Element, {
22772
22773     getZIndex : function(){
22774         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22775     },
22776
22777     getShim : function(){
22778         if(!this.useShim){
22779             return null;
22780         }
22781         if(this.shim){
22782             return this.shim;
22783         }
22784         var shim = shims.shift();
22785         if(!shim){
22786             shim = this.createShim();
22787             shim.enableDisplayMode('block');
22788             shim.dom.style.display = 'none';
22789             shim.dom.style.visibility = 'visible';
22790         }
22791         var pn = this.dom.parentNode;
22792         if(shim.dom.parentNode != pn){
22793             pn.insertBefore(shim.dom, this.dom);
22794         }
22795         shim.setStyle('z-index', this.getZIndex()-2);
22796         this.shim = shim;
22797         return shim;
22798     },
22799
22800     hideShim : function(){
22801         if(this.shim){
22802             this.shim.setDisplayed(false);
22803             shims.push(this.shim);
22804             delete this.shim;
22805         }
22806     },
22807
22808     disableShadow : function(){
22809         if(this.shadow){
22810             this.shadowDisabled = true;
22811             this.shadow.hide();
22812             this.lastShadowOffset = this.shadowOffset;
22813             this.shadowOffset = 0;
22814         }
22815     },
22816
22817     enableShadow : function(show){
22818         if(this.shadow){
22819             this.shadowDisabled = false;
22820             this.shadowOffset = this.lastShadowOffset;
22821             delete this.lastShadowOffset;
22822             if(show){
22823                 this.sync(true);
22824             }
22825         }
22826     },
22827
22828     // private
22829     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22830     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22831     sync : function(doShow){
22832         var sw = this.shadow;
22833         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22834             var sh = this.getShim();
22835
22836             var w = this.getWidth(),
22837                 h = this.getHeight();
22838
22839             var l = this.getLeft(true),
22840                 t = this.getTop(true);
22841
22842             if(sw && !this.shadowDisabled){
22843                 if(doShow && !sw.isVisible()){
22844                     sw.show(this);
22845                 }else{
22846                     sw.realign(l, t, w, h);
22847                 }
22848                 if(sh){
22849                     if(doShow){
22850                        sh.show();
22851                     }
22852                     // fit the shim behind the shadow, so it is shimmed too
22853                     var a = sw.adjusts, s = sh.dom.style;
22854                     s.left = (Math.min(l, l+a.l))+"px";
22855                     s.top = (Math.min(t, t+a.t))+"px";
22856                     s.width = (w+a.w)+"px";
22857                     s.height = (h+a.h)+"px";
22858                 }
22859             }else if(sh){
22860                 if(doShow){
22861                    sh.show();
22862                 }
22863                 sh.setSize(w, h);
22864                 sh.setLeftTop(l, t);
22865             }
22866             
22867         }
22868     },
22869
22870     // private
22871     destroy : function(){
22872         this.hideShim();
22873         if(this.shadow){
22874             this.shadow.hide();
22875         }
22876         this.removeAllListeners();
22877         var pn = this.dom.parentNode;
22878         if(pn){
22879             pn.removeChild(this.dom);
22880         }
22881         Roo.Element.uncache(this.id);
22882     },
22883
22884     remove : function(){
22885         this.destroy();
22886     },
22887
22888     // private
22889     beginUpdate : function(){
22890         this.updating = true;
22891     },
22892
22893     // private
22894     endUpdate : function(){
22895         this.updating = false;
22896         this.sync(true);
22897     },
22898
22899     // private
22900     hideUnders : function(negOffset){
22901         if(this.shadow){
22902             this.shadow.hide();
22903         }
22904         this.hideShim();
22905     },
22906
22907     // private
22908     constrainXY : function(){
22909         if(this.constrain){
22910             var vw = Roo.lib.Dom.getViewWidth(),
22911                 vh = Roo.lib.Dom.getViewHeight();
22912             var s = Roo.get(document).getScroll();
22913
22914             var xy = this.getXY();
22915             var x = xy[0], y = xy[1];   
22916             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22917             // only move it if it needs it
22918             var moved = false;
22919             // first validate right/bottom
22920             if((x + w) > vw+s.left){
22921                 x = vw - w - this.shadowOffset;
22922                 moved = true;
22923             }
22924             if((y + h) > vh+s.top){
22925                 y = vh - h - this.shadowOffset;
22926                 moved = true;
22927             }
22928             // then make sure top/left isn't negative
22929             if(x < s.left){
22930                 x = s.left;
22931                 moved = true;
22932             }
22933             if(y < s.top){
22934                 y = s.top;
22935                 moved = true;
22936             }
22937             if(moved){
22938                 if(this.avoidY){
22939                     var ay = this.avoidY;
22940                     if(y <= ay && (y+h) >= ay){
22941                         y = ay-h-5;   
22942                     }
22943                 }
22944                 xy = [x, y];
22945                 this.storeXY(xy);
22946                 supr.setXY.call(this, xy);
22947                 this.sync();
22948             }
22949         }
22950     },
22951
22952     isVisible : function(){
22953         return this.visible;    
22954     },
22955
22956     // private
22957     showAction : function(){
22958         this.visible = true; // track visibility to prevent getStyle calls
22959         if(this.useDisplay === true){
22960             this.setDisplayed("");
22961         }else if(this.lastXY){
22962             supr.setXY.call(this, this.lastXY);
22963         }else if(this.lastLT){
22964             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22965         }
22966     },
22967
22968     // private
22969     hideAction : function(){
22970         this.visible = false;
22971         if(this.useDisplay === true){
22972             this.setDisplayed(false);
22973         }else{
22974             this.setLeftTop(-10000,-10000);
22975         }
22976     },
22977
22978     // overridden Element method
22979     setVisible : function(v, a, d, c, e){
22980         if(v){
22981             this.showAction();
22982         }
22983         if(a && v){
22984             var cb = function(){
22985                 this.sync(true);
22986                 if(c){
22987                     c();
22988                 }
22989             }.createDelegate(this);
22990             supr.setVisible.call(this, true, true, d, cb, e);
22991         }else{
22992             if(!v){
22993                 this.hideUnders(true);
22994             }
22995             var cb = c;
22996             if(a){
22997                 cb = function(){
22998                     this.hideAction();
22999                     if(c){
23000                         c();
23001                     }
23002                 }.createDelegate(this);
23003             }
23004             supr.setVisible.call(this, v, a, d, cb, e);
23005             if(v){
23006                 this.sync(true);
23007             }else if(!a){
23008                 this.hideAction();
23009             }
23010         }
23011     },
23012
23013     storeXY : function(xy){
23014         delete this.lastLT;
23015         this.lastXY = xy;
23016     },
23017
23018     storeLeftTop : function(left, top){
23019         delete this.lastXY;
23020         this.lastLT = [left, top];
23021     },
23022
23023     // private
23024     beforeFx : function(){
23025         this.beforeAction();
23026         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23027     },
23028
23029     // private
23030     afterFx : function(){
23031         Roo.Layer.superclass.afterFx.apply(this, arguments);
23032         this.sync(this.isVisible());
23033     },
23034
23035     // private
23036     beforeAction : function(){
23037         if(!this.updating && this.shadow){
23038             this.shadow.hide();
23039         }
23040     },
23041
23042     // overridden Element method
23043     setLeft : function(left){
23044         this.storeLeftTop(left, this.getTop(true));
23045         supr.setLeft.apply(this, arguments);
23046         this.sync();
23047     },
23048
23049     setTop : function(top){
23050         this.storeLeftTop(this.getLeft(true), top);
23051         supr.setTop.apply(this, arguments);
23052         this.sync();
23053     },
23054
23055     setLeftTop : function(left, top){
23056         this.storeLeftTop(left, top);
23057         supr.setLeftTop.apply(this, arguments);
23058         this.sync();
23059     },
23060
23061     setXY : function(xy, a, d, c, e){
23062         this.fixDisplay();
23063         this.beforeAction();
23064         this.storeXY(xy);
23065         var cb = this.createCB(c);
23066         supr.setXY.call(this, xy, a, d, cb, e);
23067         if(!a){
23068             cb();
23069         }
23070     },
23071
23072     // private
23073     createCB : function(c){
23074         var el = this;
23075         return function(){
23076             el.constrainXY();
23077             el.sync(true);
23078             if(c){
23079                 c();
23080             }
23081         };
23082     },
23083
23084     // overridden Element method
23085     setX : function(x, a, d, c, e){
23086         this.setXY([x, this.getY()], a, d, c, e);
23087     },
23088
23089     // overridden Element method
23090     setY : function(y, a, d, c, e){
23091         this.setXY([this.getX(), y], a, d, c, e);
23092     },
23093
23094     // overridden Element method
23095     setSize : function(w, h, a, d, c, e){
23096         this.beforeAction();
23097         var cb = this.createCB(c);
23098         supr.setSize.call(this, w, h, a, d, cb, e);
23099         if(!a){
23100             cb();
23101         }
23102     },
23103
23104     // overridden Element method
23105     setWidth : function(w, a, d, c, e){
23106         this.beforeAction();
23107         var cb = this.createCB(c);
23108         supr.setWidth.call(this, w, a, d, cb, e);
23109         if(!a){
23110             cb();
23111         }
23112     },
23113
23114     // overridden Element method
23115     setHeight : function(h, a, d, c, e){
23116         this.beforeAction();
23117         var cb = this.createCB(c);
23118         supr.setHeight.call(this, h, a, d, cb, e);
23119         if(!a){
23120             cb();
23121         }
23122     },
23123
23124     // overridden Element method
23125     setBounds : function(x, y, w, h, a, d, c, e){
23126         this.beforeAction();
23127         var cb = this.createCB(c);
23128         if(!a){
23129             this.storeXY([x, y]);
23130             supr.setXY.call(this, [x, y]);
23131             supr.setSize.call(this, w, h, a, d, cb, e);
23132             cb();
23133         }else{
23134             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
23135         }
23136         return this;
23137     },
23138     
23139     /**
23140      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
23141      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
23142      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
23143      * @param {Number} zindex The new z-index to set
23144      * @return {this} The Layer
23145      */
23146     setZIndex : function(zindex){
23147         this.zindex = zindex;
23148         this.setStyle("z-index", zindex + 2);
23149         if(this.shadow){
23150             this.shadow.setZIndex(zindex + 1);
23151         }
23152         if(this.shim){
23153             this.shim.setStyle("z-index", zindex);
23154         }
23155     }
23156 });
23157 })();/*
23158  * Based on:
23159  * Ext JS Library 1.1.1
23160  * Copyright(c) 2006-2007, Ext JS, LLC.
23161  *
23162  * Originally Released Under LGPL - original licence link has changed is not relivant.
23163  *
23164  * Fork - LGPL
23165  * <script type="text/javascript">
23166  */
23167
23168
23169 /**
23170  * @class Roo.Shadow
23171  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
23172  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
23173  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
23174  * @constructor
23175  * Create a new Shadow
23176  * @param {Object} config The config object
23177  */
23178 Roo.Shadow = function(config){
23179     Roo.apply(this, config);
23180     if(typeof this.mode != "string"){
23181         this.mode = this.defaultMode;
23182     }
23183     var o = this.offset, a = {h: 0};
23184     var rad = Math.floor(this.offset/2);
23185     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
23186         case "drop":
23187             a.w = 0;
23188             a.l = a.t = o;
23189             a.t -= 1;
23190             if(Roo.isIE){
23191                 a.l -= this.offset + rad;
23192                 a.t -= this.offset + rad;
23193                 a.w -= rad;
23194                 a.h -= rad;
23195                 a.t += 1;
23196             }
23197         break;
23198         case "sides":
23199             a.w = (o*2);
23200             a.l = -o;
23201             a.t = o-1;
23202             if(Roo.isIE){
23203                 a.l -= (this.offset - rad);
23204                 a.t -= this.offset + rad;
23205                 a.l += 1;
23206                 a.w -= (this.offset - rad)*2;
23207                 a.w -= rad + 1;
23208                 a.h -= 1;
23209             }
23210         break;
23211         case "frame":
23212             a.w = a.h = (o*2);
23213             a.l = a.t = -o;
23214             a.t += 1;
23215             a.h -= 2;
23216             if(Roo.isIE){
23217                 a.l -= (this.offset - rad);
23218                 a.t -= (this.offset - rad);
23219                 a.l += 1;
23220                 a.w -= (this.offset + rad + 1);
23221                 a.h -= (this.offset + rad);
23222                 a.h += 1;
23223             }
23224         break;
23225     };
23226
23227     this.adjusts = a;
23228 };
23229
23230 Roo.Shadow.prototype = {
23231     /**
23232      * @cfg {String} mode
23233      * The shadow display mode.  Supports the following options:<br />
23234      * sides: Shadow displays on both sides and bottom only<br />
23235      * frame: Shadow displays equally on all four sides<br />
23236      * drop: Traditional bottom-right drop shadow (default)
23237      */
23238     /**
23239      * @cfg {String} offset
23240      * The number of pixels to offset the shadow from the element (defaults to 4)
23241      */
23242     offset: 4,
23243
23244     // private
23245     defaultMode: "drop",
23246
23247     /**
23248      * Displays the shadow under the target element
23249      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
23250      */
23251     show : function(target){
23252         target = Roo.get(target);
23253         if(!this.el){
23254             this.el = Roo.Shadow.Pool.pull();
23255             if(this.el.dom.nextSibling != target.dom){
23256                 this.el.insertBefore(target);
23257             }
23258         }
23259         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
23260         if(Roo.isIE){
23261             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
23262         }
23263         this.realign(
23264             target.getLeft(true),
23265             target.getTop(true),
23266             target.getWidth(),
23267             target.getHeight()
23268         );
23269         this.el.dom.style.display = "block";
23270     },
23271
23272     /**
23273      * Returns true if the shadow is visible, else false
23274      */
23275     isVisible : function(){
23276         return this.el ? true : false;  
23277     },
23278
23279     /**
23280      * Direct alignment when values are already available. Show must be called at least once before
23281      * calling this method to ensure it is initialized.
23282      * @param {Number} left The target element left position
23283      * @param {Number} top The target element top position
23284      * @param {Number} width The target element width
23285      * @param {Number} height The target element height
23286      */
23287     realign : function(l, t, w, h){
23288         if(!this.el){
23289             return;
23290         }
23291         var a = this.adjusts, d = this.el.dom, s = d.style;
23292         var iea = 0;
23293         s.left = (l+a.l)+"px";
23294         s.top = (t+a.t)+"px";
23295         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
23296  
23297         if(s.width != sws || s.height != shs){
23298             s.width = sws;
23299             s.height = shs;
23300             if(!Roo.isIE){
23301                 var cn = d.childNodes;
23302                 var sww = Math.max(0, (sw-12))+"px";
23303                 cn[0].childNodes[1].style.width = sww;
23304                 cn[1].childNodes[1].style.width = sww;
23305                 cn[2].childNodes[1].style.width = sww;
23306                 cn[1].style.height = Math.max(0, (sh-12))+"px";
23307             }
23308         }
23309     },
23310
23311     /**
23312      * Hides this shadow
23313      */
23314     hide : function(){
23315         if(this.el){
23316             this.el.dom.style.display = "none";
23317             Roo.Shadow.Pool.push(this.el);
23318             delete this.el;
23319         }
23320     },
23321
23322     /**
23323      * Adjust the z-index of this shadow
23324      * @param {Number} zindex The new z-index
23325      */
23326     setZIndex : function(z){
23327         this.zIndex = z;
23328         if(this.el){
23329             this.el.setStyle("z-index", z);
23330         }
23331     }
23332 };
23333
23334 // Private utility class that manages the internal Shadow cache
23335 Roo.Shadow.Pool = function(){
23336     var p = [];
23337     var markup = Roo.isIE ?
23338                  '<div class="x-ie-shadow"></div>' :
23339                  '<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>';
23340     return {
23341         pull : function(){
23342             var sh = p.shift();
23343             if(!sh){
23344                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
23345                 sh.autoBoxAdjust = false;
23346             }
23347             return sh;
23348         },
23349
23350         push : function(sh){
23351             p.push(sh);
23352         }
23353     };
23354 }();/*
23355  * Based on:
23356  * Ext JS Library 1.1.1
23357  * Copyright(c) 2006-2007, Ext JS, LLC.
23358  *
23359  * Originally Released Under LGPL - original licence link has changed is not relivant.
23360  *
23361  * Fork - LGPL
23362  * <script type="text/javascript">
23363  */
23364
23365 /**
23366  * @class Roo.BoxComponent
23367  * @extends Roo.Component
23368  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
23369  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
23370  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
23371  * layout containers.
23372  * @constructor
23373  * @param {Roo.Element/String/Object} config The configuration options.
23374  */
23375 Roo.BoxComponent = function(config){
23376     Roo.Component.call(this, config);
23377     this.addEvents({
23378         /**
23379          * @event resize
23380          * Fires after the component is resized.
23381              * @param {Roo.Component} this
23382              * @param {Number} adjWidth The box-adjusted width that was set
23383              * @param {Number} adjHeight The box-adjusted height that was set
23384              * @param {Number} rawWidth The width that was originally specified
23385              * @param {Number} rawHeight The height that was originally specified
23386              */
23387         resize : true,
23388         /**
23389          * @event move
23390          * Fires after the component is moved.
23391              * @param {Roo.Component} this
23392              * @param {Number} x The new x position
23393              * @param {Number} y The new y position
23394              */
23395         move : true
23396     });
23397 };
23398
23399 Roo.extend(Roo.BoxComponent, Roo.Component, {
23400     // private, set in afterRender to signify that the component has been rendered
23401     boxReady : false,
23402     // private, used to defer height settings to subclasses
23403     deferHeight: false,
23404     /** @cfg {Number} width
23405      * width (optional) size of component
23406      */
23407      /** @cfg {Number} height
23408      * height (optional) size of component
23409      */
23410      
23411     /**
23412      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
23413      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
23414      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
23415      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
23416      * @return {Roo.BoxComponent} this
23417      */
23418     setSize : function(w, h){
23419         // support for standard size objects
23420         if(typeof w == 'object'){
23421             h = w.height;
23422             w = w.width;
23423         }
23424         // not rendered
23425         if(!this.boxReady){
23426             this.width = w;
23427             this.height = h;
23428             return this;
23429         }
23430
23431         // prevent recalcs when not needed
23432         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
23433             return this;
23434         }
23435         this.lastSize = {width: w, height: h};
23436
23437         var adj = this.adjustSize(w, h);
23438         var aw = adj.width, ah = adj.height;
23439         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
23440             var rz = this.getResizeEl();
23441             if(!this.deferHeight && aw !== undefined && ah !== undefined){
23442                 rz.setSize(aw, ah);
23443             }else if(!this.deferHeight && ah !== undefined){
23444                 rz.setHeight(ah);
23445             }else if(aw !== undefined){
23446                 rz.setWidth(aw);
23447             }
23448             this.onResize(aw, ah, w, h);
23449             this.fireEvent('resize', this, aw, ah, w, h);
23450         }
23451         return this;
23452     },
23453
23454     /**
23455      * Gets the current size of the component's underlying element.
23456      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
23457      */
23458     getSize : function(){
23459         return this.el.getSize();
23460     },
23461
23462     /**
23463      * Gets the current XY position of the component's underlying element.
23464      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23465      * @return {Array} The XY position of the element (e.g., [100, 200])
23466      */
23467     getPosition : function(local){
23468         if(local === true){
23469             return [this.el.getLeft(true), this.el.getTop(true)];
23470         }
23471         return this.xy || this.el.getXY();
23472     },
23473
23474     /**
23475      * Gets the current box measurements of the component's underlying element.
23476      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
23477      * @returns {Object} box An object in the format {x, y, width, height}
23478      */
23479     getBox : function(local){
23480         var s = this.el.getSize();
23481         if(local){
23482             s.x = this.el.getLeft(true);
23483             s.y = this.el.getTop(true);
23484         }else{
23485             var xy = this.xy || this.el.getXY();
23486             s.x = xy[0];
23487             s.y = xy[1];
23488         }
23489         return s;
23490     },
23491
23492     /**
23493      * Sets the current box measurements of the component's underlying element.
23494      * @param {Object} box An object in the format {x, y, width, height}
23495      * @returns {Roo.BoxComponent} this
23496      */
23497     updateBox : function(box){
23498         this.setSize(box.width, box.height);
23499         this.setPagePosition(box.x, box.y);
23500         return this;
23501     },
23502
23503     // protected
23504     getResizeEl : function(){
23505         return this.resizeEl || this.el;
23506     },
23507
23508     // protected
23509     getPositionEl : function(){
23510         return this.positionEl || this.el;
23511     },
23512
23513     /**
23514      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23515      * This method fires the move event.
23516      * @param {Number} left The new left
23517      * @param {Number} top The new top
23518      * @returns {Roo.BoxComponent} this
23519      */
23520     setPosition : function(x, y){
23521         this.x = x;
23522         this.y = y;
23523         if(!this.boxReady){
23524             return this;
23525         }
23526         var adj = this.adjustPosition(x, y);
23527         var ax = adj.x, ay = adj.y;
23528
23529         var el = this.getPositionEl();
23530         if(ax !== undefined || ay !== undefined){
23531             if(ax !== undefined && ay !== undefined){
23532                 el.setLeftTop(ax, ay);
23533             }else if(ax !== undefined){
23534                 el.setLeft(ax);
23535             }else if(ay !== undefined){
23536                 el.setTop(ay);
23537             }
23538             this.onPosition(ax, ay);
23539             this.fireEvent('move', this, ax, ay);
23540         }
23541         return this;
23542     },
23543
23544     /**
23545      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23546      * This method fires the move event.
23547      * @param {Number} x The new x position
23548      * @param {Number} y The new y position
23549      * @returns {Roo.BoxComponent} this
23550      */
23551     setPagePosition : function(x, y){
23552         this.pageX = x;
23553         this.pageY = y;
23554         if(!this.boxReady){
23555             return;
23556         }
23557         if(x === undefined || y === undefined){ // cannot translate undefined points
23558             return;
23559         }
23560         var p = this.el.translatePoints(x, y);
23561         this.setPosition(p.left, p.top);
23562         return this;
23563     },
23564
23565     // private
23566     onRender : function(ct, position){
23567         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23568         if(this.resizeEl){
23569             this.resizeEl = Roo.get(this.resizeEl);
23570         }
23571         if(this.positionEl){
23572             this.positionEl = Roo.get(this.positionEl);
23573         }
23574     },
23575
23576     // private
23577     afterRender : function(){
23578         Roo.BoxComponent.superclass.afterRender.call(this);
23579         this.boxReady = true;
23580         this.setSize(this.width, this.height);
23581         if(this.x || this.y){
23582             this.setPosition(this.x, this.y);
23583         }
23584         if(this.pageX || this.pageY){
23585             this.setPagePosition(this.pageX, this.pageY);
23586         }
23587     },
23588
23589     /**
23590      * Force the component's size to recalculate based on the underlying element's current height and width.
23591      * @returns {Roo.BoxComponent} this
23592      */
23593     syncSize : function(){
23594         delete this.lastSize;
23595         this.setSize(this.el.getWidth(), this.el.getHeight());
23596         return this;
23597     },
23598
23599     /**
23600      * Called after the component is resized, this method is empty by default but can be implemented by any
23601      * subclass that needs to perform custom logic after a resize occurs.
23602      * @param {Number} adjWidth The box-adjusted width that was set
23603      * @param {Number} adjHeight The box-adjusted height that was set
23604      * @param {Number} rawWidth The width that was originally specified
23605      * @param {Number} rawHeight The height that was originally specified
23606      */
23607     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23608
23609     },
23610
23611     /**
23612      * Called after the component is moved, this method is empty by default but can be implemented by any
23613      * subclass that needs to perform custom logic after a move occurs.
23614      * @param {Number} x The new x position
23615      * @param {Number} y The new y position
23616      */
23617     onPosition : function(x, y){
23618
23619     },
23620
23621     // private
23622     adjustSize : function(w, h){
23623         if(this.autoWidth){
23624             w = 'auto';
23625         }
23626         if(this.autoHeight){
23627             h = 'auto';
23628         }
23629         return {width : w, height: h};
23630     },
23631
23632     // private
23633     adjustPosition : function(x, y){
23634         return {x : x, y: y};
23635     }
23636 });/*
23637  * Based on:
23638  * Ext JS Library 1.1.1
23639  * Copyright(c) 2006-2007, Ext JS, LLC.
23640  *
23641  * Originally Released Under LGPL - original licence link has changed is not relivant.
23642  *
23643  * Fork - LGPL
23644  * <script type="text/javascript">
23645  */
23646
23647
23648 /**
23649  * @class Roo.SplitBar
23650  * @extends Roo.util.Observable
23651  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23652  * <br><br>
23653  * Usage:
23654  * <pre><code>
23655 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23656                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23657 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23658 split.minSize = 100;
23659 split.maxSize = 600;
23660 split.animate = true;
23661 split.on('moved', splitterMoved);
23662 </code></pre>
23663  * @constructor
23664  * Create a new SplitBar
23665  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23666  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23667  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23668  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23669                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23670                         position of the SplitBar).
23671  */
23672 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23673     
23674     /** @private */
23675     this.el = Roo.get(dragElement, true);
23676     this.el.dom.unselectable = "on";
23677     /** @private */
23678     this.resizingEl = Roo.get(resizingElement, true);
23679
23680     /**
23681      * @private
23682      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23683      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23684      * @type Number
23685      */
23686     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23687     
23688     /**
23689      * The minimum size of the resizing element. (Defaults to 0)
23690      * @type Number
23691      */
23692     this.minSize = 0;
23693     
23694     /**
23695      * The maximum size of the resizing element. (Defaults to 2000)
23696      * @type Number
23697      */
23698     this.maxSize = 2000;
23699     
23700     /**
23701      * Whether to animate the transition to the new size
23702      * @type Boolean
23703      */
23704     this.animate = false;
23705     
23706     /**
23707      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23708      * @type Boolean
23709      */
23710     this.useShim = false;
23711     
23712     /** @private */
23713     this.shim = null;
23714     
23715     if(!existingProxy){
23716         /** @private */
23717         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23718     }else{
23719         this.proxy = Roo.get(existingProxy).dom;
23720     }
23721     /** @private */
23722     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23723     
23724     /** @private */
23725     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23726     
23727     /** @private */
23728     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23729     
23730     /** @private */
23731     this.dragSpecs = {};
23732     
23733     /**
23734      * @private The adapter to use to positon and resize elements
23735      */
23736     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23737     this.adapter.init(this);
23738     
23739     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23740         /** @private */
23741         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23742         this.el.addClass("x-splitbar-h");
23743     }else{
23744         /** @private */
23745         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23746         this.el.addClass("x-splitbar-v");
23747     }
23748     
23749     this.addEvents({
23750         /**
23751          * @event resize
23752          * Fires when the splitter is moved (alias for {@link #event-moved})
23753          * @param {Roo.SplitBar} this
23754          * @param {Number} newSize the new width or height
23755          */
23756         "resize" : true,
23757         /**
23758          * @event moved
23759          * Fires when the splitter is moved
23760          * @param {Roo.SplitBar} this
23761          * @param {Number} newSize the new width or height
23762          */
23763         "moved" : true,
23764         /**
23765          * @event beforeresize
23766          * Fires before the splitter is dragged
23767          * @param {Roo.SplitBar} this
23768          */
23769         "beforeresize" : true,
23770
23771         "beforeapply" : true
23772     });
23773
23774     Roo.util.Observable.call(this);
23775 };
23776
23777 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23778     onStartProxyDrag : function(x, y){
23779         this.fireEvent("beforeresize", this);
23780         if(!this.overlay){
23781             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23782             o.unselectable();
23783             o.enableDisplayMode("block");
23784             // all splitbars share the same overlay
23785             Roo.SplitBar.prototype.overlay = o;
23786         }
23787         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23788         this.overlay.show();
23789         Roo.get(this.proxy).setDisplayed("block");
23790         var size = this.adapter.getElementSize(this);
23791         this.activeMinSize = this.getMinimumSize();;
23792         this.activeMaxSize = this.getMaximumSize();;
23793         var c1 = size - this.activeMinSize;
23794         var c2 = Math.max(this.activeMaxSize - size, 0);
23795         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23796             this.dd.resetConstraints();
23797             this.dd.setXConstraint(
23798                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23799                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23800             );
23801             this.dd.setYConstraint(0, 0);
23802         }else{
23803             this.dd.resetConstraints();
23804             this.dd.setXConstraint(0, 0);
23805             this.dd.setYConstraint(
23806                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23807                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23808             );
23809          }
23810         this.dragSpecs.startSize = size;
23811         this.dragSpecs.startPoint = [x, y];
23812         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23813     },
23814     
23815     /** 
23816      * @private Called after the drag operation by the DDProxy
23817      */
23818     onEndProxyDrag : function(e){
23819         Roo.get(this.proxy).setDisplayed(false);
23820         var endPoint = Roo.lib.Event.getXY(e);
23821         if(this.overlay){
23822             this.overlay.hide();
23823         }
23824         var newSize;
23825         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23826             newSize = this.dragSpecs.startSize + 
23827                 (this.placement == Roo.SplitBar.LEFT ?
23828                     endPoint[0] - this.dragSpecs.startPoint[0] :
23829                     this.dragSpecs.startPoint[0] - endPoint[0]
23830                 );
23831         }else{
23832             newSize = this.dragSpecs.startSize + 
23833                 (this.placement == Roo.SplitBar.TOP ?
23834                     endPoint[1] - this.dragSpecs.startPoint[1] :
23835                     this.dragSpecs.startPoint[1] - endPoint[1]
23836                 );
23837         }
23838         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23839         if(newSize != this.dragSpecs.startSize){
23840             if(this.fireEvent('beforeapply', this, newSize) !== false){
23841                 this.adapter.setElementSize(this, newSize);
23842                 this.fireEvent("moved", this, newSize);
23843                 this.fireEvent("resize", this, newSize);
23844             }
23845         }
23846     },
23847     
23848     /**
23849      * Get the adapter this SplitBar uses
23850      * @return The adapter object
23851      */
23852     getAdapter : function(){
23853         return this.adapter;
23854     },
23855     
23856     /**
23857      * Set the adapter this SplitBar uses
23858      * @param {Object} adapter A SplitBar adapter object
23859      */
23860     setAdapter : function(adapter){
23861         this.adapter = adapter;
23862         this.adapter.init(this);
23863     },
23864     
23865     /**
23866      * Gets the minimum size for the resizing element
23867      * @return {Number} The minimum size
23868      */
23869     getMinimumSize : function(){
23870         return this.minSize;
23871     },
23872     
23873     /**
23874      * Sets the minimum size for the resizing element
23875      * @param {Number} minSize The minimum size
23876      */
23877     setMinimumSize : function(minSize){
23878         this.minSize = minSize;
23879     },
23880     
23881     /**
23882      * Gets the maximum size for the resizing element
23883      * @return {Number} The maximum size
23884      */
23885     getMaximumSize : function(){
23886         return this.maxSize;
23887     },
23888     
23889     /**
23890      * Sets the maximum size for the resizing element
23891      * @param {Number} maxSize The maximum size
23892      */
23893     setMaximumSize : function(maxSize){
23894         this.maxSize = maxSize;
23895     },
23896     
23897     /**
23898      * Sets the initialize size for the resizing element
23899      * @param {Number} size The initial size
23900      */
23901     setCurrentSize : function(size){
23902         var oldAnimate = this.animate;
23903         this.animate = false;
23904         this.adapter.setElementSize(this, size);
23905         this.animate = oldAnimate;
23906     },
23907     
23908     /**
23909      * Destroy this splitbar. 
23910      * @param {Boolean} removeEl True to remove the element
23911      */
23912     destroy : function(removeEl){
23913         if(this.shim){
23914             this.shim.remove();
23915         }
23916         this.dd.unreg();
23917         this.proxy.parentNode.removeChild(this.proxy);
23918         if(removeEl){
23919             this.el.remove();
23920         }
23921     }
23922 });
23923
23924 /**
23925  * @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.
23926  */
23927 Roo.SplitBar.createProxy = function(dir){
23928     var proxy = new Roo.Element(document.createElement("div"));
23929     proxy.unselectable();
23930     var cls = 'x-splitbar-proxy';
23931     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23932     document.body.appendChild(proxy.dom);
23933     return proxy.dom;
23934 };
23935
23936 /** 
23937  * @class Roo.SplitBar.BasicLayoutAdapter
23938  * Default Adapter. It assumes the splitter and resizing element are not positioned
23939  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23940  */
23941 Roo.SplitBar.BasicLayoutAdapter = function(){
23942 };
23943
23944 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23945     // do nothing for now
23946     init : function(s){
23947     
23948     },
23949     /**
23950      * Called before drag operations to get the current size of the resizing element. 
23951      * @param {Roo.SplitBar} s The SplitBar using this adapter
23952      */
23953      getElementSize : function(s){
23954         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23955             return s.resizingEl.getWidth();
23956         }else{
23957             return s.resizingEl.getHeight();
23958         }
23959     },
23960     
23961     /**
23962      * Called after drag operations to set the size of the resizing element.
23963      * @param {Roo.SplitBar} s The SplitBar using this adapter
23964      * @param {Number} newSize The new size to set
23965      * @param {Function} onComplete A function to be invoked when resizing is complete
23966      */
23967     setElementSize : function(s, newSize, onComplete){
23968         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23969             if(!s.animate){
23970                 s.resizingEl.setWidth(newSize);
23971                 if(onComplete){
23972                     onComplete(s, newSize);
23973                 }
23974             }else{
23975                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23976             }
23977         }else{
23978             
23979             if(!s.animate){
23980                 s.resizingEl.setHeight(newSize);
23981                 if(onComplete){
23982                     onComplete(s, newSize);
23983                 }
23984             }else{
23985                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23986             }
23987         }
23988     }
23989 };
23990
23991 /** 
23992  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23993  * @extends Roo.SplitBar.BasicLayoutAdapter
23994  * Adapter that  moves the splitter element to align with the resized sizing element. 
23995  * Used with an absolute positioned SplitBar.
23996  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23997  * document.body, make sure you assign an id to the body element.
23998  */
23999 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24000     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24001     this.container = Roo.get(container);
24002 };
24003
24004 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24005     init : function(s){
24006         this.basic.init(s);
24007     },
24008     
24009     getElementSize : function(s){
24010         return this.basic.getElementSize(s);
24011     },
24012     
24013     setElementSize : function(s, newSize, onComplete){
24014         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24015     },
24016     
24017     moveSplitter : function(s){
24018         var yes = Roo.SplitBar;
24019         switch(s.placement){
24020             case yes.LEFT:
24021                 s.el.setX(s.resizingEl.getRight());
24022                 break;
24023             case yes.RIGHT:
24024                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24025                 break;
24026             case yes.TOP:
24027                 s.el.setY(s.resizingEl.getBottom());
24028                 break;
24029             case yes.BOTTOM:
24030                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24031                 break;
24032         }
24033     }
24034 };
24035
24036 /**
24037  * Orientation constant - Create a vertical SplitBar
24038  * @static
24039  * @type Number
24040  */
24041 Roo.SplitBar.VERTICAL = 1;
24042
24043 /**
24044  * Orientation constant - Create a horizontal SplitBar
24045  * @static
24046  * @type Number
24047  */
24048 Roo.SplitBar.HORIZONTAL = 2;
24049
24050 /**
24051  * Placement constant - The resizing element is to the left of the splitter element
24052  * @static
24053  * @type Number
24054  */
24055 Roo.SplitBar.LEFT = 1;
24056
24057 /**
24058  * Placement constant - The resizing element is to the right of the splitter element
24059  * @static
24060  * @type Number
24061  */
24062 Roo.SplitBar.RIGHT = 2;
24063
24064 /**
24065  * Placement constant - The resizing element is positioned above the splitter element
24066  * @static
24067  * @type Number
24068  */
24069 Roo.SplitBar.TOP = 3;
24070
24071 /**
24072  * Placement constant - The resizing element is positioned under splitter element
24073  * @static
24074  * @type Number
24075  */
24076 Roo.SplitBar.BOTTOM = 4;
24077 /*
24078  * Based on:
24079  * Ext JS Library 1.1.1
24080  * Copyright(c) 2006-2007, Ext JS, LLC.
24081  *
24082  * Originally Released Under LGPL - original licence link has changed is not relivant.
24083  *
24084  * Fork - LGPL
24085  * <script type="text/javascript">
24086  */
24087
24088 /**
24089  * @class Roo.View
24090  * @extends Roo.util.Observable
24091  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24092  * This class also supports single and multi selection modes. <br>
24093  * Create a data model bound view:
24094  <pre><code>
24095  var store = new Roo.data.Store(...);
24096
24097  var view = new Roo.View({
24098     el : "my-element",
24099     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24100  
24101     singleSelect: true,
24102     selectedClass: "ydataview-selected",
24103     store: store
24104  });
24105
24106  // listen for node click?
24107  view.on("click", function(vw, index, node, e){
24108  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24109  });
24110
24111  // load XML data
24112  dataModel.load("foobar.xml");
24113  </code></pre>
24114  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24115  * <br><br>
24116  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24117  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24118  * 
24119  * Note: old style constructor is still suported (container, template, config)
24120  * 
24121  * @constructor
24122  * Create a new View
24123  * @param {Object} config The config object
24124  * 
24125  */
24126 Roo.View = function(config, depreciated_tpl, depreciated_config){
24127     
24128     if (typeof(depreciated_tpl) == 'undefined') {
24129         // new way.. - universal constructor.
24130         Roo.apply(this, config);
24131         this.el  = Roo.get(this.el);
24132     } else {
24133         // old format..
24134         this.el  = Roo.get(config);
24135         this.tpl = depreciated_tpl;
24136         Roo.apply(this, depreciated_config);
24137     }
24138     this.wrapEl  = this.el.wrap().wrap();
24139     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24140     
24141     
24142     if(typeof(this.tpl) == "string"){
24143         this.tpl = new Roo.Template(this.tpl);
24144     } else {
24145         // support xtype ctors..
24146         this.tpl = new Roo.factory(this.tpl, Roo);
24147     }
24148     
24149     
24150     this.tpl.compile();
24151    
24152   
24153     
24154      
24155     /** @private */
24156     this.addEvents({
24157         /**
24158          * @event beforeclick
24159          * Fires before a click is processed. Returns false to cancel the default action.
24160          * @param {Roo.View} this
24161          * @param {Number} index The index of the target node
24162          * @param {HTMLElement} node The target node
24163          * @param {Roo.EventObject} e The raw event object
24164          */
24165             "beforeclick" : true,
24166         /**
24167          * @event click
24168          * Fires when a template node is clicked.
24169          * @param {Roo.View} this
24170          * @param {Number} index The index of the target node
24171          * @param {HTMLElement} node The target node
24172          * @param {Roo.EventObject} e The raw event object
24173          */
24174             "click" : true,
24175         /**
24176          * @event dblclick
24177          * Fires when a template node is double clicked.
24178          * @param {Roo.View} this
24179          * @param {Number} index The index of the target node
24180          * @param {HTMLElement} node The target node
24181          * @param {Roo.EventObject} e The raw event object
24182          */
24183             "dblclick" : true,
24184         /**
24185          * @event contextmenu
24186          * Fires when a template node is right clicked.
24187          * @param {Roo.View} this
24188          * @param {Number} index The index of the target node
24189          * @param {HTMLElement} node The target node
24190          * @param {Roo.EventObject} e The raw event object
24191          */
24192             "contextmenu" : true,
24193         /**
24194          * @event selectionchange
24195          * Fires when the selected nodes change.
24196          * @param {Roo.View} this
24197          * @param {Array} selections Array of the selected nodes
24198          */
24199             "selectionchange" : true,
24200     
24201         /**
24202          * @event beforeselect
24203          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24204          * @param {Roo.View} this
24205          * @param {HTMLElement} node The node to be selected
24206          * @param {Array} selections Array of currently selected nodes
24207          */
24208             "beforeselect" : true,
24209         /**
24210          * @event preparedata
24211          * Fires on every row to render, to allow you to change the data.
24212          * @param {Roo.View} this
24213          * @param {Object} data to be rendered (change this)
24214          */
24215           "preparedata" : true
24216           
24217           
24218         });
24219
24220
24221
24222     this.el.on({
24223         "click": this.onClick,
24224         "dblclick": this.onDblClick,
24225         "contextmenu": this.onContextMenu,
24226         scope:this
24227     });
24228
24229     this.selections = [];
24230     this.nodes = [];
24231     this.cmp = new Roo.CompositeElementLite([]);
24232     if(this.store){
24233         this.store = Roo.factory(this.store, Roo.data);
24234         this.setStore(this.store, true);
24235     }
24236     
24237     if ( this.footer && this.footer.xtype) {
24238            
24239          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24240         
24241         this.footer.dataSource = this.store
24242         this.footer.container = fctr;
24243         this.footer = Roo.factory(this.footer, Roo);
24244         fctr.insertFirst(this.el);
24245         
24246         // this is a bit insane - as the paging toolbar seems to detach the el..
24247 //        dom.parentNode.parentNode.parentNode
24248          // they get detached?
24249     }
24250     
24251     
24252     Roo.View.superclass.constructor.call(this);
24253     
24254     
24255 };
24256
24257 Roo.extend(Roo.View, Roo.util.Observable, {
24258     
24259      /**
24260      * @cfg {Roo.data.Store} store Data store to load data from.
24261      */
24262     store : false,
24263     
24264     /**
24265      * @cfg {String|Roo.Element} el The container element.
24266      */
24267     el : '',
24268     
24269     /**
24270      * @cfg {String|Roo.Template} tpl The template used by this View 
24271      */
24272     tpl : false,
24273     /**
24274      * @cfg {String} dataName the named area of the template to use as the data area
24275      *                          Works with domtemplates roo-name="name"
24276      */
24277     dataName: false,
24278     /**
24279      * @cfg {String} selectedClass The css class to add to selected nodes
24280      */
24281     selectedClass : "x-view-selected",
24282      /**
24283      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24284      */
24285     emptyText : "",
24286     
24287     /**
24288      * @cfg {String} text to display on mask (default Loading)
24289      */
24290     mask : false,
24291     /**
24292      * @cfg {Boolean} multiSelect Allow multiple selection
24293      */
24294     multiSelect : false,
24295     /**
24296      * @cfg {Boolean} singleSelect Allow single selection
24297      */
24298     singleSelect:  false,
24299     
24300     /**
24301      * @cfg {Boolean} toggleSelect - selecting 
24302      */
24303     toggleSelect : false,
24304     
24305     /**
24306      * Returns the element this view is bound to.
24307      * @return {Roo.Element}
24308      */
24309     getEl : function(){
24310         return this.wrapEl;
24311     },
24312     
24313     
24314
24315     /**
24316      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24317      */
24318     refresh : function(){
24319         var t = this.tpl;
24320         
24321         // if we are using something like 'domtemplate', then
24322         // the what gets used is:
24323         // t.applySubtemplate(NAME, data, wrapping data..)
24324         // the outer template then get' applied with
24325         //     the store 'extra data'
24326         // and the body get's added to the
24327         //      roo-name="data" node?
24328         //      <span class='roo-tpl-{name}'></span> ?????
24329         
24330         
24331         
24332         this.clearSelections();
24333         this.el.update("");
24334         var html = [];
24335         var records = this.store.getRange();
24336         if(records.length < 1) {
24337             
24338             // is this valid??  = should it render a template??
24339             
24340             this.el.update(this.emptyText);
24341             return;
24342         }
24343         var el = this.el;
24344         if (this.dataName) {
24345             this.el.update(t.apply(this.store.meta)); //????
24346             el = this.el.child('.roo-tpl-' + this.dataName);
24347         }
24348         
24349         for(var i = 0, len = records.length; i < len; i++){
24350             var data = this.prepareData(records[i].data, i, records[i]);
24351             this.fireEvent("preparedata", this, data, i, records[i]);
24352             html[html.length] = Roo.util.Format.trim(
24353                 this.dataName ?
24354                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24355                     t.apply(data)
24356             );
24357         }
24358         
24359         
24360         
24361         el.update(html.join(""));
24362         this.nodes = el.dom.childNodes;
24363         this.updateIndexes(0);
24364     },
24365
24366     /**
24367      * Function to override to reformat the data that is sent to
24368      * the template for each node.
24369      * DEPRICATED - use the preparedata event handler.
24370      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24371      * a JSON object for an UpdateManager bound view).
24372      */
24373     prepareData : function(data, index, record)
24374     {
24375         this.fireEvent("preparedata", this, data, index, record);
24376         return data;
24377     },
24378
24379     onUpdate : function(ds, record){
24380         this.clearSelections();
24381         var index = this.store.indexOf(record);
24382         var n = this.nodes[index];
24383         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24384         n.parentNode.removeChild(n);
24385         this.updateIndexes(index, index);
24386     },
24387
24388     
24389     
24390 // --------- FIXME     
24391     onAdd : function(ds, records, index)
24392     {
24393         this.clearSelections();
24394         if(this.nodes.length == 0){
24395             this.refresh();
24396             return;
24397         }
24398         var n = this.nodes[index];
24399         for(var i = 0, len = records.length; i < len; i++){
24400             var d = this.prepareData(records[i].data, i, records[i]);
24401             if(n){
24402                 this.tpl.insertBefore(n, d);
24403             }else{
24404                 
24405                 this.tpl.append(this.el, d);
24406             }
24407         }
24408         this.updateIndexes(index);
24409     },
24410
24411     onRemove : function(ds, record, index){
24412         this.clearSelections();
24413         var el = this.dataName  ?
24414             this.el.child('.roo-tpl-' + this.dataName) :
24415             this.el; 
24416         el.dom.removeChild(this.nodes[index]);
24417         this.updateIndexes(index);
24418     },
24419
24420     /**
24421      * Refresh an individual node.
24422      * @param {Number} index
24423      */
24424     refreshNode : function(index){
24425         this.onUpdate(this.store, this.store.getAt(index));
24426     },
24427
24428     updateIndexes : function(startIndex, endIndex){
24429         var ns = this.nodes;
24430         startIndex = startIndex || 0;
24431         endIndex = endIndex || ns.length - 1;
24432         for(var i = startIndex; i <= endIndex; i++){
24433             ns[i].nodeIndex = i;
24434         }
24435     },
24436
24437     /**
24438      * Changes the data store this view uses and refresh the view.
24439      * @param {Store} store
24440      */
24441     setStore : function(store, initial){
24442         if(!initial && this.store){
24443             this.store.un("datachanged", this.refresh);
24444             this.store.un("add", this.onAdd);
24445             this.store.un("remove", this.onRemove);
24446             this.store.un("update", this.onUpdate);
24447             this.store.un("clear", this.refresh);
24448             this.store.un("beforeload", this.onBeforeLoad);
24449             this.store.un("load", this.onLoad);
24450             this.store.un("loadexception", this.onLoad);
24451         }
24452         if(store){
24453           
24454             store.on("datachanged", this.refresh, this);
24455             store.on("add", this.onAdd, this);
24456             store.on("remove", this.onRemove, this);
24457             store.on("update", this.onUpdate, this);
24458             store.on("clear", this.refresh, this);
24459             store.on("beforeload", this.onBeforeLoad, this);
24460             store.on("load", this.onLoad, this);
24461             store.on("loadexception", this.onLoad, this);
24462         }
24463         
24464         if(store){
24465             this.refresh();
24466         }
24467     },
24468     /**
24469      * onbeforeLoad - masks the loading area.
24470      *
24471      */
24472     onBeforeLoad : function()
24473     {
24474         this.el.update("");
24475         this.el.mask(this.mask ? this.mask : "Loading" ); 
24476     },
24477     onLoad : function ()
24478     {
24479         this.el.unmask();
24480     },
24481     
24482
24483     /**
24484      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
24485      * @param {HTMLElement} node
24486      * @return {HTMLElement} The template node
24487      */
24488     findItemFromChild : function(node){
24489         var el = this.dataName  ?
24490             this.el.child('.roo-tpl-' + this.dataName,true) :
24491             this.el.dom; 
24492         
24493         if(!node || node.parentNode == el){
24494                     return node;
24495             }
24496             var p = node.parentNode;
24497             while(p && p != el){
24498             if(p.parentNode == el){
24499                 return p;
24500             }
24501             p = p.parentNode;
24502         }
24503             return null;
24504     },
24505
24506     /** @ignore */
24507     onClick : function(e){
24508         var item = this.findItemFromChild(e.getTarget());
24509         if(item){
24510             var index = this.indexOf(item);
24511             if(this.onItemClick(item, index, e) !== false){
24512                 this.fireEvent("click", this, index, item, e);
24513             }
24514         }else{
24515             this.clearSelections();
24516         }
24517     },
24518
24519     /** @ignore */
24520     onContextMenu : function(e){
24521         var item = this.findItemFromChild(e.getTarget());
24522         if(item){
24523             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
24524         }
24525     },
24526
24527     /** @ignore */
24528     onDblClick : function(e){
24529         var item = this.findItemFromChild(e.getTarget());
24530         if(item){
24531             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
24532         }
24533     },
24534
24535     onItemClick : function(item, index, e)
24536     {
24537         if(this.fireEvent("beforeclick", this, index, item, e) === false){
24538             return false;
24539         }
24540         if (this.toggleSelect) {
24541             var m = this.isSelected(item) ? 'unselect' : 'select';
24542             Roo.log(m);
24543             var _t = this;
24544             _t[m](item, true, false);
24545             return true;
24546         }
24547         if(this.multiSelect || this.singleSelect){
24548             if(this.multiSelect && e.shiftKey && this.lastSelection){
24549                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
24550             }else{
24551                 this.select(item, this.multiSelect && e.ctrlKey);
24552                 this.lastSelection = item;
24553             }
24554             e.preventDefault();
24555         }
24556         return true;
24557     },
24558
24559     /**
24560      * Get the number of selected nodes.
24561      * @return {Number}
24562      */
24563     getSelectionCount : function(){
24564         return this.selections.length;
24565     },
24566
24567     /**
24568      * Get the currently selected nodes.
24569      * @return {Array} An array of HTMLElements
24570      */
24571     getSelectedNodes : function(){
24572         return this.selections;
24573     },
24574
24575     /**
24576      * Get the indexes of the selected nodes.
24577      * @return {Array}
24578      */
24579     getSelectedIndexes : function(){
24580         var indexes = [], s = this.selections;
24581         for(var i = 0, len = s.length; i < len; i++){
24582             indexes.push(s[i].nodeIndex);
24583         }
24584         return indexes;
24585     },
24586
24587     /**
24588      * Clear all selections
24589      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
24590      */
24591     clearSelections : function(suppressEvent){
24592         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24593             this.cmp.elements = this.selections;
24594             this.cmp.removeClass(this.selectedClass);
24595             this.selections = [];
24596             if(!suppressEvent){
24597                 this.fireEvent("selectionchange", this, this.selections);
24598             }
24599         }
24600     },
24601
24602     /**
24603      * Returns true if the passed node is selected
24604      * @param {HTMLElement/Number} node The node or node index
24605      * @return {Boolean}
24606      */
24607     isSelected : function(node){
24608         var s = this.selections;
24609         if(s.length < 1){
24610             return false;
24611         }
24612         node = this.getNode(node);
24613         return s.indexOf(node) !== -1;
24614     },
24615
24616     /**
24617      * Selects nodes.
24618      * @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
24619      * @param {Boolean} keepExisting (optional) true to keep existing selections
24620      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24621      */
24622     select : function(nodeInfo, keepExisting, suppressEvent){
24623         if(nodeInfo instanceof Array){
24624             if(!keepExisting){
24625                 this.clearSelections(true);
24626             }
24627             for(var i = 0, len = nodeInfo.length; i < len; i++){
24628                 this.select(nodeInfo[i], true, true);
24629             }
24630             return;
24631         } 
24632         var node = this.getNode(nodeInfo);
24633         if(!node || this.isSelected(node)){
24634             return; // already selected.
24635         }
24636         if(!keepExisting){
24637             this.clearSelections(true);
24638         }
24639         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24640             Roo.fly(node).addClass(this.selectedClass);
24641             this.selections.push(node);
24642             if(!suppressEvent){
24643                 this.fireEvent("selectionchange", this, this.selections);
24644             }
24645         }
24646         
24647         
24648     },
24649       /**
24650      * Unselects nodes.
24651      * @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
24652      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24653      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24654      */
24655     unselect : function(nodeInfo, keepExisting, suppressEvent)
24656     {
24657         if(nodeInfo instanceof Array){
24658             Roo.each(this.selections, function(s) {
24659                 this.unselect(s, nodeInfo);
24660             }, this);
24661             return;
24662         }
24663         var node = this.getNode(nodeInfo);
24664         if(!node || !this.isSelected(node)){
24665             Roo.log("not selected");
24666             return; // not selected.
24667         }
24668         // fireevent???
24669         var ns = [];
24670         Roo.each(this.selections, function(s) {
24671             if (s == node ) {
24672                 Roo.fly(node).removeClass(this.selectedClass);
24673
24674                 return;
24675             }
24676             ns.push(s);
24677         },this);
24678         
24679         this.selections= ns;
24680         this.fireEvent("selectionchange", this, this.selections);
24681     },
24682
24683     /**
24684      * Gets a template node.
24685      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24686      * @return {HTMLElement} The node or null if it wasn't found
24687      */
24688     getNode : function(nodeInfo){
24689         if(typeof nodeInfo == "string"){
24690             return document.getElementById(nodeInfo);
24691         }else if(typeof nodeInfo == "number"){
24692             return this.nodes[nodeInfo];
24693         }
24694         return nodeInfo;
24695     },
24696
24697     /**
24698      * Gets a range template nodes.
24699      * @param {Number} startIndex
24700      * @param {Number} endIndex
24701      * @return {Array} An array of nodes
24702      */
24703     getNodes : function(start, end){
24704         var ns = this.nodes;
24705         start = start || 0;
24706         end = typeof end == "undefined" ? ns.length - 1 : end;
24707         var nodes = [];
24708         if(start <= end){
24709             for(var i = start; i <= end; i++){
24710                 nodes.push(ns[i]);
24711             }
24712         } else{
24713             for(var i = start; i >= end; i--){
24714                 nodes.push(ns[i]);
24715             }
24716         }
24717         return nodes;
24718     },
24719
24720     /**
24721      * Finds the index of the passed node
24722      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24723      * @return {Number} The index of the node or -1
24724      */
24725     indexOf : function(node){
24726         node = this.getNode(node);
24727         if(typeof node.nodeIndex == "number"){
24728             return node.nodeIndex;
24729         }
24730         var ns = this.nodes;
24731         for(var i = 0, len = ns.length; i < len; i++){
24732             if(ns[i] == node){
24733                 return i;
24734             }
24735         }
24736         return -1;
24737     }
24738 });
24739 /*
24740  * Based on:
24741  * Ext JS Library 1.1.1
24742  * Copyright(c) 2006-2007, Ext JS, LLC.
24743  *
24744  * Originally Released Under LGPL - original licence link has changed is not relivant.
24745  *
24746  * Fork - LGPL
24747  * <script type="text/javascript">
24748  */
24749
24750 /**
24751  * @class Roo.JsonView
24752  * @extends Roo.View
24753  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24754 <pre><code>
24755 var view = new Roo.JsonView({
24756     container: "my-element",
24757     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24758     multiSelect: true, 
24759     jsonRoot: "data" 
24760 });
24761
24762 // listen for node click?
24763 view.on("click", function(vw, index, node, e){
24764     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24765 });
24766
24767 // direct load of JSON data
24768 view.load("foobar.php");
24769
24770 // Example from my blog list
24771 var tpl = new Roo.Template(
24772     '&lt;div class="entry"&gt;' +
24773     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24774     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24775     "&lt;/div&gt;&lt;hr /&gt;"
24776 );
24777
24778 var moreView = new Roo.JsonView({
24779     container :  "entry-list", 
24780     template : tpl,
24781     jsonRoot: "posts"
24782 });
24783 moreView.on("beforerender", this.sortEntries, this);
24784 moreView.load({
24785     url: "/blog/get-posts.php",
24786     params: "allposts=true",
24787     text: "Loading Blog Entries..."
24788 });
24789 </code></pre>
24790
24791 * Note: old code is supported with arguments : (container, template, config)
24792
24793
24794  * @constructor
24795  * Create a new JsonView
24796  * 
24797  * @param {Object} config The config object
24798  * 
24799  */
24800 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24801     
24802     
24803     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24804
24805     var um = this.el.getUpdateManager();
24806     um.setRenderer(this);
24807     um.on("update", this.onLoad, this);
24808     um.on("failure", this.onLoadException, this);
24809
24810     /**
24811      * @event beforerender
24812      * Fires before rendering of the downloaded JSON data.
24813      * @param {Roo.JsonView} this
24814      * @param {Object} data The JSON data loaded
24815      */
24816     /**
24817      * @event load
24818      * Fires when data is loaded.
24819      * @param {Roo.JsonView} this
24820      * @param {Object} data The JSON data loaded
24821      * @param {Object} response The raw Connect response object
24822      */
24823     /**
24824      * @event loadexception
24825      * Fires when loading fails.
24826      * @param {Roo.JsonView} this
24827      * @param {Object} response The raw Connect response object
24828      */
24829     this.addEvents({
24830         'beforerender' : true,
24831         'load' : true,
24832         'loadexception' : true
24833     });
24834 };
24835 Roo.extend(Roo.JsonView, Roo.View, {
24836     /**
24837      * @type {String} The root property in the loaded JSON object that contains the data
24838      */
24839     jsonRoot : "",
24840
24841     /**
24842      * Refreshes the view.
24843      */
24844     refresh : function(){
24845         this.clearSelections();
24846         this.el.update("");
24847         var html = [];
24848         var o = this.jsonData;
24849         if(o && o.length > 0){
24850             for(var i = 0, len = o.length; i < len; i++){
24851                 var data = this.prepareData(o[i], i, o);
24852                 html[html.length] = this.tpl.apply(data);
24853             }
24854         }else{
24855             html.push(this.emptyText);
24856         }
24857         this.el.update(html.join(""));
24858         this.nodes = this.el.dom.childNodes;
24859         this.updateIndexes(0);
24860     },
24861
24862     /**
24863      * 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.
24864      * @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:
24865      <pre><code>
24866      view.load({
24867          url: "your-url.php",
24868          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24869          callback: yourFunction,
24870          scope: yourObject, //(optional scope)
24871          discardUrl: false,
24872          nocache: false,
24873          text: "Loading...",
24874          timeout: 30,
24875          scripts: false
24876      });
24877      </code></pre>
24878      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24879      * 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.
24880      * @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}
24881      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24882      * @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.
24883      */
24884     load : function(){
24885         var um = this.el.getUpdateManager();
24886         um.update.apply(um, arguments);
24887     },
24888
24889     render : function(el, response){
24890         this.clearSelections();
24891         this.el.update("");
24892         var o;
24893         try{
24894             o = Roo.util.JSON.decode(response.responseText);
24895             if(this.jsonRoot){
24896                 
24897                 o = o[this.jsonRoot];
24898             }
24899         } catch(e){
24900         }
24901         /**
24902          * The current JSON data or null
24903          */
24904         this.jsonData = o;
24905         this.beforeRender();
24906         this.refresh();
24907     },
24908
24909 /**
24910  * Get the number of records in the current JSON dataset
24911  * @return {Number}
24912  */
24913     getCount : function(){
24914         return this.jsonData ? this.jsonData.length : 0;
24915     },
24916
24917 /**
24918  * Returns the JSON object for the specified node(s)
24919  * @param {HTMLElement/Array} node The node or an array of nodes
24920  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24921  * you get the JSON object for the node
24922  */
24923     getNodeData : function(node){
24924         if(node instanceof Array){
24925             var data = [];
24926             for(var i = 0, len = node.length; i < len; i++){
24927                 data.push(this.getNodeData(node[i]));
24928             }
24929             return data;
24930         }
24931         return this.jsonData[this.indexOf(node)] || null;
24932     },
24933
24934     beforeRender : function(){
24935         this.snapshot = this.jsonData;
24936         if(this.sortInfo){
24937             this.sort.apply(this, this.sortInfo);
24938         }
24939         this.fireEvent("beforerender", this, this.jsonData);
24940     },
24941
24942     onLoad : function(el, o){
24943         this.fireEvent("load", this, this.jsonData, o);
24944     },
24945
24946     onLoadException : function(el, o){
24947         this.fireEvent("loadexception", this, o);
24948     },
24949
24950 /**
24951  * Filter the data by a specific property.
24952  * @param {String} property A property on your JSON objects
24953  * @param {String/RegExp} value Either string that the property values
24954  * should start with, or a RegExp to test against the property
24955  */
24956     filter : function(property, value){
24957         if(this.jsonData){
24958             var data = [];
24959             var ss = this.snapshot;
24960             if(typeof value == "string"){
24961                 var vlen = value.length;
24962                 if(vlen == 0){
24963                     this.clearFilter();
24964                     return;
24965                 }
24966                 value = value.toLowerCase();
24967                 for(var i = 0, len = ss.length; i < len; i++){
24968                     var o = ss[i];
24969                     if(o[property].substr(0, vlen).toLowerCase() == value){
24970                         data.push(o);
24971                     }
24972                 }
24973             } else if(value.exec){ // regex?
24974                 for(var i = 0, len = ss.length; i < len; i++){
24975                     var o = ss[i];
24976                     if(value.test(o[property])){
24977                         data.push(o);
24978                     }
24979                 }
24980             } else{
24981                 return;
24982             }
24983             this.jsonData = data;
24984             this.refresh();
24985         }
24986     },
24987
24988 /**
24989  * Filter by a function. The passed function will be called with each
24990  * object in the current dataset. If the function returns true the value is kept,
24991  * otherwise it is filtered.
24992  * @param {Function} fn
24993  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24994  */
24995     filterBy : function(fn, scope){
24996         if(this.jsonData){
24997             var data = [];
24998             var ss = this.snapshot;
24999             for(var i = 0, len = ss.length; i < len; i++){
25000                 var o = ss[i];
25001                 if(fn.call(scope || this, o)){
25002                     data.push(o);
25003                 }
25004             }
25005             this.jsonData = data;
25006             this.refresh();
25007         }
25008     },
25009
25010 /**
25011  * Clears the current filter.
25012  */
25013     clearFilter : function(){
25014         if(this.snapshot && this.jsonData != this.snapshot){
25015             this.jsonData = this.snapshot;
25016             this.refresh();
25017         }
25018     },
25019
25020
25021 /**
25022  * Sorts the data for this view and refreshes it.
25023  * @param {String} property A property on your JSON objects to sort on
25024  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25025  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25026  */
25027     sort : function(property, dir, sortType){
25028         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25029         if(this.jsonData){
25030             var p = property;
25031             var dsc = dir && dir.toLowerCase() == "desc";
25032             var f = function(o1, o2){
25033                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25034                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25035                 ;
25036                 if(v1 < v2){
25037                     return dsc ? +1 : -1;
25038                 } else if(v1 > v2){
25039                     return dsc ? -1 : +1;
25040                 } else{
25041                     return 0;
25042                 }
25043             };
25044             this.jsonData.sort(f);
25045             this.refresh();
25046             if(this.jsonData != this.snapshot){
25047                 this.snapshot.sort(f);
25048             }
25049         }
25050     }
25051 });/*
25052  * Based on:
25053  * Ext JS Library 1.1.1
25054  * Copyright(c) 2006-2007, Ext JS, LLC.
25055  *
25056  * Originally Released Under LGPL - original licence link has changed is not relivant.
25057  *
25058  * Fork - LGPL
25059  * <script type="text/javascript">
25060  */
25061  
25062
25063 /**
25064  * @class Roo.ColorPalette
25065  * @extends Roo.Component
25066  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25067  * Here's an example of typical usage:
25068  * <pre><code>
25069 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25070 cp.render('my-div');
25071
25072 cp.on('select', function(palette, selColor){
25073     // do something with selColor
25074 });
25075 </code></pre>
25076  * @constructor
25077  * Create a new ColorPalette
25078  * @param {Object} config The config object
25079  */
25080 Roo.ColorPalette = function(config){
25081     Roo.ColorPalette.superclass.constructor.call(this, config);
25082     this.addEvents({
25083         /**
25084              * @event select
25085              * Fires when a color is selected
25086              * @param {ColorPalette} this
25087              * @param {String} color The 6-digit color hex code (without the # symbol)
25088              */
25089         select: true
25090     });
25091
25092     if(this.handler){
25093         this.on("select", this.handler, this.scope, true);
25094     }
25095 };
25096 Roo.extend(Roo.ColorPalette, Roo.Component, {
25097     /**
25098      * @cfg {String} itemCls
25099      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25100      */
25101     itemCls : "x-color-palette",
25102     /**
25103      * @cfg {String} value
25104      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25105      * the hex codes are case-sensitive.
25106      */
25107     value : null,
25108     clickEvent:'click',
25109     // private
25110     ctype: "Roo.ColorPalette",
25111
25112     /**
25113      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25114      */
25115     allowReselect : false,
25116
25117     /**
25118      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25119      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25120      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25121      * of colors with the width setting until the box is symmetrical.</p>
25122      * <p>You can override individual colors if needed:</p>
25123      * <pre><code>
25124 var cp = new Roo.ColorPalette();
25125 cp.colors[0] = "FF0000";  // change the first box to red
25126 </code></pre>
25127
25128 Or you can provide a custom array of your own for complete control:
25129 <pre><code>
25130 var cp = new Roo.ColorPalette();
25131 cp.colors = ["000000", "993300", "333300"];
25132 </code></pre>
25133      * @type Array
25134      */
25135     colors : [
25136         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25137         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25138         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25139         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25140         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25141     ],
25142
25143     // private
25144     onRender : function(container, position){
25145         var t = new Roo.MasterTemplate(
25146             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25147         );
25148         var c = this.colors;
25149         for(var i = 0, len = c.length; i < len; i++){
25150             t.add([c[i]]);
25151         }
25152         var el = document.createElement("div");
25153         el.className = this.itemCls;
25154         t.overwrite(el);
25155         container.dom.insertBefore(el, position);
25156         this.el = Roo.get(el);
25157         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25158         if(this.clickEvent != 'click'){
25159             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25160         }
25161     },
25162
25163     // private
25164     afterRender : function(){
25165         Roo.ColorPalette.superclass.afterRender.call(this);
25166         if(this.value){
25167             var s = this.value;
25168             this.value = null;
25169             this.select(s);
25170         }
25171     },
25172
25173     // private
25174     handleClick : function(e, t){
25175         e.preventDefault();
25176         if(!this.disabled){
25177             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25178             this.select(c.toUpperCase());
25179         }
25180     },
25181
25182     /**
25183      * Selects the specified color in the palette (fires the select event)
25184      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25185      */
25186     select : function(color){
25187         color = color.replace("#", "");
25188         if(color != this.value || this.allowReselect){
25189             var el = this.el;
25190             if(this.value){
25191                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25192             }
25193             el.child("a.color-"+color).addClass("x-color-palette-sel");
25194             this.value = color;
25195             this.fireEvent("select", this, color);
25196         }
25197     }
25198 });/*
25199  * Based on:
25200  * Ext JS Library 1.1.1
25201  * Copyright(c) 2006-2007, Ext JS, LLC.
25202  *
25203  * Originally Released Under LGPL - original licence link has changed is not relivant.
25204  *
25205  * Fork - LGPL
25206  * <script type="text/javascript">
25207  */
25208  
25209 /**
25210  * @class Roo.DatePicker
25211  * @extends Roo.Component
25212  * Simple date picker class.
25213  * @constructor
25214  * Create a new DatePicker
25215  * @param {Object} config The config object
25216  */
25217 Roo.DatePicker = function(config){
25218     Roo.DatePicker.superclass.constructor.call(this, config);
25219
25220     this.value = config && config.value ?
25221                  config.value.clearTime() : new Date().clearTime();
25222
25223     this.addEvents({
25224         /**
25225              * @event select
25226              * Fires when a date is selected
25227              * @param {DatePicker} this
25228              * @param {Date} date The selected date
25229              */
25230         'select': true,
25231         /**
25232              * @event monthchange
25233              * Fires when the displayed month changes 
25234              * @param {DatePicker} this
25235              * @param {Date} date The selected month
25236              */
25237         'monthchange': true
25238     });
25239
25240     if(this.handler){
25241         this.on("select", this.handler,  this.scope || this);
25242     }
25243     // build the disabledDatesRE
25244     if(!this.disabledDatesRE && this.disabledDates){
25245         var dd = this.disabledDates;
25246         var re = "(?:";
25247         for(var i = 0; i < dd.length; i++){
25248             re += dd[i];
25249             if(i != dd.length-1) re += "|";
25250         }
25251         this.disabledDatesRE = new RegExp(re + ")");
25252     }
25253 };
25254
25255 Roo.extend(Roo.DatePicker, Roo.Component, {
25256     /**
25257      * @cfg {String} todayText
25258      * The text to display on the button that selects the current date (defaults to "Today")
25259      */
25260     todayText : "Today",
25261     /**
25262      * @cfg {String} okText
25263      * The text to display on the ok button
25264      */
25265     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25266     /**
25267      * @cfg {String} cancelText
25268      * The text to display on the cancel button
25269      */
25270     cancelText : "Cancel",
25271     /**
25272      * @cfg {String} todayTip
25273      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25274      */
25275     todayTip : "{0} (Spacebar)",
25276     /**
25277      * @cfg {Date} minDate
25278      * Minimum allowable date (JavaScript date object, defaults to null)
25279      */
25280     minDate : null,
25281     /**
25282      * @cfg {Date} maxDate
25283      * Maximum allowable date (JavaScript date object, defaults to null)
25284      */
25285     maxDate : null,
25286     /**
25287      * @cfg {String} minText
25288      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25289      */
25290     minText : "This date is before the minimum date",
25291     /**
25292      * @cfg {String} maxText
25293      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25294      */
25295     maxText : "This date is after the maximum date",
25296     /**
25297      * @cfg {String} format
25298      * The default date format string which can be overriden for localization support.  The format must be
25299      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25300      */
25301     format : "m/d/y",
25302     /**
25303      * @cfg {Array} disabledDays
25304      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25305      */
25306     disabledDays : null,
25307     /**
25308      * @cfg {String} disabledDaysText
25309      * The tooltip to display when the date falls on a disabled day (defaults to "")
25310      */
25311     disabledDaysText : "",
25312     /**
25313      * @cfg {RegExp} disabledDatesRE
25314      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25315      */
25316     disabledDatesRE : null,
25317     /**
25318      * @cfg {String} disabledDatesText
25319      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25320      */
25321     disabledDatesText : "",
25322     /**
25323      * @cfg {Boolean} constrainToViewport
25324      * True to constrain the date picker to the viewport (defaults to true)
25325      */
25326     constrainToViewport : true,
25327     /**
25328      * @cfg {Array} monthNames
25329      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25330      */
25331     monthNames : Date.monthNames,
25332     /**
25333      * @cfg {Array} dayNames
25334      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25335      */
25336     dayNames : Date.dayNames,
25337     /**
25338      * @cfg {String} nextText
25339      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25340      */
25341     nextText: 'Next Month (Control+Right)',
25342     /**
25343      * @cfg {String} prevText
25344      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25345      */
25346     prevText: 'Previous Month (Control+Left)',
25347     /**
25348      * @cfg {String} monthYearText
25349      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25350      */
25351     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25352     /**
25353      * @cfg {Number} startDay
25354      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25355      */
25356     startDay : 0,
25357     /**
25358      * @cfg {Bool} showClear
25359      * Show a clear button (usefull for date form elements that can be blank.)
25360      */
25361     
25362     showClear: false,
25363     
25364     /**
25365      * Sets the value of the date field
25366      * @param {Date} value The date to set
25367      */
25368     setValue : function(value){
25369         var old = this.value;
25370         
25371         if (typeof(value) == 'string') {
25372          
25373             value = Date.parseDate(value, this.format);
25374         }
25375         if (!value) {
25376             value = new Date();
25377         }
25378         
25379         this.value = value.clearTime(true);
25380         if(this.el){
25381             this.update(this.value);
25382         }
25383     },
25384
25385     /**
25386      * Gets the current selected value of the date field
25387      * @return {Date} The selected date
25388      */
25389     getValue : function(){
25390         return this.value;
25391     },
25392
25393     // private
25394     focus : function(){
25395         if(this.el){
25396             this.update(this.activeDate);
25397         }
25398     },
25399
25400     // privateval
25401     onRender : function(container, position){
25402         
25403         var m = [
25404              '<table cellspacing="0">',
25405                 '<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>',
25406                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
25407         var dn = this.dayNames;
25408         for(var i = 0; i < 7; i++){
25409             var d = this.startDay+i;
25410             if(d > 6){
25411                 d = d-7;
25412             }
25413             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
25414         }
25415         m[m.length] = "</tr></thead><tbody><tr>";
25416         for(var i = 0; i < 42; i++) {
25417             if(i % 7 == 0 && i != 0){
25418                 m[m.length] = "</tr><tr>";
25419             }
25420             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
25421         }
25422         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
25423             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
25424
25425         var el = document.createElement("div");
25426         el.className = "x-date-picker";
25427         el.innerHTML = m.join("");
25428
25429         container.dom.insertBefore(el, position);
25430
25431         this.el = Roo.get(el);
25432         this.eventEl = Roo.get(el.firstChild);
25433
25434         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
25435             handler: this.showPrevMonth,
25436             scope: this,
25437             preventDefault:true,
25438             stopDefault:true
25439         });
25440
25441         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
25442             handler: this.showNextMonth,
25443             scope: this,
25444             preventDefault:true,
25445             stopDefault:true
25446         });
25447
25448         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
25449
25450         this.monthPicker = this.el.down('div.x-date-mp');
25451         this.monthPicker.enableDisplayMode('block');
25452         
25453         var kn = new Roo.KeyNav(this.eventEl, {
25454             "left" : function(e){
25455                 e.ctrlKey ?
25456                     this.showPrevMonth() :
25457                     this.update(this.activeDate.add("d", -1));
25458             },
25459
25460             "right" : function(e){
25461                 e.ctrlKey ?
25462                     this.showNextMonth() :
25463                     this.update(this.activeDate.add("d", 1));
25464             },
25465
25466             "up" : function(e){
25467                 e.ctrlKey ?
25468                     this.showNextYear() :
25469                     this.update(this.activeDate.add("d", -7));
25470             },
25471
25472             "down" : function(e){
25473                 e.ctrlKey ?
25474                     this.showPrevYear() :
25475                     this.update(this.activeDate.add("d", 7));
25476             },
25477
25478             "pageUp" : function(e){
25479                 this.showNextMonth();
25480             },
25481
25482             "pageDown" : function(e){
25483                 this.showPrevMonth();
25484             },
25485
25486             "enter" : function(e){
25487                 e.stopPropagation();
25488                 return true;
25489             },
25490
25491             scope : this
25492         });
25493
25494         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
25495
25496         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
25497
25498         this.el.unselectable();
25499         
25500         this.cells = this.el.select("table.x-date-inner tbody td");
25501         this.textNodes = this.el.query("table.x-date-inner tbody span");
25502
25503         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
25504             text: "&#160;",
25505             tooltip: this.monthYearText
25506         });
25507
25508         this.mbtn.on('click', this.showMonthPicker, this);
25509         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
25510
25511
25512         var today = (new Date()).dateFormat(this.format);
25513         
25514         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
25515         if (this.showClear) {
25516             baseTb.add( new Roo.Toolbar.Fill());
25517         }
25518         baseTb.add({
25519             text: String.format(this.todayText, today),
25520             tooltip: String.format(this.todayTip, today),
25521             handler: this.selectToday,
25522             scope: this
25523         });
25524         
25525         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
25526             
25527         //});
25528         if (this.showClear) {
25529             
25530             baseTb.add( new Roo.Toolbar.Fill());
25531             baseTb.add({
25532                 text: '&#160;',
25533                 cls: 'x-btn-icon x-btn-clear',
25534                 handler: function() {
25535                     //this.value = '';
25536                     this.fireEvent("select", this, '');
25537                 },
25538                 scope: this
25539             });
25540         }
25541         
25542         
25543         if(Roo.isIE){
25544             this.el.repaint();
25545         }
25546         this.update(this.value);
25547     },
25548
25549     createMonthPicker : function(){
25550         if(!this.monthPicker.dom.firstChild){
25551             var buf = ['<table border="0" cellspacing="0">'];
25552             for(var i = 0; i < 6; i++){
25553                 buf.push(
25554                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
25555                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
25556                     i == 0 ?
25557                     '<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>' :
25558                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
25559                 );
25560             }
25561             buf.push(
25562                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
25563                     this.okText,
25564                     '</button><button type="button" class="x-date-mp-cancel">',
25565                     this.cancelText,
25566                     '</button></td></tr>',
25567                 '</table>'
25568             );
25569             this.monthPicker.update(buf.join(''));
25570             this.monthPicker.on('click', this.onMonthClick, this);
25571             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
25572
25573             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
25574             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
25575
25576             this.mpMonths.each(function(m, a, i){
25577                 i += 1;
25578                 if((i%2) == 0){
25579                     m.dom.xmonth = 5 + Math.round(i * .5);
25580                 }else{
25581                     m.dom.xmonth = Math.round((i-1) * .5);
25582                 }
25583             });
25584         }
25585     },
25586
25587     showMonthPicker : function(){
25588         this.createMonthPicker();
25589         var size = this.el.getSize();
25590         this.monthPicker.setSize(size);
25591         this.monthPicker.child('table').setSize(size);
25592
25593         this.mpSelMonth = (this.activeDate || this.value).getMonth();
25594         this.updateMPMonth(this.mpSelMonth);
25595         this.mpSelYear = (this.activeDate || this.value).getFullYear();
25596         this.updateMPYear(this.mpSelYear);
25597
25598         this.monthPicker.slideIn('t', {duration:.2});
25599     },
25600
25601     updateMPYear : function(y){
25602         this.mpyear = y;
25603         var ys = this.mpYears.elements;
25604         for(var i = 1; i <= 10; i++){
25605             var td = ys[i-1], y2;
25606             if((i%2) == 0){
25607                 y2 = y + Math.round(i * .5);
25608                 td.firstChild.innerHTML = y2;
25609                 td.xyear = y2;
25610             }else{
25611                 y2 = y - (5-Math.round(i * .5));
25612                 td.firstChild.innerHTML = y2;
25613                 td.xyear = y2;
25614             }
25615             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25616         }
25617     },
25618
25619     updateMPMonth : function(sm){
25620         this.mpMonths.each(function(m, a, i){
25621             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25622         });
25623     },
25624
25625     selectMPMonth: function(m){
25626         
25627     },
25628
25629     onMonthClick : function(e, t){
25630         e.stopEvent();
25631         var el = new Roo.Element(t), pn;
25632         if(el.is('button.x-date-mp-cancel')){
25633             this.hideMonthPicker();
25634         }
25635         else if(el.is('button.x-date-mp-ok')){
25636             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25637             this.hideMonthPicker();
25638         }
25639         else if(pn = el.up('td.x-date-mp-month', 2)){
25640             this.mpMonths.removeClass('x-date-mp-sel');
25641             pn.addClass('x-date-mp-sel');
25642             this.mpSelMonth = pn.dom.xmonth;
25643         }
25644         else if(pn = el.up('td.x-date-mp-year', 2)){
25645             this.mpYears.removeClass('x-date-mp-sel');
25646             pn.addClass('x-date-mp-sel');
25647             this.mpSelYear = pn.dom.xyear;
25648         }
25649         else if(el.is('a.x-date-mp-prev')){
25650             this.updateMPYear(this.mpyear-10);
25651         }
25652         else if(el.is('a.x-date-mp-next')){
25653             this.updateMPYear(this.mpyear+10);
25654         }
25655     },
25656
25657     onMonthDblClick : function(e, t){
25658         e.stopEvent();
25659         var el = new Roo.Element(t), pn;
25660         if(pn = el.up('td.x-date-mp-month', 2)){
25661             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25662             this.hideMonthPicker();
25663         }
25664         else if(pn = el.up('td.x-date-mp-year', 2)){
25665             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25666             this.hideMonthPicker();
25667         }
25668     },
25669
25670     hideMonthPicker : function(disableAnim){
25671         if(this.monthPicker){
25672             if(disableAnim === true){
25673                 this.monthPicker.hide();
25674             }else{
25675                 this.monthPicker.slideOut('t', {duration:.2});
25676             }
25677         }
25678     },
25679
25680     // private
25681     showPrevMonth : function(e){
25682         this.update(this.activeDate.add("mo", -1));
25683     },
25684
25685     // private
25686     showNextMonth : function(e){
25687         this.update(this.activeDate.add("mo", 1));
25688     },
25689
25690     // private
25691     showPrevYear : function(){
25692         this.update(this.activeDate.add("y", -1));
25693     },
25694
25695     // private
25696     showNextYear : function(){
25697         this.update(this.activeDate.add("y", 1));
25698     },
25699
25700     // private
25701     handleMouseWheel : function(e){
25702         var delta = e.getWheelDelta();
25703         if(delta > 0){
25704             this.showPrevMonth();
25705             e.stopEvent();
25706         } else if(delta < 0){
25707             this.showNextMonth();
25708             e.stopEvent();
25709         }
25710     },
25711
25712     // private
25713     handleDateClick : function(e, t){
25714         e.stopEvent();
25715         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25716             this.setValue(new Date(t.dateValue));
25717             this.fireEvent("select", this, this.value);
25718         }
25719     },
25720
25721     // private
25722     selectToday : function(){
25723         this.setValue(new Date().clearTime());
25724         this.fireEvent("select", this, this.value);
25725     },
25726
25727     // private
25728     update : function(date)
25729     {
25730         var vd = this.activeDate;
25731         this.activeDate = date;
25732         if(vd && this.el){
25733             var t = date.getTime();
25734             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25735                 this.cells.removeClass("x-date-selected");
25736                 this.cells.each(function(c){
25737                    if(c.dom.firstChild.dateValue == t){
25738                        c.addClass("x-date-selected");
25739                        setTimeout(function(){
25740                             try{c.dom.firstChild.focus();}catch(e){}
25741                        }, 50);
25742                        return false;
25743                    }
25744                 });
25745                 return;
25746             }
25747         }
25748         
25749         var days = date.getDaysInMonth();
25750         var firstOfMonth = date.getFirstDateOfMonth();
25751         var startingPos = firstOfMonth.getDay()-this.startDay;
25752
25753         if(startingPos <= this.startDay){
25754             startingPos += 7;
25755         }
25756
25757         var pm = date.add("mo", -1);
25758         var prevStart = pm.getDaysInMonth()-startingPos;
25759
25760         var cells = this.cells.elements;
25761         var textEls = this.textNodes;
25762         days += startingPos;
25763
25764         // convert everything to numbers so it's fast
25765         var day = 86400000;
25766         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25767         var today = new Date().clearTime().getTime();
25768         var sel = date.clearTime().getTime();
25769         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25770         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25771         var ddMatch = this.disabledDatesRE;
25772         var ddText = this.disabledDatesText;
25773         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25774         var ddaysText = this.disabledDaysText;
25775         var format = this.format;
25776
25777         var setCellClass = function(cal, cell){
25778             cell.title = "";
25779             var t = d.getTime();
25780             cell.firstChild.dateValue = t;
25781             if(t == today){
25782                 cell.className += " x-date-today";
25783                 cell.title = cal.todayText;
25784             }
25785             if(t == sel){
25786                 cell.className += " x-date-selected";
25787                 setTimeout(function(){
25788                     try{cell.firstChild.focus();}catch(e){}
25789                 }, 50);
25790             }
25791             // disabling
25792             if(t < min) {
25793                 cell.className = " x-date-disabled";
25794                 cell.title = cal.minText;
25795                 return;
25796             }
25797             if(t > max) {
25798                 cell.className = " x-date-disabled";
25799                 cell.title = cal.maxText;
25800                 return;
25801             }
25802             if(ddays){
25803                 if(ddays.indexOf(d.getDay()) != -1){
25804                     cell.title = ddaysText;
25805                     cell.className = " x-date-disabled";
25806                 }
25807             }
25808             if(ddMatch && format){
25809                 var fvalue = d.dateFormat(format);
25810                 if(ddMatch.test(fvalue)){
25811                     cell.title = ddText.replace("%0", fvalue);
25812                     cell.className = " x-date-disabled";
25813                 }
25814             }
25815         };
25816
25817         var i = 0;
25818         for(; i < startingPos; i++) {
25819             textEls[i].innerHTML = (++prevStart);
25820             d.setDate(d.getDate()+1);
25821             cells[i].className = "x-date-prevday";
25822             setCellClass(this, cells[i]);
25823         }
25824         for(; i < days; i++){
25825             intDay = i - startingPos + 1;
25826             textEls[i].innerHTML = (intDay);
25827             d.setDate(d.getDate()+1);
25828             cells[i].className = "x-date-active";
25829             setCellClass(this, cells[i]);
25830         }
25831         var extraDays = 0;
25832         for(; i < 42; i++) {
25833              textEls[i].innerHTML = (++extraDays);
25834              d.setDate(d.getDate()+1);
25835              cells[i].className = "x-date-nextday";
25836              setCellClass(this, cells[i]);
25837         }
25838
25839         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25840         this.fireEvent('monthchange', this, date);
25841         
25842         if(!this.internalRender){
25843             var main = this.el.dom.firstChild;
25844             var w = main.offsetWidth;
25845             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25846             Roo.fly(main).setWidth(w);
25847             this.internalRender = true;
25848             // opera does not respect the auto grow header center column
25849             // then, after it gets a width opera refuses to recalculate
25850             // without a second pass
25851             if(Roo.isOpera && !this.secondPass){
25852                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25853                 this.secondPass = true;
25854                 this.update.defer(10, this, [date]);
25855             }
25856         }
25857         
25858         
25859     }
25860 });        /*
25861  * Based on:
25862  * Ext JS Library 1.1.1
25863  * Copyright(c) 2006-2007, Ext JS, LLC.
25864  *
25865  * Originally Released Under LGPL - original licence link has changed is not relivant.
25866  *
25867  * Fork - LGPL
25868  * <script type="text/javascript">
25869  */
25870 /**
25871  * @class Roo.TabPanel
25872  * @extends Roo.util.Observable
25873  * A lightweight tab container.
25874  * <br><br>
25875  * Usage:
25876  * <pre><code>
25877 // basic tabs 1, built from existing content
25878 var tabs = new Roo.TabPanel("tabs1");
25879 tabs.addTab("script", "View Script");
25880 tabs.addTab("markup", "View Markup");
25881 tabs.activate("script");
25882
25883 // more advanced tabs, built from javascript
25884 var jtabs = new Roo.TabPanel("jtabs");
25885 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25886
25887 // set up the UpdateManager
25888 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25889 var updater = tab2.getUpdateManager();
25890 updater.setDefaultUrl("ajax1.htm");
25891 tab2.on('activate', updater.refresh, updater, true);
25892
25893 // Use setUrl for Ajax loading
25894 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25895 tab3.setUrl("ajax2.htm", null, true);
25896
25897 // Disabled tab
25898 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25899 tab4.disable();
25900
25901 jtabs.activate("jtabs-1");
25902  * </code></pre>
25903  * @constructor
25904  * Create a new TabPanel.
25905  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25906  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25907  */
25908 Roo.TabPanel = function(container, config){
25909     /**
25910     * The container element for this TabPanel.
25911     * @type Roo.Element
25912     */
25913     this.el = Roo.get(container, true);
25914     if(config){
25915         if(typeof config == "boolean"){
25916             this.tabPosition = config ? "bottom" : "top";
25917         }else{
25918             Roo.apply(this, config);
25919         }
25920     }
25921     if(this.tabPosition == "bottom"){
25922         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25923         this.el.addClass("x-tabs-bottom");
25924     }
25925     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25926     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25927     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25928     if(Roo.isIE){
25929         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25930     }
25931     if(this.tabPosition != "bottom"){
25932         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25933          * @type Roo.Element
25934          */
25935         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25936         this.el.addClass("x-tabs-top");
25937     }
25938     this.items = [];
25939
25940     this.bodyEl.setStyle("position", "relative");
25941
25942     this.active = null;
25943     this.activateDelegate = this.activate.createDelegate(this);
25944
25945     this.addEvents({
25946         /**
25947          * @event tabchange
25948          * Fires when the active tab changes
25949          * @param {Roo.TabPanel} this
25950          * @param {Roo.TabPanelItem} activePanel The new active tab
25951          */
25952         "tabchange": true,
25953         /**
25954          * @event beforetabchange
25955          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25956          * @param {Roo.TabPanel} this
25957          * @param {Object} e Set cancel to true on this object to cancel the tab change
25958          * @param {Roo.TabPanelItem} tab The tab being changed to
25959          */
25960         "beforetabchange" : true
25961     });
25962
25963     Roo.EventManager.onWindowResize(this.onResize, this);
25964     this.cpad = this.el.getPadding("lr");
25965     this.hiddenCount = 0;
25966
25967
25968     // toolbar on the tabbar support...
25969     if (this.toolbar) {
25970         var tcfg = this.toolbar;
25971         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25972         this.toolbar = new Roo.Toolbar(tcfg);
25973         if (Roo.isSafari) {
25974             var tbl = tcfg.container.child('table', true);
25975             tbl.setAttribute('width', '100%');
25976         }
25977         
25978     }
25979    
25980
25981
25982     Roo.TabPanel.superclass.constructor.call(this);
25983 };
25984
25985 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25986     /*
25987      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25988      */
25989     tabPosition : "top",
25990     /*
25991      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25992      */
25993     currentTabWidth : 0,
25994     /*
25995      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25996      */
25997     minTabWidth : 40,
25998     /*
25999      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26000      */
26001     maxTabWidth : 250,
26002     /*
26003      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26004      */
26005     preferredTabWidth : 175,
26006     /*
26007      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26008      */
26009     resizeTabs : false,
26010     /*
26011      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26012      */
26013     monitorResize : true,
26014     /*
26015      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26016      */
26017     toolbar : false,
26018
26019     /**
26020      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26021      * @param {String} id The id of the div to use <b>or create</b>
26022      * @param {String} text The text for the tab
26023      * @param {String} content (optional) Content to put in the TabPanelItem body
26024      * @param {Boolean} closable (optional) True to create a close icon on the tab
26025      * @return {Roo.TabPanelItem} The created TabPanelItem
26026      */
26027     addTab : function(id, text, content, closable){
26028         var item = new Roo.TabPanelItem(this, id, text, closable);
26029         this.addTabItem(item);
26030         if(content){
26031             item.setContent(content);
26032         }
26033         return item;
26034     },
26035
26036     /**
26037      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26038      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26039      * @return {Roo.TabPanelItem}
26040      */
26041     getTab : function(id){
26042         return this.items[id];
26043     },
26044
26045     /**
26046      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26047      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26048      */
26049     hideTab : function(id){
26050         var t = this.items[id];
26051         if(!t.isHidden()){
26052            t.setHidden(true);
26053            this.hiddenCount++;
26054            this.autoSizeTabs();
26055         }
26056     },
26057
26058     /**
26059      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26060      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26061      */
26062     unhideTab : function(id){
26063         var t = this.items[id];
26064         if(t.isHidden()){
26065            t.setHidden(false);
26066            this.hiddenCount--;
26067            this.autoSizeTabs();
26068         }
26069     },
26070
26071     /**
26072      * Adds an existing {@link Roo.TabPanelItem}.
26073      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26074      */
26075     addTabItem : function(item){
26076         this.items[item.id] = item;
26077         this.items.push(item);
26078         if(this.resizeTabs){
26079            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26080            this.autoSizeTabs();
26081         }else{
26082             item.autoSize();
26083         }
26084     },
26085
26086     /**
26087      * Removes a {@link Roo.TabPanelItem}.
26088      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26089      */
26090     removeTab : function(id){
26091         var items = this.items;
26092         var tab = items[id];
26093         if(!tab) { return; }
26094         var index = items.indexOf(tab);
26095         if(this.active == tab && items.length > 1){
26096             var newTab = this.getNextAvailable(index);
26097             if(newTab) {
26098                 newTab.activate();
26099             }
26100         }
26101         this.stripEl.dom.removeChild(tab.pnode.dom);
26102         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26103             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26104         }
26105         items.splice(index, 1);
26106         delete this.items[tab.id];
26107         tab.fireEvent("close", tab);
26108         tab.purgeListeners();
26109         this.autoSizeTabs();
26110     },
26111
26112     getNextAvailable : function(start){
26113         var items = this.items;
26114         var index = start;
26115         // look for a next tab that will slide over to
26116         // replace the one being removed
26117         while(index < items.length){
26118             var item = items[++index];
26119             if(item && !item.isHidden()){
26120                 return item;
26121             }
26122         }
26123         // if one isn't found select the previous tab (on the left)
26124         index = start;
26125         while(index >= 0){
26126             var item = items[--index];
26127             if(item && !item.isHidden()){
26128                 return item;
26129             }
26130         }
26131         return null;
26132     },
26133
26134     /**
26135      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26136      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26137      */
26138     disableTab : function(id){
26139         var tab = this.items[id];
26140         if(tab && this.active != tab){
26141             tab.disable();
26142         }
26143     },
26144
26145     /**
26146      * Enables a {@link Roo.TabPanelItem} that is disabled.
26147      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26148      */
26149     enableTab : function(id){
26150         var tab = this.items[id];
26151         tab.enable();
26152     },
26153
26154     /**
26155      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26156      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26157      * @return {Roo.TabPanelItem} The TabPanelItem.
26158      */
26159     activate : function(id){
26160         var tab = this.items[id];
26161         if(!tab){
26162             return null;
26163         }
26164         if(tab == this.active || tab.disabled){
26165             return tab;
26166         }
26167         var e = {};
26168         this.fireEvent("beforetabchange", this, e, tab);
26169         if(e.cancel !== true && !tab.disabled){
26170             if(this.active){
26171                 this.active.hide();
26172             }
26173             this.active = this.items[id];
26174             this.active.show();
26175             this.fireEvent("tabchange", this, this.active);
26176         }
26177         return tab;
26178     },
26179
26180     /**
26181      * Gets the active {@link Roo.TabPanelItem}.
26182      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26183      */
26184     getActiveTab : function(){
26185         return this.active;
26186     },
26187
26188     /**
26189      * Updates the tab body element to fit the height of the container element
26190      * for overflow scrolling
26191      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26192      */
26193     syncHeight : function(targetHeight){
26194         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26195         var bm = this.bodyEl.getMargins();
26196         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26197         this.bodyEl.setHeight(newHeight);
26198         return newHeight;
26199     },
26200
26201     onResize : function(){
26202         if(this.monitorResize){
26203             this.autoSizeTabs();
26204         }
26205     },
26206
26207     /**
26208      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26209      */
26210     beginUpdate : function(){
26211         this.updating = true;
26212     },
26213
26214     /**
26215      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26216      */
26217     endUpdate : function(){
26218         this.updating = false;
26219         this.autoSizeTabs();
26220     },
26221
26222     /**
26223      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26224      */
26225     autoSizeTabs : function(){
26226         var count = this.items.length;
26227         var vcount = count - this.hiddenCount;
26228         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26229         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26230         var availWidth = Math.floor(w / vcount);
26231         var b = this.stripBody;
26232         if(b.getWidth() > w){
26233             var tabs = this.items;
26234             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26235             if(availWidth < this.minTabWidth){
26236                 /*if(!this.sleft){    // incomplete scrolling code
26237                     this.createScrollButtons();
26238                 }
26239                 this.showScroll();
26240                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26241             }
26242         }else{
26243             if(this.currentTabWidth < this.preferredTabWidth){
26244                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26245             }
26246         }
26247     },
26248
26249     /**
26250      * Returns the number of tabs in this TabPanel.
26251      * @return {Number}
26252      */
26253      getCount : function(){
26254          return this.items.length;
26255      },
26256
26257     /**
26258      * Resizes all the tabs to the passed width
26259      * @param {Number} The new width
26260      */
26261     setTabWidth : function(width){
26262         this.currentTabWidth = width;
26263         for(var i = 0, len = this.items.length; i < len; i++) {
26264                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26265         }
26266     },
26267
26268     /**
26269      * Destroys this TabPanel
26270      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26271      */
26272     destroy : function(removeEl){
26273         Roo.EventManager.removeResizeListener(this.onResize, this);
26274         for(var i = 0, len = this.items.length; i < len; i++){
26275             this.items[i].purgeListeners();
26276         }
26277         if(removeEl === true){
26278             this.el.update("");
26279             this.el.remove();
26280         }
26281     }
26282 });
26283
26284 /**
26285  * @class Roo.TabPanelItem
26286  * @extends Roo.util.Observable
26287  * Represents an individual item (tab plus body) in a TabPanel.
26288  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26289  * @param {String} id The id of this TabPanelItem
26290  * @param {String} text The text for the tab of this TabPanelItem
26291  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26292  */
26293 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26294     /**
26295      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26296      * @type Roo.TabPanel
26297      */
26298     this.tabPanel = tabPanel;
26299     /**
26300      * The id for this TabPanelItem
26301      * @type String
26302      */
26303     this.id = id;
26304     /** @private */
26305     this.disabled = false;
26306     /** @private */
26307     this.text = text;
26308     /** @private */
26309     this.loaded = false;
26310     this.closable = closable;
26311
26312     /**
26313      * The body element for this TabPanelItem.
26314      * @type Roo.Element
26315      */
26316     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26317     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26318     this.bodyEl.setStyle("display", "block");
26319     this.bodyEl.setStyle("zoom", "1");
26320     this.hideAction();
26321
26322     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26323     /** @private */
26324     this.el = Roo.get(els.el, true);
26325     this.inner = Roo.get(els.inner, true);
26326     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26327     this.pnode = Roo.get(els.el.parentNode, true);
26328     this.el.on("mousedown", this.onTabMouseDown, this);
26329     this.el.on("click", this.onTabClick, this);
26330     /** @private */
26331     if(closable){
26332         var c = Roo.get(els.close, true);
26333         c.dom.title = this.closeText;
26334         c.addClassOnOver("close-over");
26335         c.on("click", this.closeClick, this);
26336      }
26337
26338     this.addEvents({
26339          /**
26340          * @event activate
26341          * Fires when this tab becomes the active tab.
26342          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26343          * @param {Roo.TabPanelItem} this
26344          */
26345         "activate": true,
26346         /**
26347          * @event beforeclose
26348          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26349          * @param {Roo.TabPanelItem} this
26350          * @param {Object} e Set cancel to true on this object to cancel the close.
26351          */
26352         "beforeclose": true,
26353         /**
26354          * @event close
26355          * Fires when this tab is closed.
26356          * @param {Roo.TabPanelItem} this
26357          */
26358          "close": true,
26359         /**
26360          * @event deactivate
26361          * Fires when this tab is no longer the active tab.
26362          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26363          * @param {Roo.TabPanelItem} this
26364          */
26365          "deactivate" : true
26366     });
26367     this.hidden = false;
26368
26369     Roo.TabPanelItem.superclass.constructor.call(this);
26370 };
26371
26372 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26373     purgeListeners : function(){
26374        Roo.util.Observable.prototype.purgeListeners.call(this);
26375        this.el.removeAllListeners();
26376     },
26377     /**
26378      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26379      */
26380     show : function(){
26381         this.pnode.addClass("on");
26382         this.showAction();
26383         if(Roo.isOpera){
26384             this.tabPanel.stripWrap.repaint();
26385         }
26386         this.fireEvent("activate", this.tabPanel, this);
26387     },
26388
26389     /**
26390      * Returns true if this tab is the active tab.
26391      * @return {Boolean}
26392      */
26393     isActive : function(){
26394         return this.tabPanel.getActiveTab() == this;
26395     },
26396
26397     /**
26398      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
26399      */
26400     hide : function(){
26401         this.pnode.removeClass("on");
26402         this.hideAction();
26403         this.fireEvent("deactivate", this.tabPanel, this);
26404     },
26405
26406     hideAction : function(){
26407         this.bodyEl.hide();
26408         this.bodyEl.setStyle("position", "absolute");
26409         this.bodyEl.setLeft("-20000px");
26410         this.bodyEl.setTop("-20000px");
26411     },
26412
26413     showAction : function(){
26414         this.bodyEl.setStyle("position", "relative");
26415         this.bodyEl.setTop("");
26416         this.bodyEl.setLeft("");
26417         this.bodyEl.show();
26418     },
26419
26420     /**
26421      * Set the tooltip for the tab.
26422      * @param {String} tooltip The tab's tooltip
26423      */
26424     setTooltip : function(text){
26425         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
26426             this.textEl.dom.qtip = text;
26427             this.textEl.dom.removeAttribute('title');
26428         }else{
26429             this.textEl.dom.title = text;
26430         }
26431     },
26432
26433     onTabClick : function(e){
26434         e.preventDefault();
26435         this.tabPanel.activate(this.id);
26436     },
26437
26438     onTabMouseDown : function(e){
26439         e.preventDefault();
26440         this.tabPanel.activate(this.id);
26441     },
26442
26443     getWidth : function(){
26444         return this.inner.getWidth();
26445     },
26446
26447     setWidth : function(width){
26448         var iwidth = width - this.pnode.getPadding("lr");
26449         this.inner.setWidth(iwidth);
26450         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
26451         this.pnode.setWidth(width);
26452     },
26453
26454     /**
26455      * Show or hide the tab
26456      * @param {Boolean} hidden True to hide or false to show.
26457      */
26458     setHidden : function(hidden){
26459         this.hidden = hidden;
26460         this.pnode.setStyle("display", hidden ? "none" : "");
26461     },
26462
26463     /**
26464      * Returns true if this tab is "hidden"
26465      * @return {Boolean}
26466      */
26467     isHidden : function(){
26468         return this.hidden;
26469     },
26470
26471     /**
26472      * Returns the text for this tab
26473      * @return {String}
26474      */
26475     getText : function(){
26476         return this.text;
26477     },
26478
26479     autoSize : function(){
26480         //this.el.beginMeasure();
26481         this.textEl.setWidth(1);
26482         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
26483         //this.el.endMeasure();
26484     },
26485
26486     /**
26487      * Sets the text for the tab (Note: this also sets the tooltip text)
26488      * @param {String} text The tab's text and tooltip
26489      */
26490     setText : function(text){
26491         this.text = text;
26492         this.textEl.update(text);
26493         this.setTooltip(text);
26494         if(!this.tabPanel.resizeTabs){
26495             this.autoSize();
26496         }
26497     },
26498     /**
26499      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
26500      */
26501     activate : function(){
26502         this.tabPanel.activate(this.id);
26503     },
26504
26505     /**
26506      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
26507      */
26508     disable : function(){
26509         if(this.tabPanel.active != this){
26510             this.disabled = true;
26511             this.pnode.addClass("disabled");
26512         }
26513     },
26514
26515     /**
26516      * Enables this TabPanelItem if it was previously disabled.
26517      */
26518     enable : function(){
26519         this.disabled = false;
26520         this.pnode.removeClass("disabled");
26521     },
26522
26523     /**
26524      * Sets the content for this TabPanelItem.
26525      * @param {String} content The content
26526      * @param {Boolean} loadScripts true to look for and load scripts
26527      */
26528     setContent : function(content, loadScripts){
26529         this.bodyEl.update(content, loadScripts);
26530     },
26531
26532     /**
26533      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
26534      * @return {Roo.UpdateManager} The UpdateManager
26535      */
26536     getUpdateManager : function(){
26537         return this.bodyEl.getUpdateManager();
26538     },
26539
26540     /**
26541      * Set a URL to be used to load the content for this TabPanelItem.
26542      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
26543      * @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)
26544      * @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)
26545      * @return {Roo.UpdateManager} The UpdateManager
26546      */
26547     setUrl : function(url, params, loadOnce){
26548         if(this.refreshDelegate){
26549             this.un('activate', this.refreshDelegate);
26550         }
26551         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
26552         this.on("activate", this.refreshDelegate);
26553         return this.bodyEl.getUpdateManager();
26554     },
26555
26556     /** @private */
26557     _handleRefresh : function(url, params, loadOnce){
26558         if(!loadOnce || !this.loaded){
26559             var updater = this.bodyEl.getUpdateManager();
26560             updater.update(url, params, this._setLoaded.createDelegate(this));
26561         }
26562     },
26563
26564     /**
26565      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
26566      *   Will fail silently if the setUrl method has not been called.
26567      *   This does not activate the panel, just updates its content.
26568      */
26569     refresh : function(){
26570         if(this.refreshDelegate){
26571            this.loaded = false;
26572            this.refreshDelegate();
26573         }
26574     },
26575
26576     /** @private */
26577     _setLoaded : function(){
26578         this.loaded = true;
26579     },
26580
26581     /** @private */
26582     closeClick : function(e){
26583         var o = {};
26584         e.stopEvent();
26585         this.fireEvent("beforeclose", this, o);
26586         if(o.cancel !== true){
26587             this.tabPanel.removeTab(this.id);
26588         }
26589     },
26590     /**
26591      * The text displayed in the tooltip for the close icon.
26592      * @type String
26593      */
26594     closeText : "Close this tab"
26595 });
26596
26597 /** @private */
26598 Roo.TabPanel.prototype.createStrip = function(container){
26599     var strip = document.createElement("div");
26600     strip.className = "x-tabs-wrap";
26601     container.appendChild(strip);
26602     return strip;
26603 };
26604 /** @private */
26605 Roo.TabPanel.prototype.createStripList = function(strip){
26606     // div wrapper for retard IE
26607     // returns the "tr" element.
26608     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26609         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26610         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26611     return strip.firstChild.firstChild.firstChild.firstChild;
26612 };
26613 /** @private */
26614 Roo.TabPanel.prototype.createBody = function(container){
26615     var body = document.createElement("div");
26616     Roo.id(body, "tab-body");
26617     Roo.fly(body).addClass("x-tabs-body");
26618     container.appendChild(body);
26619     return body;
26620 };
26621 /** @private */
26622 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26623     var body = Roo.getDom(id);
26624     if(!body){
26625         body = document.createElement("div");
26626         body.id = id;
26627     }
26628     Roo.fly(body).addClass("x-tabs-item-body");
26629     bodyEl.insertBefore(body, bodyEl.firstChild);
26630     return body;
26631 };
26632 /** @private */
26633 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26634     var td = document.createElement("td");
26635     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26636     //stripEl.appendChild(td);
26637     if(closable){
26638         td.className = "x-tabs-closable";
26639         if(!this.closeTpl){
26640             this.closeTpl = new Roo.Template(
26641                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26642                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26643                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26644             );
26645         }
26646         var el = this.closeTpl.overwrite(td, {"text": text});
26647         var close = el.getElementsByTagName("div")[0];
26648         var inner = el.getElementsByTagName("em")[0];
26649         return {"el": el, "close": close, "inner": inner};
26650     } else {
26651         if(!this.tabTpl){
26652             this.tabTpl = new Roo.Template(
26653                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26654                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26655             );
26656         }
26657         var el = this.tabTpl.overwrite(td, {"text": text});
26658         var inner = el.getElementsByTagName("em")[0];
26659         return {"el": el, "inner": inner};
26660     }
26661 };/*
26662  * Based on:
26663  * Ext JS Library 1.1.1
26664  * Copyright(c) 2006-2007, Ext JS, LLC.
26665  *
26666  * Originally Released Under LGPL - original licence link has changed is not relivant.
26667  *
26668  * Fork - LGPL
26669  * <script type="text/javascript">
26670  */
26671
26672 /**
26673  * @class Roo.Button
26674  * @extends Roo.util.Observable
26675  * Simple Button class
26676  * @cfg {String} text The button text
26677  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26678  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26679  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26680  * @cfg {Object} scope The scope of the handler
26681  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26682  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26683  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26684  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26685  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26686  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26687    applies if enableToggle = true)
26688  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26689  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26690   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26691  * @constructor
26692  * Create a new button
26693  * @param {Object} config The config object
26694  */
26695 Roo.Button = function(renderTo, config)
26696 {
26697     if (!config) {
26698         config = renderTo;
26699         renderTo = config.renderTo || false;
26700     }
26701     
26702     Roo.apply(this, config);
26703     this.addEvents({
26704         /**
26705              * @event click
26706              * Fires when this button is clicked
26707              * @param {Button} this
26708              * @param {EventObject} e The click event
26709              */
26710             "click" : true,
26711         /**
26712              * @event toggle
26713              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26714              * @param {Button} this
26715              * @param {Boolean} pressed
26716              */
26717             "toggle" : true,
26718         /**
26719              * @event mouseover
26720              * Fires when the mouse hovers over the button
26721              * @param {Button} this
26722              * @param {Event} e The event object
26723              */
26724         'mouseover' : true,
26725         /**
26726              * @event mouseout
26727              * Fires when the mouse exits the button
26728              * @param {Button} this
26729              * @param {Event} e The event object
26730              */
26731         'mouseout': true,
26732          /**
26733              * @event render
26734              * Fires when the button is rendered
26735              * @param {Button} this
26736              */
26737         'render': true
26738     });
26739     if(this.menu){
26740         this.menu = Roo.menu.MenuMgr.get(this.menu);
26741     }
26742     // register listeners first!!  - so render can be captured..
26743     Roo.util.Observable.call(this);
26744     if(renderTo){
26745         this.render(renderTo);
26746     }
26747     
26748   
26749 };
26750
26751 Roo.extend(Roo.Button, Roo.util.Observable, {
26752     /**
26753      * 
26754      */
26755     
26756     /**
26757      * Read-only. True if this button is hidden
26758      * @type Boolean
26759      */
26760     hidden : false,
26761     /**
26762      * Read-only. True if this button is disabled
26763      * @type Boolean
26764      */
26765     disabled : false,
26766     /**
26767      * Read-only. True if this button is pressed (only if enableToggle = true)
26768      * @type Boolean
26769      */
26770     pressed : false,
26771
26772     /**
26773      * @cfg {Number} tabIndex 
26774      * The DOM tabIndex for this button (defaults to undefined)
26775      */
26776     tabIndex : undefined,
26777
26778     /**
26779      * @cfg {Boolean} enableToggle
26780      * True to enable pressed/not pressed toggling (defaults to false)
26781      */
26782     enableToggle: false,
26783     /**
26784      * @cfg {Mixed} menu
26785      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26786      */
26787     menu : undefined,
26788     /**
26789      * @cfg {String} menuAlign
26790      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26791      */
26792     menuAlign : "tl-bl?",
26793
26794     /**
26795      * @cfg {String} iconCls
26796      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26797      */
26798     iconCls : undefined,
26799     /**
26800      * @cfg {String} type
26801      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26802      */
26803     type : 'button',
26804
26805     // private
26806     menuClassTarget: 'tr',
26807
26808     /**
26809      * @cfg {String} clickEvent
26810      * The type of event to map to the button's event handler (defaults to 'click')
26811      */
26812     clickEvent : 'click',
26813
26814     /**
26815      * @cfg {Boolean} handleMouseEvents
26816      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26817      */
26818     handleMouseEvents : true,
26819
26820     /**
26821      * @cfg {String} tooltipType
26822      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26823      */
26824     tooltipType : 'qtip',
26825
26826     /**
26827      * @cfg {String} cls
26828      * A CSS class to apply to the button's main element.
26829      */
26830     
26831     /**
26832      * @cfg {Roo.Template} template (Optional)
26833      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26834      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26835      * require code modifications if required elements (e.g. a button) aren't present.
26836      */
26837
26838     // private
26839     render : function(renderTo){
26840         var btn;
26841         if(this.hideParent){
26842             this.parentEl = Roo.get(renderTo);
26843         }
26844         if(!this.dhconfig){
26845             if(!this.template){
26846                 if(!Roo.Button.buttonTemplate){
26847                     // hideous table template
26848                     Roo.Button.buttonTemplate = new Roo.Template(
26849                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26850                         '<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>',
26851                         "</tr></tbody></table>");
26852                 }
26853                 this.template = Roo.Button.buttonTemplate;
26854             }
26855             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26856             var btnEl = btn.child("button:first");
26857             btnEl.on('focus', this.onFocus, this);
26858             btnEl.on('blur', this.onBlur, this);
26859             if(this.cls){
26860                 btn.addClass(this.cls);
26861             }
26862             if(this.icon){
26863                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26864             }
26865             if(this.iconCls){
26866                 btnEl.addClass(this.iconCls);
26867                 if(!this.cls){
26868                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26869                 }
26870             }
26871             if(this.tabIndex !== undefined){
26872                 btnEl.dom.tabIndex = this.tabIndex;
26873             }
26874             if(this.tooltip){
26875                 if(typeof this.tooltip == 'object'){
26876                     Roo.QuickTips.tips(Roo.apply({
26877                           target: btnEl.id
26878                     }, this.tooltip));
26879                 } else {
26880                     btnEl.dom[this.tooltipType] = this.tooltip;
26881                 }
26882             }
26883         }else{
26884             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26885         }
26886         this.el = btn;
26887         if(this.id){
26888             this.el.dom.id = this.el.id = this.id;
26889         }
26890         if(this.menu){
26891             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26892             this.menu.on("show", this.onMenuShow, this);
26893             this.menu.on("hide", this.onMenuHide, this);
26894         }
26895         btn.addClass("x-btn");
26896         if(Roo.isIE && !Roo.isIE7){
26897             this.autoWidth.defer(1, this);
26898         }else{
26899             this.autoWidth();
26900         }
26901         if(this.handleMouseEvents){
26902             btn.on("mouseover", this.onMouseOver, this);
26903             btn.on("mouseout", this.onMouseOut, this);
26904             btn.on("mousedown", this.onMouseDown, this);
26905         }
26906         btn.on(this.clickEvent, this.onClick, this);
26907         //btn.on("mouseup", this.onMouseUp, this);
26908         if(this.hidden){
26909             this.hide();
26910         }
26911         if(this.disabled){
26912             this.disable();
26913         }
26914         Roo.ButtonToggleMgr.register(this);
26915         if(this.pressed){
26916             this.el.addClass("x-btn-pressed");
26917         }
26918         if(this.repeat){
26919             var repeater = new Roo.util.ClickRepeater(btn,
26920                 typeof this.repeat == "object" ? this.repeat : {}
26921             );
26922             repeater.on("click", this.onClick,  this);
26923         }
26924         
26925         this.fireEvent('render', this);
26926         
26927     },
26928     /**
26929      * Returns the button's underlying element
26930      * @return {Roo.Element} The element
26931      */
26932     getEl : function(){
26933         return this.el;  
26934     },
26935     
26936     /**
26937      * Destroys this Button and removes any listeners.
26938      */
26939     destroy : function(){
26940         Roo.ButtonToggleMgr.unregister(this);
26941         this.el.removeAllListeners();
26942         this.purgeListeners();
26943         this.el.remove();
26944     },
26945
26946     // private
26947     autoWidth : function(){
26948         if(this.el){
26949             this.el.setWidth("auto");
26950             if(Roo.isIE7 && Roo.isStrict){
26951                 var ib = this.el.child('button');
26952                 if(ib && ib.getWidth() > 20){
26953                     ib.clip();
26954                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26955                 }
26956             }
26957             if(this.minWidth){
26958                 if(this.hidden){
26959                     this.el.beginMeasure();
26960                 }
26961                 if(this.el.getWidth() < this.minWidth){
26962                     this.el.setWidth(this.minWidth);
26963                 }
26964                 if(this.hidden){
26965                     this.el.endMeasure();
26966                 }
26967             }
26968         }
26969     },
26970
26971     /**
26972      * Assigns this button's click handler
26973      * @param {Function} handler The function to call when the button is clicked
26974      * @param {Object} scope (optional) Scope for the function passed in
26975      */
26976     setHandler : function(handler, scope){
26977         this.handler = handler;
26978         this.scope = scope;  
26979     },
26980     
26981     /**
26982      * Sets this button's text
26983      * @param {String} text The button text
26984      */
26985     setText : function(text){
26986         this.text = text;
26987         if(this.el){
26988             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26989         }
26990         this.autoWidth();
26991     },
26992     
26993     /**
26994      * Gets the text for this button
26995      * @return {String} The button text
26996      */
26997     getText : function(){
26998         return this.text;  
26999     },
27000     
27001     /**
27002      * Show this button
27003      */
27004     show: function(){
27005         this.hidden = false;
27006         if(this.el){
27007             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27008         }
27009     },
27010     
27011     /**
27012      * Hide this button
27013      */
27014     hide: function(){
27015         this.hidden = true;
27016         if(this.el){
27017             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27018         }
27019     },
27020     
27021     /**
27022      * Convenience function for boolean show/hide
27023      * @param {Boolean} visible True to show, false to hide
27024      */
27025     setVisible: function(visible){
27026         if(visible) {
27027             this.show();
27028         }else{
27029             this.hide();
27030         }
27031     },
27032     
27033     /**
27034      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27035      * @param {Boolean} state (optional) Force a particular state
27036      */
27037     toggle : function(state){
27038         state = state === undefined ? !this.pressed : state;
27039         if(state != this.pressed){
27040             if(state){
27041                 this.el.addClass("x-btn-pressed");
27042                 this.pressed = true;
27043                 this.fireEvent("toggle", this, true);
27044             }else{
27045                 this.el.removeClass("x-btn-pressed");
27046                 this.pressed = false;
27047                 this.fireEvent("toggle", this, false);
27048             }
27049             if(this.toggleHandler){
27050                 this.toggleHandler.call(this.scope || this, this, state);
27051             }
27052         }
27053     },
27054     
27055     /**
27056      * Focus the button
27057      */
27058     focus : function(){
27059         this.el.child('button:first').focus();
27060     },
27061     
27062     /**
27063      * Disable this button
27064      */
27065     disable : function(){
27066         if(this.el){
27067             this.el.addClass("x-btn-disabled");
27068         }
27069         this.disabled = true;
27070     },
27071     
27072     /**
27073      * Enable this button
27074      */
27075     enable : function(){
27076         if(this.el){
27077             this.el.removeClass("x-btn-disabled");
27078         }
27079         this.disabled = false;
27080     },
27081
27082     /**
27083      * Convenience function for boolean enable/disable
27084      * @param {Boolean} enabled True to enable, false to disable
27085      */
27086     setDisabled : function(v){
27087         this[v !== true ? "enable" : "disable"]();
27088     },
27089
27090     // private
27091     onClick : function(e){
27092         if(e){
27093             e.preventDefault();
27094         }
27095         if(e.button != 0){
27096             return;
27097         }
27098         if(!this.disabled){
27099             if(this.enableToggle){
27100                 this.toggle();
27101             }
27102             if(this.menu && !this.menu.isVisible()){
27103                 this.menu.show(this.el, this.menuAlign);
27104             }
27105             this.fireEvent("click", this, e);
27106             if(this.handler){
27107                 this.el.removeClass("x-btn-over");
27108                 this.handler.call(this.scope || this, this, e);
27109             }
27110         }
27111     },
27112     // private
27113     onMouseOver : function(e){
27114         if(!this.disabled){
27115             this.el.addClass("x-btn-over");
27116             this.fireEvent('mouseover', this, e);
27117         }
27118     },
27119     // private
27120     onMouseOut : function(e){
27121         if(!e.within(this.el,  true)){
27122             this.el.removeClass("x-btn-over");
27123             this.fireEvent('mouseout', this, e);
27124         }
27125     },
27126     // private
27127     onFocus : function(e){
27128         if(!this.disabled){
27129             this.el.addClass("x-btn-focus");
27130         }
27131     },
27132     // private
27133     onBlur : function(e){
27134         this.el.removeClass("x-btn-focus");
27135     },
27136     // private
27137     onMouseDown : function(e){
27138         if(!this.disabled && e.button == 0){
27139             this.el.addClass("x-btn-click");
27140             Roo.get(document).on('mouseup', this.onMouseUp, this);
27141         }
27142     },
27143     // private
27144     onMouseUp : function(e){
27145         if(e.button == 0){
27146             this.el.removeClass("x-btn-click");
27147             Roo.get(document).un('mouseup', this.onMouseUp, this);
27148         }
27149     },
27150     // private
27151     onMenuShow : function(e){
27152         this.el.addClass("x-btn-menu-active");
27153     },
27154     // private
27155     onMenuHide : function(e){
27156         this.el.removeClass("x-btn-menu-active");
27157     }   
27158 });
27159
27160 // Private utility class used by Button
27161 Roo.ButtonToggleMgr = function(){
27162    var groups = {};
27163    
27164    function toggleGroup(btn, state){
27165        if(state){
27166            var g = groups[btn.toggleGroup];
27167            for(var i = 0, l = g.length; i < l; i++){
27168                if(g[i] != btn){
27169                    g[i].toggle(false);
27170                }
27171            }
27172        }
27173    }
27174    
27175    return {
27176        register : function(btn){
27177            if(!btn.toggleGroup){
27178                return;
27179            }
27180            var g = groups[btn.toggleGroup];
27181            if(!g){
27182                g = groups[btn.toggleGroup] = [];
27183            }
27184            g.push(btn);
27185            btn.on("toggle", toggleGroup);
27186        },
27187        
27188        unregister : function(btn){
27189            if(!btn.toggleGroup){
27190                return;
27191            }
27192            var g = groups[btn.toggleGroup];
27193            if(g){
27194                g.remove(btn);
27195                btn.un("toggle", toggleGroup);
27196            }
27197        }
27198    };
27199 }();/*
27200  * Based on:
27201  * Ext JS Library 1.1.1
27202  * Copyright(c) 2006-2007, Ext JS, LLC.
27203  *
27204  * Originally Released Under LGPL - original licence link has changed is not relivant.
27205  *
27206  * Fork - LGPL
27207  * <script type="text/javascript">
27208  */
27209  
27210 /**
27211  * @class Roo.SplitButton
27212  * @extends Roo.Button
27213  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27214  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27215  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27216  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27217  * @cfg {String} arrowTooltip The title attribute of the arrow
27218  * @constructor
27219  * Create a new menu button
27220  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27221  * @param {Object} config The config object
27222  */
27223 Roo.SplitButton = function(renderTo, config){
27224     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27225     /**
27226      * @event arrowclick
27227      * Fires when this button's arrow is clicked
27228      * @param {SplitButton} this
27229      * @param {EventObject} e The click event
27230      */
27231     this.addEvents({"arrowclick":true});
27232 };
27233
27234 Roo.extend(Roo.SplitButton, Roo.Button, {
27235     render : function(renderTo){
27236         // this is one sweet looking template!
27237         var tpl = new Roo.Template(
27238             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27239             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27240             '<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>',
27241             "</tbody></table></td><td>",
27242             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27243             '<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>',
27244             "</tbody></table></td></tr></table>"
27245         );
27246         var btn = tpl.append(renderTo, [this.text, this.type], true);
27247         var btnEl = btn.child("button");
27248         if(this.cls){
27249             btn.addClass(this.cls);
27250         }
27251         if(this.icon){
27252             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27253         }
27254         if(this.iconCls){
27255             btnEl.addClass(this.iconCls);
27256             if(!this.cls){
27257                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27258             }
27259         }
27260         this.el = btn;
27261         if(this.handleMouseEvents){
27262             btn.on("mouseover", this.onMouseOver, this);
27263             btn.on("mouseout", this.onMouseOut, this);
27264             btn.on("mousedown", this.onMouseDown, this);
27265             btn.on("mouseup", this.onMouseUp, this);
27266         }
27267         btn.on(this.clickEvent, this.onClick, this);
27268         if(this.tooltip){
27269             if(typeof this.tooltip == 'object'){
27270                 Roo.QuickTips.tips(Roo.apply({
27271                       target: btnEl.id
27272                 }, this.tooltip));
27273             } else {
27274                 btnEl.dom[this.tooltipType] = this.tooltip;
27275             }
27276         }
27277         if(this.arrowTooltip){
27278             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27279         }
27280         if(this.hidden){
27281             this.hide();
27282         }
27283         if(this.disabled){
27284             this.disable();
27285         }
27286         if(this.pressed){
27287             this.el.addClass("x-btn-pressed");
27288         }
27289         if(Roo.isIE && !Roo.isIE7){
27290             this.autoWidth.defer(1, this);
27291         }else{
27292             this.autoWidth();
27293         }
27294         if(this.menu){
27295             this.menu.on("show", this.onMenuShow, this);
27296             this.menu.on("hide", this.onMenuHide, this);
27297         }
27298         this.fireEvent('render', this);
27299     },
27300
27301     // private
27302     autoWidth : function(){
27303         if(this.el){
27304             var tbl = this.el.child("table:first");
27305             var tbl2 = this.el.child("table:last");
27306             this.el.setWidth("auto");
27307             tbl.setWidth("auto");
27308             if(Roo.isIE7 && Roo.isStrict){
27309                 var ib = this.el.child('button:first');
27310                 if(ib && ib.getWidth() > 20){
27311                     ib.clip();
27312                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27313                 }
27314             }
27315             if(this.minWidth){
27316                 if(this.hidden){
27317                     this.el.beginMeasure();
27318                 }
27319                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27320                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27321                 }
27322                 if(this.hidden){
27323                     this.el.endMeasure();
27324                 }
27325             }
27326             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27327         } 
27328     },
27329     /**
27330      * Sets this button's click handler
27331      * @param {Function} handler The function to call when the button is clicked
27332      * @param {Object} scope (optional) Scope for the function passed above
27333      */
27334     setHandler : function(handler, scope){
27335         this.handler = handler;
27336         this.scope = scope;  
27337     },
27338     
27339     /**
27340      * Sets this button's arrow click handler
27341      * @param {Function} handler The function to call when the arrow is clicked
27342      * @param {Object} scope (optional) Scope for the function passed above
27343      */
27344     setArrowHandler : function(handler, scope){
27345         this.arrowHandler = handler;
27346         this.scope = scope;  
27347     },
27348     
27349     /**
27350      * Focus the button
27351      */
27352     focus : function(){
27353         if(this.el){
27354             this.el.child("button:first").focus();
27355         }
27356     },
27357
27358     // private
27359     onClick : function(e){
27360         e.preventDefault();
27361         if(!this.disabled){
27362             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27363                 if(this.menu && !this.menu.isVisible()){
27364                     this.menu.show(this.el, this.menuAlign);
27365                 }
27366                 this.fireEvent("arrowclick", this, e);
27367                 if(this.arrowHandler){
27368                     this.arrowHandler.call(this.scope || this, this, e);
27369                 }
27370             }else{
27371                 this.fireEvent("click", this, e);
27372                 if(this.handler){
27373                     this.handler.call(this.scope || this, this, e);
27374                 }
27375             }
27376         }
27377     },
27378     // private
27379     onMouseDown : function(e){
27380         if(!this.disabled){
27381             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27382         }
27383     },
27384     // private
27385     onMouseUp : function(e){
27386         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27387     }   
27388 });
27389
27390
27391 // backwards compat
27392 Roo.MenuButton = Roo.SplitButton;/*
27393  * Based on:
27394  * Ext JS Library 1.1.1
27395  * Copyright(c) 2006-2007, Ext JS, LLC.
27396  *
27397  * Originally Released Under LGPL - original licence link has changed is not relivant.
27398  *
27399  * Fork - LGPL
27400  * <script type="text/javascript">
27401  */
27402
27403 /**
27404  * @class Roo.Toolbar
27405  * Basic Toolbar class.
27406  * @constructor
27407  * Creates a new Toolbar
27408  * @param {Object} container The config object
27409  */ 
27410 Roo.Toolbar = function(container, buttons, config)
27411 {
27412     /// old consturctor format still supported..
27413     if(container instanceof Array){ // omit the container for later rendering
27414         buttons = container;
27415         config = buttons;
27416         container = null;
27417     }
27418     if (typeof(container) == 'object' && container.xtype) {
27419         config = container;
27420         container = config.container;
27421         buttons = config.buttons || []; // not really - use items!!
27422     }
27423     var xitems = [];
27424     if (config && config.items) {
27425         xitems = config.items;
27426         delete config.items;
27427     }
27428     Roo.apply(this, config);
27429     this.buttons = buttons;
27430     
27431     if(container){
27432         this.render(container);
27433     }
27434     this.xitems = xitems;
27435     Roo.each(xitems, function(b) {
27436         this.add(b);
27437     }, this);
27438     
27439 };
27440
27441 Roo.Toolbar.prototype = {
27442     /**
27443      * @cfg {Array} items
27444      * array of button configs or elements to add (will be converted to a MixedCollection)
27445      */
27446     
27447     /**
27448      * @cfg {String/HTMLElement/Element} container
27449      * The id or element that will contain the toolbar
27450      */
27451     // private
27452     render : function(ct){
27453         this.el = Roo.get(ct);
27454         if(this.cls){
27455             this.el.addClass(this.cls);
27456         }
27457         // using a table allows for vertical alignment
27458         // 100% width is needed by Safari...
27459         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
27460         this.tr = this.el.child("tr", true);
27461         var autoId = 0;
27462         this.items = new Roo.util.MixedCollection(false, function(o){
27463             return o.id || ("item" + (++autoId));
27464         });
27465         if(this.buttons){
27466             this.add.apply(this, this.buttons);
27467             delete this.buttons;
27468         }
27469     },
27470
27471     /**
27472      * Adds element(s) to the toolbar -- this function takes a variable number of 
27473      * arguments of mixed type and adds them to the toolbar.
27474      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
27475      * <ul>
27476      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
27477      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
27478      * <li>Field: Any form field (equivalent to {@link #addField})</li>
27479      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
27480      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
27481      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
27482      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
27483      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
27484      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
27485      * </ul>
27486      * @param {Mixed} arg2
27487      * @param {Mixed} etc.
27488      */
27489     add : function(){
27490         var a = arguments, l = a.length;
27491         for(var i = 0; i < l; i++){
27492             this._add(a[i]);
27493         }
27494     },
27495     // private..
27496     _add : function(el) {
27497         
27498         if (el.xtype) {
27499             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
27500         }
27501         
27502         if (el.applyTo){ // some kind of form field
27503             return this.addField(el);
27504         } 
27505         if (el.render){ // some kind of Toolbar.Item
27506             return this.addItem(el);
27507         }
27508         if (typeof el == "string"){ // string
27509             if(el == "separator" || el == "-"){
27510                 return this.addSeparator();
27511             }
27512             if (el == " "){
27513                 return this.addSpacer();
27514             }
27515             if(el == "->"){
27516                 return this.addFill();
27517             }
27518             return this.addText(el);
27519             
27520         }
27521         if(el.tagName){ // element
27522             return this.addElement(el);
27523         }
27524         if(typeof el == "object"){ // must be button config?
27525             return this.addButton(el);
27526         }
27527         // and now what?!?!
27528         return false;
27529         
27530     },
27531     
27532     /**
27533      * Add an Xtype element
27534      * @param {Object} xtype Xtype Object
27535      * @return {Object} created Object
27536      */
27537     addxtype : function(e){
27538         return this.add(e);  
27539     },
27540     
27541     /**
27542      * Returns the Element for this toolbar.
27543      * @return {Roo.Element}
27544      */
27545     getEl : function(){
27546         return this.el;  
27547     },
27548     
27549     /**
27550      * Adds a separator
27551      * @return {Roo.Toolbar.Item} The separator item
27552      */
27553     addSeparator : function(){
27554         return this.addItem(new Roo.Toolbar.Separator());
27555     },
27556
27557     /**
27558      * Adds a spacer element
27559      * @return {Roo.Toolbar.Spacer} The spacer item
27560      */
27561     addSpacer : function(){
27562         return this.addItem(new Roo.Toolbar.Spacer());
27563     },
27564
27565     /**
27566      * Adds a fill element that forces subsequent additions to the right side of the toolbar
27567      * @return {Roo.Toolbar.Fill} The fill item
27568      */
27569     addFill : function(){
27570         return this.addItem(new Roo.Toolbar.Fill());
27571     },
27572
27573     /**
27574      * Adds any standard HTML element to the toolbar
27575      * @param {String/HTMLElement/Element} el The element or id of the element to add
27576      * @return {Roo.Toolbar.Item} The element's item
27577      */
27578     addElement : function(el){
27579         return this.addItem(new Roo.Toolbar.Item(el));
27580     },
27581     /**
27582      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
27583      * @type Roo.util.MixedCollection  
27584      */
27585     items : false,
27586      
27587     /**
27588      * Adds any Toolbar.Item or subclass
27589      * @param {Roo.Toolbar.Item} item
27590      * @return {Roo.Toolbar.Item} The item
27591      */
27592     addItem : function(item){
27593         var td = this.nextBlock();
27594         item.render(td);
27595         this.items.add(item);
27596         return item;
27597     },
27598     
27599     /**
27600      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
27601      * @param {Object/Array} config A button config or array of configs
27602      * @return {Roo.Toolbar.Button/Array}
27603      */
27604     addButton : function(config){
27605         if(config instanceof Array){
27606             var buttons = [];
27607             for(var i = 0, len = config.length; i < len; i++) {
27608                 buttons.push(this.addButton(config[i]));
27609             }
27610             return buttons;
27611         }
27612         var b = config;
27613         if(!(config instanceof Roo.Toolbar.Button)){
27614             b = config.split ?
27615                 new Roo.Toolbar.SplitButton(config) :
27616                 new Roo.Toolbar.Button(config);
27617         }
27618         var td = this.nextBlock();
27619         b.render(td);
27620         this.items.add(b);
27621         return b;
27622     },
27623     
27624     /**
27625      * Adds text to the toolbar
27626      * @param {String} text The text to add
27627      * @return {Roo.Toolbar.Item} The element's item
27628      */
27629     addText : function(text){
27630         return this.addItem(new Roo.Toolbar.TextItem(text));
27631     },
27632     
27633     /**
27634      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27635      * @param {Number} index The index where the item is to be inserted
27636      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27637      * @return {Roo.Toolbar.Button/Item}
27638      */
27639     insertButton : function(index, item){
27640         if(item instanceof Array){
27641             var buttons = [];
27642             for(var i = 0, len = item.length; i < len; i++) {
27643                buttons.push(this.insertButton(index + i, item[i]));
27644             }
27645             return buttons;
27646         }
27647         if (!(item instanceof Roo.Toolbar.Button)){
27648            item = new Roo.Toolbar.Button(item);
27649         }
27650         var td = document.createElement("td");
27651         this.tr.insertBefore(td, this.tr.childNodes[index]);
27652         item.render(td);
27653         this.items.insert(index, item);
27654         return item;
27655     },
27656     
27657     /**
27658      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27659      * @param {Object} config
27660      * @return {Roo.Toolbar.Item} The element's item
27661      */
27662     addDom : function(config, returnEl){
27663         var td = this.nextBlock();
27664         Roo.DomHelper.overwrite(td, config);
27665         var ti = new Roo.Toolbar.Item(td.firstChild);
27666         ti.render(td);
27667         this.items.add(ti);
27668         return ti;
27669     },
27670
27671     /**
27672      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27673      * @type Roo.util.MixedCollection  
27674      */
27675     fields : false,
27676     
27677     /**
27678      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27679      * Note: the field should not have been rendered yet. For a field that has already been
27680      * rendered, use {@link #addElement}.
27681      * @param {Roo.form.Field} field
27682      * @return {Roo.ToolbarItem}
27683      */
27684      
27685       
27686     addField : function(field) {
27687         if (!this.fields) {
27688             var autoId = 0;
27689             this.fields = new Roo.util.MixedCollection(false, function(o){
27690                 return o.id || ("item" + (++autoId));
27691             });
27692
27693         }
27694         
27695         var td = this.nextBlock();
27696         field.render(td);
27697         var ti = new Roo.Toolbar.Item(td.firstChild);
27698         ti.render(td);
27699         this.items.add(ti);
27700         this.fields.add(field);
27701         return ti;
27702     },
27703     /**
27704      * Hide the toolbar
27705      * @method hide
27706      */
27707      
27708       
27709     hide : function()
27710     {
27711         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27712         this.el.child('div').hide();
27713     },
27714     /**
27715      * Show the toolbar
27716      * @method show
27717      */
27718     show : function()
27719     {
27720         this.el.child('div').show();
27721     },
27722       
27723     // private
27724     nextBlock : function(){
27725         var td = document.createElement("td");
27726         this.tr.appendChild(td);
27727         return td;
27728     },
27729
27730     // private
27731     destroy : function(){
27732         if(this.items){ // rendered?
27733             Roo.destroy.apply(Roo, this.items.items);
27734         }
27735         if(this.fields){ // rendered?
27736             Roo.destroy.apply(Roo, this.fields.items);
27737         }
27738         Roo.Element.uncache(this.el, this.tr);
27739     }
27740 };
27741
27742 /**
27743  * @class Roo.Toolbar.Item
27744  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27745  * @constructor
27746  * Creates a new Item
27747  * @param {HTMLElement} el 
27748  */
27749 Roo.Toolbar.Item = function(el){
27750     this.el = Roo.getDom(el);
27751     this.id = Roo.id(this.el);
27752     this.hidden = false;
27753 };
27754
27755 Roo.Toolbar.Item.prototype = {
27756     
27757     /**
27758      * Get this item's HTML Element
27759      * @return {HTMLElement}
27760      */
27761     getEl : function(){
27762        return this.el;  
27763     },
27764
27765     // private
27766     render : function(td){
27767         this.td = td;
27768         td.appendChild(this.el);
27769     },
27770     
27771     /**
27772      * Removes and destroys this item.
27773      */
27774     destroy : function(){
27775         this.td.parentNode.removeChild(this.td);
27776     },
27777     
27778     /**
27779      * Shows this item.
27780      */
27781     show: function(){
27782         this.hidden = false;
27783         this.td.style.display = "";
27784     },
27785     
27786     /**
27787      * Hides this item.
27788      */
27789     hide: function(){
27790         this.hidden = true;
27791         this.td.style.display = "none";
27792     },
27793     
27794     /**
27795      * Convenience function for boolean show/hide.
27796      * @param {Boolean} visible true to show/false to hide
27797      */
27798     setVisible: function(visible){
27799         if(visible) {
27800             this.show();
27801         }else{
27802             this.hide();
27803         }
27804     },
27805     
27806     /**
27807      * Try to focus this item.
27808      */
27809     focus : function(){
27810         Roo.fly(this.el).focus();
27811     },
27812     
27813     /**
27814      * Disables this item.
27815      */
27816     disable : function(){
27817         Roo.fly(this.td).addClass("x-item-disabled");
27818         this.disabled = true;
27819         this.el.disabled = true;
27820     },
27821     
27822     /**
27823      * Enables this item.
27824      */
27825     enable : function(){
27826         Roo.fly(this.td).removeClass("x-item-disabled");
27827         this.disabled = false;
27828         this.el.disabled = false;
27829     }
27830 };
27831
27832
27833 /**
27834  * @class Roo.Toolbar.Separator
27835  * @extends Roo.Toolbar.Item
27836  * A simple toolbar separator class
27837  * @constructor
27838  * Creates a new Separator
27839  */
27840 Roo.Toolbar.Separator = function(){
27841     var s = document.createElement("span");
27842     s.className = "ytb-sep";
27843     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27844 };
27845 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27846     enable:Roo.emptyFn,
27847     disable:Roo.emptyFn,
27848     focus:Roo.emptyFn
27849 });
27850
27851 /**
27852  * @class Roo.Toolbar.Spacer
27853  * @extends Roo.Toolbar.Item
27854  * A simple element that adds extra horizontal space to a toolbar.
27855  * @constructor
27856  * Creates a new Spacer
27857  */
27858 Roo.Toolbar.Spacer = function(){
27859     var s = document.createElement("div");
27860     s.className = "ytb-spacer";
27861     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27862 };
27863 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27864     enable:Roo.emptyFn,
27865     disable:Roo.emptyFn,
27866     focus:Roo.emptyFn
27867 });
27868
27869 /**
27870  * @class Roo.Toolbar.Fill
27871  * @extends Roo.Toolbar.Spacer
27872  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27873  * @constructor
27874  * Creates a new Spacer
27875  */
27876 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27877     // private
27878     render : function(td){
27879         td.style.width = '100%';
27880         Roo.Toolbar.Fill.superclass.render.call(this, td);
27881     }
27882 });
27883
27884 /**
27885  * @class Roo.Toolbar.TextItem
27886  * @extends Roo.Toolbar.Item
27887  * A simple class that renders text directly into a toolbar.
27888  * @constructor
27889  * Creates a new TextItem
27890  * @param {String} text
27891  */
27892 Roo.Toolbar.TextItem = function(text){
27893     if (typeof(text) == 'object') {
27894         text = text.text;
27895     }
27896     var s = document.createElement("span");
27897     s.className = "ytb-text";
27898     s.innerHTML = text;
27899     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27900 };
27901 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27902     enable:Roo.emptyFn,
27903     disable:Roo.emptyFn,
27904     focus:Roo.emptyFn
27905 });
27906
27907 /**
27908  * @class Roo.Toolbar.Button
27909  * @extends Roo.Button
27910  * A button that renders into a toolbar.
27911  * @constructor
27912  * Creates a new Button
27913  * @param {Object} config A standard {@link Roo.Button} config object
27914  */
27915 Roo.Toolbar.Button = function(config){
27916     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27917 };
27918 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27919     render : function(td){
27920         this.td = td;
27921         Roo.Toolbar.Button.superclass.render.call(this, td);
27922     },
27923     
27924     /**
27925      * Removes and destroys this button
27926      */
27927     destroy : function(){
27928         Roo.Toolbar.Button.superclass.destroy.call(this);
27929         this.td.parentNode.removeChild(this.td);
27930     },
27931     
27932     /**
27933      * Shows this button
27934      */
27935     show: function(){
27936         this.hidden = false;
27937         this.td.style.display = "";
27938     },
27939     
27940     /**
27941      * Hides this button
27942      */
27943     hide: function(){
27944         this.hidden = true;
27945         this.td.style.display = "none";
27946     },
27947
27948     /**
27949      * Disables this item
27950      */
27951     disable : function(){
27952         Roo.fly(this.td).addClass("x-item-disabled");
27953         this.disabled = true;
27954     },
27955
27956     /**
27957      * Enables this item
27958      */
27959     enable : function(){
27960         Roo.fly(this.td).removeClass("x-item-disabled");
27961         this.disabled = false;
27962     }
27963 });
27964 // backwards compat
27965 Roo.ToolbarButton = Roo.Toolbar.Button;
27966
27967 /**
27968  * @class Roo.Toolbar.SplitButton
27969  * @extends Roo.SplitButton
27970  * A menu button that renders into a toolbar.
27971  * @constructor
27972  * Creates a new SplitButton
27973  * @param {Object} config A standard {@link Roo.SplitButton} config object
27974  */
27975 Roo.Toolbar.SplitButton = function(config){
27976     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27977 };
27978 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27979     render : function(td){
27980         this.td = td;
27981         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27982     },
27983     
27984     /**
27985      * Removes and destroys this button
27986      */
27987     destroy : function(){
27988         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27989         this.td.parentNode.removeChild(this.td);
27990     },
27991     
27992     /**
27993      * Shows this button
27994      */
27995     show: function(){
27996         this.hidden = false;
27997         this.td.style.display = "";
27998     },
27999     
28000     /**
28001      * Hides this button
28002      */
28003     hide: function(){
28004         this.hidden = true;
28005         this.td.style.display = "none";
28006     }
28007 });
28008
28009 // backwards compat
28010 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28011  * Based on:
28012  * Ext JS Library 1.1.1
28013  * Copyright(c) 2006-2007, Ext JS, LLC.
28014  *
28015  * Originally Released Under LGPL - original licence link has changed is not relivant.
28016  *
28017  * Fork - LGPL
28018  * <script type="text/javascript">
28019  */
28020  
28021 /**
28022  * @class Roo.PagingToolbar
28023  * @extends Roo.Toolbar
28024  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28025  * @constructor
28026  * Create a new PagingToolbar
28027  * @param {Object} config The config object
28028  */
28029 Roo.PagingToolbar = function(el, ds, config)
28030 {
28031     // old args format still supported... - xtype is prefered..
28032     if (typeof(el) == 'object' && el.xtype) {
28033         // created from xtype...
28034         config = el;
28035         ds = el.dataSource;
28036         el = config.container;
28037     }
28038     var items = [];
28039     if (config.items) {
28040         items = config.items;
28041         config.items = [];
28042     }
28043     
28044     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28045     this.ds = ds;
28046     this.cursor = 0;
28047     this.renderButtons(this.el);
28048     this.bind(ds);
28049     
28050     // supprot items array.
28051    
28052     Roo.each(items, function(e) {
28053         this.add(Roo.factory(e));
28054     },this);
28055     
28056 };
28057
28058 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28059     /**
28060      * @cfg {Roo.data.Store} dataSource
28061      * The underlying data store providing the paged data
28062      */
28063     /**
28064      * @cfg {String/HTMLElement/Element} container
28065      * container The id or element that will contain the toolbar
28066      */
28067     /**
28068      * @cfg {Boolean} displayInfo
28069      * True to display the displayMsg (defaults to false)
28070      */
28071     /**
28072      * @cfg {Number} pageSize
28073      * The number of records to display per page (defaults to 20)
28074      */
28075     pageSize: 20,
28076     /**
28077      * @cfg {String} displayMsg
28078      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28079      */
28080     displayMsg : 'Displaying {0} - {1} of {2}',
28081     /**
28082      * @cfg {String} emptyMsg
28083      * The message to display when no records are found (defaults to "No data to display")
28084      */
28085     emptyMsg : 'No data to display',
28086     /**
28087      * Customizable piece of the default paging text (defaults to "Page")
28088      * @type String
28089      */
28090     beforePageText : "Page",
28091     /**
28092      * Customizable piece of the default paging text (defaults to "of %0")
28093      * @type String
28094      */
28095     afterPageText : "of {0}",
28096     /**
28097      * Customizable piece of the default paging text (defaults to "First Page")
28098      * @type String
28099      */
28100     firstText : "First Page",
28101     /**
28102      * Customizable piece of the default paging text (defaults to "Previous Page")
28103      * @type String
28104      */
28105     prevText : "Previous Page",
28106     /**
28107      * Customizable piece of the default paging text (defaults to "Next Page")
28108      * @type String
28109      */
28110     nextText : "Next Page",
28111     /**
28112      * Customizable piece of the default paging text (defaults to "Last Page")
28113      * @type String
28114      */
28115     lastText : "Last Page",
28116     /**
28117      * Customizable piece of the default paging text (defaults to "Refresh")
28118      * @type String
28119      */
28120     refreshText : "Refresh",
28121
28122     // private
28123     renderButtons : function(el){
28124         Roo.PagingToolbar.superclass.render.call(this, el);
28125         this.first = this.addButton({
28126             tooltip: this.firstText,
28127             cls: "x-btn-icon x-grid-page-first",
28128             disabled: true,
28129             handler: this.onClick.createDelegate(this, ["first"])
28130         });
28131         this.prev = this.addButton({
28132             tooltip: this.prevText,
28133             cls: "x-btn-icon x-grid-page-prev",
28134             disabled: true,
28135             handler: this.onClick.createDelegate(this, ["prev"])
28136         });
28137         //this.addSeparator();
28138         this.add(this.beforePageText);
28139         this.field = Roo.get(this.addDom({
28140            tag: "input",
28141            type: "text",
28142            size: "3",
28143            value: "1",
28144            cls: "x-grid-page-number"
28145         }).el);
28146         this.field.on("keydown", this.onPagingKeydown, this);
28147         this.field.on("focus", function(){this.dom.select();});
28148         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28149         this.field.setHeight(18);
28150         //this.addSeparator();
28151         this.next = this.addButton({
28152             tooltip: this.nextText,
28153             cls: "x-btn-icon x-grid-page-next",
28154             disabled: true,
28155             handler: this.onClick.createDelegate(this, ["next"])
28156         });
28157         this.last = this.addButton({
28158             tooltip: this.lastText,
28159             cls: "x-btn-icon x-grid-page-last",
28160             disabled: true,
28161             handler: this.onClick.createDelegate(this, ["last"])
28162         });
28163         //this.addSeparator();
28164         this.loading = this.addButton({
28165             tooltip: this.refreshText,
28166             cls: "x-btn-icon x-grid-loading",
28167             handler: this.onClick.createDelegate(this, ["refresh"])
28168         });
28169
28170         if(this.displayInfo){
28171             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28172         }
28173     },
28174
28175     // private
28176     updateInfo : function(){
28177         if(this.displayEl){
28178             var count = this.ds.getCount();
28179             var msg = count == 0 ?
28180                 this.emptyMsg :
28181                 String.format(
28182                     this.displayMsg,
28183                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28184                 );
28185             this.displayEl.update(msg);
28186         }
28187     },
28188
28189     // private
28190     onLoad : function(ds, r, o){
28191        this.cursor = o.params ? o.params.start : 0;
28192        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28193
28194        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28195        this.field.dom.value = ap;
28196        this.first.setDisabled(ap == 1);
28197        this.prev.setDisabled(ap == 1);
28198        this.next.setDisabled(ap == ps);
28199        this.last.setDisabled(ap == ps);
28200        this.loading.enable();
28201        this.updateInfo();
28202     },
28203
28204     // private
28205     getPageData : function(){
28206         var total = this.ds.getTotalCount();
28207         return {
28208             total : total,
28209             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28210             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28211         };
28212     },
28213
28214     // private
28215     onLoadError : function(){
28216         this.loading.enable();
28217     },
28218
28219     // private
28220     onPagingKeydown : function(e){
28221         var k = e.getKey();
28222         var d = this.getPageData();
28223         if(k == e.RETURN){
28224             var v = this.field.dom.value, pageNum;
28225             if(!v || isNaN(pageNum = parseInt(v, 10))){
28226                 this.field.dom.value = d.activePage;
28227                 return;
28228             }
28229             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28230             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28231             e.stopEvent();
28232         }
28233         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))
28234         {
28235           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28236           this.field.dom.value = pageNum;
28237           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28238           e.stopEvent();
28239         }
28240         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28241         {
28242           var v = this.field.dom.value, pageNum; 
28243           var increment = (e.shiftKey) ? 10 : 1;
28244           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28245             increment *= -1;
28246           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28247             this.field.dom.value = d.activePage;
28248             return;
28249           }
28250           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28251           {
28252             this.field.dom.value = parseInt(v, 10) + increment;
28253             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28254             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28255           }
28256           e.stopEvent();
28257         }
28258     },
28259
28260     // private
28261     beforeLoad : function(){
28262         if(this.loading){
28263             this.loading.disable();
28264         }
28265     },
28266
28267     // private
28268     onClick : function(which){
28269         var ds = this.ds;
28270         switch(which){
28271             case "first":
28272                 ds.load({params:{start: 0, limit: this.pageSize}});
28273             break;
28274             case "prev":
28275                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28276             break;
28277             case "next":
28278                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28279             break;
28280             case "last":
28281                 var total = ds.getTotalCount();
28282                 var extra = total % this.pageSize;
28283                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28284                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28285             break;
28286             case "refresh":
28287                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28288             break;
28289         }
28290     },
28291
28292     /**
28293      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28294      * @param {Roo.data.Store} store The data store to unbind
28295      */
28296     unbind : function(ds){
28297         ds.un("beforeload", this.beforeLoad, this);
28298         ds.un("load", this.onLoad, this);
28299         ds.un("loadexception", this.onLoadError, this);
28300         ds.un("remove", this.updateInfo, this);
28301         ds.un("add", this.updateInfo, this);
28302         this.ds = undefined;
28303     },
28304
28305     /**
28306      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28307      * @param {Roo.data.Store} store The data store to bind
28308      */
28309     bind : function(ds){
28310         ds.on("beforeload", this.beforeLoad, this);
28311         ds.on("load", this.onLoad, this);
28312         ds.on("loadexception", this.onLoadError, this);
28313         ds.on("remove", this.updateInfo, this);
28314         ds.on("add", this.updateInfo, this);
28315         this.ds = ds;
28316     }
28317 });/*
28318  * Based on:
28319  * Ext JS Library 1.1.1
28320  * Copyright(c) 2006-2007, Ext JS, LLC.
28321  *
28322  * Originally Released Under LGPL - original licence link has changed is not relivant.
28323  *
28324  * Fork - LGPL
28325  * <script type="text/javascript">
28326  */
28327
28328 /**
28329  * @class Roo.Resizable
28330  * @extends Roo.util.Observable
28331  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28332  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28333  * 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
28334  * the element will be wrapped for you automatically.</p>
28335  * <p>Here is the list of valid resize handles:</p>
28336  * <pre>
28337 Value   Description
28338 ------  -------------------
28339  'n'     north
28340  's'     south
28341  'e'     east
28342  'w'     west
28343  'nw'    northwest
28344  'sw'    southwest
28345  'se'    southeast
28346  'ne'    northeast
28347  'hd'    horizontal drag
28348  'all'   all
28349 </pre>
28350  * <p>Here's an example showing the creation of a typical Resizable:</p>
28351  * <pre><code>
28352 var resizer = new Roo.Resizable("element-id", {
28353     handles: 'all',
28354     minWidth: 200,
28355     minHeight: 100,
28356     maxWidth: 500,
28357     maxHeight: 400,
28358     pinned: true
28359 });
28360 resizer.on("resize", myHandler);
28361 </code></pre>
28362  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28363  * resizer.east.setDisplayed(false);</p>
28364  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28365  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28366  * resize operation's new size (defaults to [0, 0])
28367  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28368  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28369  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28370  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28371  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28372  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28373  * @cfg {Number} width The width of the element in pixels (defaults to null)
28374  * @cfg {Number} height The height of the element in pixels (defaults to null)
28375  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28376  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28377  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28378  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28379  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28380  * in favor of the handles config option (defaults to false)
28381  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28382  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28383  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28384  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28385  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28386  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28387  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28388  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28389  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
28390  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
28391  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
28392  * @constructor
28393  * Create a new resizable component
28394  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
28395  * @param {Object} config configuration options
28396   */
28397 Roo.Resizable = function(el, config)
28398 {
28399     this.el = Roo.get(el);
28400
28401     if(config && config.wrap){
28402         config.resizeChild = this.el;
28403         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
28404         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
28405         this.el.setStyle("overflow", "hidden");
28406         this.el.setPositioning(config.resizeChild.getPositioning());
28407         config.resizeChild.clearPositioning();
28408         if(!config.width || !config.height){
28409             var csize = config.resizeChild.getSize();
28410             this.el.setSize(csize.width, csize.height);
28411         }
28412         if(config.pinned && !config.adjustments){
28413             config.adjustments = "auto";
28414         }
28415     }
28416
28417     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
28418     this.proxy.unselectable();
28419     this.proxy.enableDisplayMode('block');
28420
28421     Roo.apply(this, config);
28422
28423     if(this.pinned){
28424         this.disableTrackOver = true;
28425         this.el.addClass("x-resizable-pinned");
28426     }
28427     // if the element isn't positioned, make it relative
28428     var position = this.el.getStyle("position");
28429     if(position != "absolute" && position != "fixed"){
28430         this.el.setStyle("position", "relative");
28431     }
28432     if(!this.handles){ // no handles passed, must be legacy style
28433         this.handles = 's,e,se';
28434         if(this.multiDirectional){
28435             this.handles += ',n,w';
28436         }
28437     }
28438     if(this.handles == "all"){
28439         this.handles = "n s e w ne nw se sw";
28440     }
28441     var hs = this.handles.split(/\s*?[,;]\s*?| /);
28442     var ps = Roo.Resizable.positions;
28443     for(var i = 0, len = hs.length; i < len; i++){
28444         if(hs[i] && ps[hs[i]]){
28445             var pos = ps[hs[i]];
28446             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
28447         }
28448     }
28449     // legacy
28450     this.corner = this.southeast;
28451     
28452     // updateBox = the box can move..
28453     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
28454         this.updateBox = true;
28455     }
28456
28457     this.activeHandle = null;
28458
28459     if(this.resizeChild){
28460         if(typeof this.resizeChild == "boolean"){
28461             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
28462         }else{
28463             this.resizeChild = Roo.get(this.resizeChild, true);
28464         }
28465     }
28466     
28467     if(this.adjustments == "auto"){
28468         var rc = this.resizeChild;
28469         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
28470         if(rc && (hw || hn)){
28471             rc.position("relative");
28472             rc.setLeft(hw ? hw.el.getWidth() : 0);
28473             rc.setTop(hn ? hn.el.getHeight() : 0);
28474         }
28475         this.adjustments = [
28476             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
28477             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
28478         ];
28479     }
28480
28481     if(this.draggable){
28482         this.dd = this.dynamic ?
28483             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
28484         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
28485     }
28486
28487     // public events
28488     this.addEvents({
28489         /**
28490          * @event beforeresize
28491          * Fired before resize is allowed. Set enabled to false to cancel resize.
28492          * @param {Roo.Resizable} this
28493          * @param {Roo.EventObject} e The mousedown event
28494          */
28495         "beforeresize" : true,
28496         /**
28497          * @event resize
28498          * Fired after a resize.
28499          * @param {Roo.Resizable} this
28500          * @param {Number} width The new width
28501          * @param {Number} height The new height
28502          * @param {Roo.EventObject} e The mouseup event
28503          */
28504         "resize" : true
28505     });
28506
28507     if(this.width !== null && this.height !== null){
28508         this.resizeTo(this.width, this.height);
28509     }else{
28510         this.updateChildSize();
28511     }
28512     if(Roo.isIE){
28513         this.el.dom.style.zoom = 1;
28514     }
28515     Roo.Resizable.superclass.constructor.call(this);
28516 };
28517
28518 Roo.extend(Roo.Resizable, Roo.util.Observable, {
28519         resizeChild : false,
28520         adjustments : [0, 0],
28521         minWidth : 5,
28522         minHeight : 5,
28523         maxWidth : 10000,
28524         maxHeight : 10000,
28525         enabled : true,
28526         animate : false,
28527         duration : .35,
28528         dynamic : false,
28529         handles : false,
28530         multiDirectional : false,
28531         disableTrackOver : false,
28532         easing : 'easeOutStrong',
28533         widthIncrement : 0,
28534         heightIncrement : 0,
28535         pinned : false,
28536         width : null,
28537         height : null,
28538         preserveRatio : false,
28539         transparent: false,
28540         minX: 0,
28541         minY: 0,
28542         draggable: false,
28543
28544         /**
28545          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
28546          */
28547         constrainTo: undefined,
28548         /**
28549          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
28550          */
28551         resizeRegion: undefined,
28552
28553
28554     /**
28555      * Perform a manual resize
28556      * @param {Number} width
28557      * @param {Number} height
28558      */
28559     resizeTo : function(width, height){
28560         this.el.setSize(width, height);
28561         this.updateChildSize();
28562         this.fireEvent("resize", this, width, height, null);
28563     },
28564
28565     // private
28566     startSizing : function(e, handle){
28567         this.fireEvent("beforeresize", this, e);
28568         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
28569
28570             if(!this.overlay){
28571                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
28572                 this.overlay.unselectable();
28573                 this.overlay.enableDisplayMode("block");
28574                 this.overlay.on("mousemove", this.onMouseMove, this);
28575                 this.overlay.on("mouseup", this.onMouseUp, this);
28576             }
28577             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
28578
28579             this.resizing = true;
28580             this.startBox = this.el.getBox();
28581             this.startPoint = e.getXY();
28582             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
28583                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
28584
28585             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28586             this.overlay.show();
28587
28588             if(this.constrainTo) {
28589                 var ct = Roo.get(this.constrainTo);
28590                 this.resizeRegion = ct.getRegion().adjust(
28591                     ct.getFrameWidth('t'),
28592                     ct.getFrameWidth('l'),
28593                     -ct.getFrameWidth('b'),
28594                     -ct.getFrameWidth('r')
28595                 );
28596             }
28597
28598             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
28599             this.proxy.show();
28600             this.proxy.setBox(this.startBox);
28601             if(!this.dynamic){
28602                 this.proxy.setStyle('visibility', 'visible');
28603             }
28604         }
28605     },
28606
28607     // private
28608     onMouseDown : function(handle, e){
28609         if(this.enabled){
28610             e.stopEvent();
28611             this.activeHandle = handle;
28612             this.startSizing(e, handle);
28613         }
28614     },
28615
28616     // private
28617     onMouseUp : function(e){
28618         var size = this.resizeElement();
28619         this.resizing = false;
28620         this.handleOut();
28621         this.overlay.hide();
28622         this.proxy.hide();
28623         this.fireEvent("resize", this, size.width, size.height, e);
28624     },
28625
28626     // private
28627     updateChildSize : function(){
28628         
28629         if(this.resizeChild){
28630             var el = this.el;
28631             var child = this.resizeChild;
28632             var adj = this.adjustments;
28633             if(el.dom.offsetWidth){
28634                 var b = el.getSize(true);
28635                 child.setSize(b.width+adj[0], b.height+adj[1]);
28636             }
28637             // Second call here for IE
28638             // The first call enables instant resizing and
28639             // the second call corrects scroll bars if they
28640             // exist
28641             if(Roo.isIE){
28642                 setTimeout(function(){
28643                     if(el.dom.offsetWidth){
28644                         var b = el.getSize(true);
28645                         child.setSize(b.width+adj[0], b.height+adj[1]);
28646                     }
28647                 }, 10);
28648             }
28649         }
28650     },
28651
28652     // private
28653     snap : function(value, inc, min){
28654         if(!inc || !value) return value;
28655         var newValue = value;
28656         var m = value % inc;
28657         if(m > 0){
28658             if(m > (inc/2)){
28659                 newValue = value + (inc-m);
28660             }else{
28661                 newValue = value - m;
28662             }
28663         }
28664         return Math.max(min, newValue);
28665     },
28666
28667     // private
28668     resizeElement : function(){
28669         var box = this.proxy.getBox();
28670         if(this.updateBox){
28671             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28672         }else{
28673             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28674         }
28675         this.updateChildSize();
28676         if(!this.dynamic){
28677             this.proxy.hide();
28678         }
28679         return box;
28680     },
28681
28682     // private
28683     constrain : function(v, diff, m, mx){
28684         if(v - diff < m){
28685             diff = v - m;
28686         }else if(v - diff > mx){
28687             diff = mx - v;
28688         }
28689         return diff;
28690     },
28691
28692     // private
28693     onMouseMove : function(e){
28694         if(this.enabled){
28695             try{// try catch so if something goes wrong the user doesn't get hung
28696
28697             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28698                 return;
28699             }
28700
28701             //var curXY = this.startPoint;
28702             var curSize = this.curSize || this.startBox;
28703             var x = this.startBox.x, y = this.startBox.y;
28704             var ox = x, oy = y;
28705             var w = curSize.width, h = curSize.height;
28706             var ow = w, oh = h;
28707             var mw = this.minWidth, mh = this.minHeight;
28708             var mxw = this.maxWidth, mxh = this.maxHeight;
28709             var wi = this.widthIncrement;
28710             var hi = this.heightIncrement;
28711
28712             var eventXY = e.getXY();
28713             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28714             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28715
28716             var pos = this.activeHandle.position;
28717
28718             switch(pos){
28719                 case "east":
28720                     w += diffX;
28721                     w = Math.min(Math.max(mw, w), mxw);
28722                     break;
28723              
28724                 case "south":
28725                     h += diffY;
28726                     h = Math.min(Math.max(mh, h), mxh);
28727                     break;
28728                 case "southeast":
28729                     w += diffX;
28730                     h += diffY;
28731                     w = Math.min(Math.max(mw, w), mxw);
28732                     h = Math.min(Math.max(mh, h), mxh);
28733                     break;
28734                 case "north":
28735                     diffY = this.constrain(h, diffY, mh, mxh);
28736                     y += diffY;
28737                     h -= diffY;
28738                     break;
28739                 case "hdrag":
28740                     
28741                     if (wi) {
28742                         var adiffX = Math.abs(diffX);
28743                         var sub = (adiffX % wi); // how much 
28744                         if (sub > (wi/2)) { // far enough to snap
28745                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28746                         } else {
28747                             // remove difference.. 
28748                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28749                         }
28750                     }
28751                     x += diffX;
28752                     x = Math.max(this.minX, x);
28753                     break;
28754                 case "west":
28755                     diffX = this.constrain(w, diffX, mw, mxw);
28756                     x += diffX;
28757                     w -= diffX;
28758                     break;
28759                 case "northeast":
28760                     w += diffX;
28761                     w = Math.min(Math.max(mw, w), mxw);
28762                     diffY = this.constrain(h, diffY, mh, mxh);
28763                     y += diffY;
28764                     h -= diffY;
28765                     break;
28766                 case "northwest":
28767                     diffX = this.constrain(w, diffX, mw, mxw);
28768                     diffY = this.constrain(h, diffY, mh, mxh);
28769                     y += diffY;
28770                     h -= diffY;
28771                     x += diffX;
28772                     w -= diffX;
28773                     break;
28774                case "southwest":
28775                     diffX = this.constrain(w, diffX, mw, mxw);
28776                     h += diffY;
28777                     h = Math.min(Math.max(mh, h), mxh);
28778                     x += diffX;
28779                     w -= diffX;
28780                     break;
28781             }
28782
28783             var sw = this.snap(w, wi, mw);
28784             var sh = this.snap(h, hi, mh);
28785             if(sw != w || sh != h){
28786                 switch(pos){
28787                     case "northeast":
28788                         y -= sh - h;
28789                     break;
28790                     case "north":
28791                         y -= sh - h;
28792                         break;
28793                     case "southwest":
28794                         x -= sw - w;
28795                     break;
28796                     case "west":
28797                         x -= sw - w;
28798                         break;
28799                     case "northwest":
28800                         x -= sw - w;
28801                         y -= sh - h;
28802                     break;
28803                 }
28804                 w = sw;
28805                 h = sh;
28806             }
28807
28808             if(this.preserveRatio){
28809                 switch(pos){
28810                     case "southeast":
28811                     case "east":
28812                         h = oh * (w/ow);
28813                         h = Math.min(Math.max(mh, h), mxh);
28814                         w = ow * (h/oh);
28815                        break;
28816                     case "south":
28817                         w = ow * (h/oh);
28818                         w = Math.min(Math.max(mw, w), mxw);
28819                         h = oh * (w/ow);
28820                         break;
28821                     case "northeast":
28822                         w = ow * (h/oh);
28823                         w = Math.min(Math.max(mw, w), mxw);
28824                         h = oh * (w/ow);
28825                     break;
28826                     case "north":
28827                         var tw = w;
28828                         w = ow * (h/oh);
28829                         w = Math.min(Math.max(mw, w), mxw);
28830                         h = oh * (w/ow);
28831                         x += (tw - w) / 2;
28832                         break;
28833                     case "southwest":
28834                         h = oh * (w/ow);
28835                         h = Math.min(Math.max(mh, h), mxh);
28836                         var tw = w;
28837                         w = ow * (h/oh);
28838                         x += tw - w;
28839                         break;
28840                     case "west":
28841                         var th = h;
28842                         h = oh * (w/ow);
28843                         h = Math.min(Math.max(mh, h), mxh);
28844                         y += (th - h) / 2;
28845                         var tw = w;
28846                         w = ow * (h/oh);
28847                         x += tw - w;
28848                        break;
28849                     case "northwest":
28850                         var tw = w;
28851                         var th = h;
28852                         h = oh * (w/ow);
28853                         h = Math.min(Math.max(mh, h), mxh);
28854                         w = ow * (h/oh);
28855                         y += th - h;
28856                         x += tw - w;
28857                        break;
28858
28859                 }
28860             }
28861             if (pos == 'hdrag') {
28862                 w = ow;
28863             }
28864             this.proxy.setBounds(x, y, w, h);
28865             if(this.dynamic){
28866                 this.resizeElement();
28867             }
28868             }catch(e){}
28869         }
28870     },
28871
28872     // private
28873     handleOver : function(){
28874         if(this.enabled){
28875             this.el.addClass("x-resizable-over");
28876         }
28877     },
28878
28879     // private
28880     handleOut : function(){
28881         if(!this.resizing){
28882             this.el.removeClass("x-resizable-over");
28883         }
28884     },
28885
28886     /**
28887      * Returns the element this component is bound to.
28888      * @return {Roo.Element}
28889      */
28890     getEl : function(){
28891         return this.el;
28892     },
28893
28894     /**
28895      * Returns the resizeChild element (or null).
28896      * @return {Roo.Element}
28897      */
28898     getResizeChild : function(){
28899         return this.resizeChild;
28900     },
28901     groupHandler : function()
28902     {
28903         
28904     },
28905     /**
28906      * Destroys this resizable. If the element was wrapped and
28907      * removeEl is not true then the element remains.
28908      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28909      */
28910     destroy : function(removeEl){
28911         this.proxy.remove();
28912         if(this.overlay){
28913             this.overlay.removeAllListeners();
28914             this.overlay.remove();
28915         }
28916         var ps = Roo.Resizable.positions;
28917         for(var k in ps){
28918             if(typeof ps[k] != "function" && this[ps[k]]){
28919                 var h = this[ps[k]];
28920                 h.el.removeAllListeners();
28921                 h.el.remove();
28922             }
28923         }
28924         if(removeEl){
28925             this.el.update("");
28926             this.el.remove();
28927         }
28928     }
28929 });
28930
28931 // private
28932 // hash to map config positions to true positions
28933 Roo.Resizable.positions = {
28934     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28935     hd: "hdrag"
28936 };
28937
28938 // private
28939 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28940     if(!this.tpl){
28941         // only initialize the template if resizable is used
28942         var tpl = Roo.DomHelper.createTemplate(
28943             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28944         );
28945         tpl.compile();
28946         Roo.Resizable.Handle.prototype.tpl = tpl;
28947     }
28948     this.position = pos;
28949     this.rz = rz;
28950     // show north drag fro topdra
28951     var handlepos = pos == 'hdrag' ? 'north' : pos;
28952     
28953     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28954     if (pos == 'hdrag') {
28955         this.el.setStyle('cursor', 'pointer');
28956     }
28957     this.el.unselectable();
28958     if(transparent){
28959         this.el.setOpacity(0);
28960     }
28961     this.el.on("mousedown", this.onMouseDown, this);
28962     if(!disableTrackOver){
28963         this.el.on("mouseover", this.onMouseOver, this);
28964         this.el.on("mouseout", this.onMouseOut, this);
28965     }
28966 };
28967
28968 // private
28969 Roo.Resizable.Handle.prototype = {
28970     afterResize : function(rz){
28971         // do nothing
28972     },
28973     // private
28974     onMouseDown : function(e){
28975         this.rz.onMouseDown(this, e);
28976     },
28977     // private
28978     onMouseOver : function(e){
28979         this.rz.handleOver(this, e);
28980     },
28981     // private
28982     onMouseOut : function(e){
28983         this.rz.handleOut(this, e);
28984     }
28985 };/*
28986  * Based on:
28987  * Ext JS Library 1.1.1
28988  * Copyright(c) 2006-2007, Ext JS, LLC.
28989  *
28990  * Originally Released Under LGPL - original licence link has changed is not relivant.
28991  *
28992  * Fork - LGPL
28993  * <script type="text/javascript">
28994  */
28995
28996 /**
28997  * @class Roo.Editor
28998  * @extends Roo.Component
28999  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29000  * @constructor
29001  * Create a new Editor
29002  * @param {Roo.form.Field} field The Field object (or descendant)
29003  * @param {Object} config The config object
29004  */
29005 Roo.Editor = function(field, config){
29006     Roo.Editor.superclass.constructor.call(this, config);
29007     this.field = field;
29008     this.addEvents({
29009         /**
29010              * @event beforestartedit
29011              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29012              * false from the handler of this event.
29013              * @param {Editor} this
29014              * @param {Roo.Element} boundEl The underlying element bound to this editor
29015              * @param {Mixed} value The field value being set
29016              */
29017         "beforestartedit" : true,
29018         /**
29019              * @event startedit
29020              * Fires when this editor is displayed
29021              * @param {Roo.Element} boundEl The underlying element bound to this editor
29022              * @param {Mixed} value The starting field value
29023              */
29024         "startedit" : true,
29025         /**
29026              * @event beforecomplete
29027              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29028              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29029              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29030              * event will not fire since no edit actually occurred.
29031              * @param {Editor} this
29032              * @param {Mixed} value The current field value
29033              * @param {Mixed} startValue The original field value
29034              */
29035         "beforecomplete" : true,
29036         /**
29037              * @event complete
29038              * Fires after editing is complete and any changed value has been written to the underlying field.
29039              * @param {Editor} this
29040              * @param {Mixed} value The current field value
29041              * @param {Mixed} startValue The original field value
29042              */
29043         "complete" : true,
29044         /**
29045          * @event specialkey
29046          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29047          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29048          * @param {Roo.form.Field} this
29049          * @param {Roo.EventObject} e The event object
29050          */
29051         "specialkey" : true
29052     });
29053 };
29054
29055 Roo.extend(Roo.Editor, Roo.Component, {
29056     /**
29057      * @cfg {Boolean/String} autosize
29058      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29059      * or "height" to adopt the height only (defaults to false)
29060      */
29061     /**
29062      * @cfg {Boolean} revertInvalid
29063      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29064      * validation fails (defaults to true)
29065      */
29066     /**
29067      * @cfg {Boolean} ignoreNoChange
29068      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29069      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29070      * will never be ignored.
29071      */
29072     /**
29073      * @cfg {Boolean} hideEl
29074      * False to keep the bound element visible while the editor is displayed (defaults to true)
29075      */
29076     /**
29077      * @cfg {Mixed} value
29078      * The data value of the underlying field (defaults to "")
29079      */
29080     value : "",
29081     /**
29082      * @cfg {String} alignment
29083      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29084      */
29085     alignment: "c-c?",
29086     /**
29087      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29088      * for bottom-right shadow (defaults to "frame")
29089      */
29090     shadow : "frame",
29091     /**
29092      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29093      */
29094     constrain : false,
29095     /**
29096      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29097      */
29098     completeOnEnter : false,
29099     /**
29100      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29101      */
29102     cancelOnEsc : false,
29103     /**
29104      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29105      */
29106     updateEl : false,
29107
29108     // private
29109     onRender : function(ct, position){
29110         this.el = new Roo.Layer({
29111             shadow: this.shadow,
29112             cls: "x-editor",
29113             parentEl : ct,
29114             shim : this.shim,
29115             shadowOffset:4,
29116             id: this.id,
29117             constrain: this.constrain
29118         });
29119         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29120         if(this.field.msgTarget != 'title'){
29121             this.field.msgTarget = 'qtip';
29122         }
29123         this.field.render(this.el);
29124         if(Roo.isGecko){
29125             this.field.el.dom.setAttribute('autocomplete', 'off');
29126         }
29127         this.field.on("specialkey", this.onSpecialKey, this);
29128         if(this.swallowKeys){
29129             this.field.el.swallowEvent(['keydown','keypress']);
29130         }
29131         this.field.show();
29132         this.field.on("blur", this.onBlur, this);
29133         if(this.field.grow){
29134             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29135         }
29136     },
29137
29138     onSpecialKey : function(field, e)
29139     {
29140         //Roo.log('editor onSpecialKey');
29141         if(this.completeOnEnter && e.getKey() == e.ENTER){
29142             e.stopEvent();
29143             this.completeEdit();
29144             return;
29145         }
29146         // do not fire special key otherwise it might hide close the editor...
29147         if(e.getKey() == e.ENTER){    
29148             return;
29149         }
29150         if(this.cancelOnEsc && e.getKey() == e.ESC){
29151             this.cancelEdit();
29152             return;
29153         } 
29154         this.fireEvent('specialkey', field, e);
29155     
29156     },
29157
29158     /**
29159      * Starts the editing process and shows the editor.
29160      * @param {String/HTMLElement/Element} el The element to edit
29161      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29162       * to the innerHTML of el.
29163      */
29164     startEdit : function(el, value){
29165         if(this.editing){
29166             this.completeEdit();
29167         }
29168         this.boundEl = Roo.get(el);
29169         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29170         if(!this.rendered){
29171             this.render(this.parentEl || document.body);
29172         }
29173         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29174             return;
29175         }
29176         this.startValue = v;
29177         this.field.setValue(v);
29178         if(this.autoSize){
29179             var sz = this.boundEl.getSize();
29180             switch(this.autoSize){
29181                 case "width":
29182                 this.setSize(sz.width,  "");
29183                 break;
29184                 case "height":
29185                 this.setSize("",  sz.height);
29186                 break;
29187                 default:
29188                 this.setSize(sz.width,  sz.height);
29189             }
29190         }
29191         this.el.alignTo(this.boundEl, this.alignment);
29192         this.editing = true;
29193         if(Roo.QuickTips){
29194             Roo.QuickTips.disable();
29195         }
29196         this.show();
29197     },
29198
29199     /**
29200      * Sets the height and width of this editor.
29201      * @param {Number} width The new width
29202      * @param {Number} height The new height
29203      */
29204     setSize : function(w, h){
29205         this.field.setSize(w, h);
29206         if(this.el){
29207             this.el.sync();
29208         }
29209     },
29210
29211     /**
29212      * Realigns the editor to the bound field based on the current alignment config value.
29213      */
29214     realign : function(){
29215         this.el.alignTo(this.boundEl, this.alignment);
29216     },
29217
29218     /**
29219      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29220      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29221      */
29222     completeEdit : function(remainVisible){
29223         if(!this.editing){
29224             return;
29225         }
29226         var v = this.getValue();
29227         if(this.revertInvalid !== false && !this.field.isValid()){
29228             v = this.startValue;
29229             this.cancelEdit(true);
29230         }
29231         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29232             this.editing = false;
29233             this.hide();
29234             return;
29235         }
29236         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29237             this.editing = false;
29238             if(this.updateEl && this.boundEl){
29239                 this.boundEl.update(v);
29240             }
29241             if(remainVisible !== true){
29242                 this.hide();
29243             }
29244             this.fireEvent("complete", this, v, this.startValue);
29245         }
29246     },
29247
29248     // private
29249     onShow : function(){
29250         this.el.show();
29251         if(this.hideEl !== false){
29252             this.boundEl.hide();
29253         }
29254         this.field.show();
29255         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29256             this.fixIEFocus = true;
29257             this.deferredFocus.defer(50, this);
29258         }else{
29259             this.field.focus();
29260         }
29261         this.fireEvent("startedit", this.boundEl, this.startValue);
29262     },
29263
29264     deferredFocus : function(){
29265         if(this.editing){
29266             this.field.focus();
29267         }
29268     },
29269
29270     /**
29271      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29272      * reverted to the original starting value.
29273      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29274      * cancel (defaults to false)
29275      */
29276     cancelEdit : function(remainVisible){
29277         if(this.editing){
29278             this.setValue(this.startValue);
29279             if(remainVisible !== true){
29280                 this.hide();
29281             }
29282         }
29283     },
29284
29285     // private
29286     onBlur : function(){
29287         if(this.allowBlur !== true && this.editing){
29288             this.completeEdit();
29289         }
29290     },
29291
29292     // private
29293     onHide : function(){
29294         if(this.editing){
29295             this.completeEdit();
29296             return;
29297         }
29298         this.field.blur();
29299         if(this.field.collapse){
29300             this.field.collapse();
29301         }
29302         this.el.hide();
29303         if(this.hideEl !== false){
29304             this.boundEl.show();
29305         }
29306         if(Roo.QuickTips){
29307             Roo.QuickTips.enable();
29308         }
29309     },
29310
29311     /**
29312      * Sets the data value of the editor
29313      * @param {Mixed} value Any valid value supported by the underlying field
29314      */
29315     setValue : function(v){
29316         this.field.setValue(v);
29317     },
29318
29319     /**
29320      * Gets the data value of the editor
29321      * @return {Mixed} The data value
29322      */
29323     getValue : function(){
29324         return this.field.getValue();
29325     }
29326 });/*
29327  * Based on:
29328  * Ext JS Library 1.1.1
29329  * Copyright(c) 2006-2007, Ext JS, LLC.
29330  *
29331  * Originally Released Under LGPL - original licence link has changed is not relivant.
29332  *
29333  * Fork - LGPL
29334  * <script type="text/javascript">
29335  */
29336  
29337 /**
29338  * @class Roo.BasicDialog
29339  * @extends Roo.util.Observable
29340  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29341  * <pre><code>
29342 var dlg = new Roo.BasicDialog("my-dlg", {
29343     height: 200,
29344     width: 300,
29345     minHeight: 100,
29346     minWidth: 150,
29347     modal: true,
29348     proxyDrag: true,
29349     shadow: true
29350 });
29351 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29352 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29353 dlg.addButton('Cancel', dlg.hide, dlg);
29354 dlg.show();
29355 </code></pre>
29356   <b>A Dialog should always be a direct child of the body element.</b>
29357  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29358  * @cfg {String} title Default text to display in the title bar (defaults to null)
29359  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29360  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29361  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29362  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29363  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29364  * (defaults to null with no animation)
29365  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29366  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29367  * property for valid values (defaults to 'all')
29368  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29369  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29370  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29371  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29372  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29373  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29374  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29375  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
29376  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
29377  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
29378  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
29379  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
29380  * draggable = true (defaults to false)
29381  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
29382  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
29383  * shadow (defaults to false)
29384  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
29385  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
29386  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
29387  * @cfg {Array} buttons Array of buttons
29388  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
29389  * @constructor
29390  * Create a new BasicDialog.
29391  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
29392  * @param {Object} config Configuration options
29393  */
29394 Roo.BasicDialog = function(el, config){
29395     this.el = Roo.get(el);
29396     var dh = Roo.DomHelper;
29397     if(!this.el && config && config.autoCreate){
29398         if(typeof config.autoCreate == "object"){
29399             if(!config.autoCreate.id){
29400                 config.autoCreate.id = el;
29401             }
29402             this.el = dh.append(document.body,
29403                         config.autoCreate, true);
29404         }else{
29405             this.el = dh.append(document.body,
29406                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
29407         }
29408     }
29409     el = this.el;
29410     el.setDisplayed(true);
29411     el.hide = this.hideAction;
29412     this.id = el.id;
29413     el.addClass("x-dlg");
29414
29415     Roo.apply(this, config);
29416
29417     this.proxy = el.createProxy("x-dlg-proxy");
29418     this.proxy.hide = this.hideAction;
29419     this.proxy.setOpacity(.5);
29420     this.proxy.hide();
29421
29422     if(config.width){
29423         el.setWidth(config.width);
29424     }
29425     if(config.height){
29426         el.setHeight(config.height);
29427     }
29428     this.size = el.getSize();
29429     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
29430         this.xy = [config.x,config.y];
29431     }else{
29432         this.xy = el.getCenterXY(true);
29433     }
29434     /** The header element @type Roo.Element */
29435     this.header = el.child("> .x-dlg-hd");
29436     /** The body element @type Roo.Element */
29437     this.body = el.child("> .x-dlg-bd");
29438     /** The footer element @type Roo.Element */
29439     this.footer = el.child("> .x-dlg-ft");
29440
29441     if(!this.header){
29442         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
29443     }
29444     if(!this.body){
29445         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
29446     }
29447
29448     this.header.unselectable();
29449     if(this.title){
29450         this.header.update(this.title);
29451     }
29452     // this element allows the dialog to be focused for keyboard event
29453     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
29454     this.focusEl.swallowEvent("click", true);
29455
29456     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
29457
29458     // wrap the body and footer for special rendering
29459     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
29460     if(this.footer){
29461         this.bwrap.dom.appendChild(this.footer.dom);
29462     }
29463
29464     this.bg = this.el.createChild({
29465         tag: "div", cls:"x-dlg-bg",
29466         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
29467     });
29468     this.centerBg = this.bg.child("div.x-dlg-bg-center");
29469
29470
29471     if(this.autoScroll !== false && !this.autoTabs){
29472         this.body.setStyle("overflow", "auto");
29473     }
29474
29475     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
29476
29477     if(this.closable !== false){
29478         this.el.addClass("x-dlg-closable");
29479         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
29480         this.close.on("click", this.closeClick, this);
29481         this.close.addClassOnOver("x-dlg-close-over");
29482     }
29483     if(this.collapsible !== false){
29484         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
29485         this.collapseBtn.on("click", this.collapseClick, this);
29486         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
29487         this.header.on("dblclick", this.collapseClick, this);
29488     }
29489     if(this.resizable !== false){
29490         this.el.addClass("x-dlg-resizable");
29491         this.resizer = new Roo.Resizable(el, {
29492             minWidth: this.minWidth || 80,
29493             minHeight:this.minHeight || 80,
29494             handles: this.resizeHandles || "all",
29495             pinned: true
29496         });
29497         this.resizer.on("beforeresize", this.beforeResize, this);
29498         this.resizer.on("resize", this.onResize, this);
29499     }
29500     if(this.draggable !== false){
29501         el.addClass("x-dlg-draggable");
29502         if (!this.proxyDrag) {
29503             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
29504         }
29505         else {
29506             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
29507         }
29508         dd.setHandleElId(this.header.id);
29509         dd.endDrag = this.endMove.createDelegate(this);
29510         dd.startDrag = this.startMove.createDelegate(this);
29511         dd.onDrag = this.onDrag.createDelegate(this);
29512         dd.scroll = false;
29513         this.dd = dd;
29514     }
29515     if(this.modal){
29516         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
29517         this.mask.enableDisplayMode("block");
29518         this.mask.hide();
29519         this.el.addClass("x-dlg-modal");
29520     }
29521     if(this.shadow){
29522         this.shadow = new Roo.Shadow({
29523             mode : typeof this.shadow == "string" ? this.shadow : "sides",
29524             offset : this.shadowOffset
29525         });
29526     }else{
29527         this.shadowOffset = 0;
29528     }
29529     if(Roo.useShims && this.shim !== false){
29530         this.shim = this.el.createShim();
29531         this.shim.hide = this.hideAction;
29532         this.shim.hide();
29533     }else{
29534         this.shim = false;
29535     }
29536     if(this.autoTabs){
29537         this.initTabs();
29538     }
29539     if (this.buttons) { 
29540         var bts= this.buttons;
29541         this.buttons = [];
29542         Roo.each(bts, function(b) {
29543             this.addButton(b);
29544         }, this);
29545     }
29546     
29547     
29548     this.addEvents({
29549         /**
29550          * @event keydown
29551          * Fires when a key is pressed
29552          * @param {Roo.BasicDialog} this
29553          * @param {Roo.EventObject} e
29554          */
29555         "keydown" : true,
29556         /**
29557          * @event move
29558          * Fires when this dialog is moved by the user.
29559          * @param {Roo.BasicDialog} this
29560          * @param {Number} x The new page X
29561          * @param {Number} y The new page Y
29562          */
29563         "move" : true,
29564         /**
29565          * @event resize
29566          * Fires when this dialog is resized by the user.
29567          * @param {Roo.BasicDialog} this
29568          * @param {Number} width The new width
29569          * @param {Number} height The new height
29570          */
29571         "resize" : true,
29572         /**
29573          * @event beforehide
29574          * Fires before this dialog is hidden.
29575          * @param {Roo.BasicDialog} this
29576          */
29577         "beforehide" : true,
29578         /**
29579          * @event hide
29580          * Fires when this dialog is hidden.
29581          * @param {Roo.BasicDialog} this
29582          */
29583         "hide" : true,
29584         /**
29585          * @event beforeshow
29586          * Fires before this dialog is shown.
29587          * @param {Roo.BasicDialog} this
29588          */
29589         "beforeshow" : true,
29590         /**
29591          * @event show
29592          * Fires when this dialog is shown.
29593          * @param {Roo.BasicDialog} this
29594          */
29595         "show" : true
29596     });
29597     el.on("keydown", this.onKeyDown, this);
29598     el.on("mousedown", this.toFront, this);
29599     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
29600     this.el.hide();
29601     Roo.DialogManager.register(this);
29602     Roo.BasicDialog.superclass.constructor.call(this);
29603 };
29604
29605 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
29606     shadowOffset: Roo.isIE ? 6 : 5,
29607     minHeight: 80,
29608     minWidth: 200,
29609     minButtonWidth: 75,
29610     defaultButton: null,
29611     buttonAlign: "right",
29612     tabTag: 'div',
29613     firstShow: true,
29614
29615     /**
29616      * Sets the dialog title text
29617      * @param {String} text The title text to display
29618      * @return {Roo.BasicDialog} this
29619      */
29620     setTitle : function(text){
29621         this.header.update(text);
29622         return this;
29623     },
29624
29625     // private
29626     closeClick : function(){
29627         this.hide();
29628     },
29629
29630     // private
29631     collapseClick : function(){
29632         this[this.collapsed ? "expand" : "collapse"]();
29633     },
29634
29635     /**
29636      * Collapses the dialog to its minimized state (only the title bar is visible).
29637      * Equivalent to the user clicking the collapse dialog button.
29638      */
29639     collapse : function(){
29640         if(!this.collapsed){
29641             this.collapsed = true;
29642             this.el.addClass("x-dlg-collapsed");
29643             this.restoreHeight = this.el.getHeight();
29644             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29645         }
29646     },
29647
29648     /**
29649      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29650      * clicking the expand dialog button.
29651      */
29652     expand : function(){
29653         if(this.collapsed){
29654             this.collapsed = false;
29655             this.el.removeClass("x-dlg-collapsed");
29656             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29657         }
29658     },
29659
29660     /**
29661      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29662      * @return {Roo.TabPanel} The tabs component
29663      */
29664     initTabs : function(){
29665         var tabs = this.getTabs();
29666         while(tabs.getTab(0)){
29667             tabs.removeTab(0);
29668         }
29669         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29670             var dom = el.dom;
29671             tabs.addTab(Roo.id(dom), dom.title);
29672             dom.title = "";
29673         });
29674         tabs.activate(0);
29675         return tabs;
29676     },
29677
29678     // private
29679     beforeResize : function(){
29680         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29681     },
29682
29683     // private
29684     onResize : function(){
29685         this.refreshSize();
29686         this.syncBodyHeight();
29687         this.adjustAssets();
29688         this.focus();
29689         this.fireEvent("resize", this, this.size.width, this.size.height);
29690     },
29691
29692     // private
29693     onKeyDown : function(e){
29694         if(this.isVisible()){
29695             this.fireEvent("keydown", this, e);
29696         }
29697     },
29698
29699     /**
29700      * Resizes the dialog.
29701      * @param {Number} width
29702      * @param {Number} height
29703      * @return {Roo.BasicDialog} this
29704      */
29705     resizeTo : function(width, height){
29706         this.el.setSize(width, height);
29707         this.size = {width: width, height: height};
29708         this.syncBodyHeight();
29709         if(this.fixedcenter){
29710             this.center();
29711         }
29712         if(this.isVisible()){
29713             this.constrainXY();
29714             this.adjustAssets();
29715         }
29716         this.fireEvent("resize", this, width, height);
29717         return this;
29718     },
29719
29720
29721     /**
29722      * Resizes the dialog to fit the specified content size.
29723      * @param {Number} width
29724      * @param {Number} height
29725      * @return {Roo.BasicDialog} this
29726      */
29727     setContentSize : function(w, h){
29728         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29729         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29730         //if(!this.el.isBorderBox()){
29731             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29732             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29733         //}
29734         if(this.tabs){
29735             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29736             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29737         }
29738         this.resizeTo(w, h);
29739         return this;
29740     },
29741
29742     /**
29743      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29744      * executed in response to a particular key being pressed while the dialog is active.
29745      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29746      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29747      * @param {Function} fn The function to call
29748      * @param {Object} scope (optional) The scope of the function
29749      * @return {Roo.BasicDialog} this
29750      */
29751     addKeyListener : function(key, fn, scope){
29752         var keyCode, shift, ctrl, alt;
29753         if(typeof key == "object" && !(key instanceof Array)){
29754             keyCode = key["key"];
29755             shift = key["shift"];
29756             ctrl = key["ctrl"];
29757             alt = key["alt"];
29758         }else{
29759             keyCode = key;
29760         }
29761         var handler = function(dlg, e){
29762             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29763                 var k = e.getKey();
29764                 if(keyCode instanceof Array){
29765                     for(var i = 0, len = keyCode.length; i < len; i++){
29766                         if(keyCode[i] == k){
29767                           fn.call(scope || window, dlg, k, e);
29768                           return;
29769                         }
29770                     }
29771                 }else{
29772                     if(k == keyCode){
29773                         fn.call(scope || window, dlg, k, e);
29774                     }
29775                 }
29776             }
29777         };
29778         this.on("keydown", handler);
29779         return this;
29780     },
29781
29782     /**
29783      * Returns the TabPanel component (creates it if it doesn't exist).
29784      * Note: If you wish to simply check for the existence of tabs without creating them,
29785      * check for a null 'tabs' property.
29786      * @return {Roo.TabPanel} The tabs component
29787      */
29788     getTabs : function(){
29789         if(!this.tabs){
29790             this.el.addClass("x-dlg-auto-tabs");
29791             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29792             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29793         }
29794         return this.tabs;
29795     },
29796
29797     /**
29798      * Adds a button to the footer section of the dialog.
29799      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29800      * object or a valid Roo.DomHelper element config
29801      * @param {Function} handler The function called when the button is clicked
29802      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29803      * @return {Roo.Button} The new button
29804      */
29805     addButton : function(config, handler, scope){
29806         var dh = Roo.DomHelper;
29807         if(!this.footer){
29808             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29809         }
29810         if(!this.btnContainer){
29811             var tb = this.footer.createChild({
29812
29813                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29814                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29815             }, null, true);
29816             this.btnContainer = tb.firstChild.firstChild.firstChild;
29817         }
29818         var bconfig = {
29819             handler: handler,
29820             scope: scope,
29821             minWidth: this.minButtonWidth,
29822             hideParent:true
29823         };
29824         if(typeof config == "string"){
29825             bconfig.text = config;
29826         }else{
29827             if(config.tag){
29828                 bconfig.dhconfig = config;
29829             }else{
29830                 Roo.apply(bconfig, config);
29831             }
29832         }
29833         var fc = false;
29834         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29835             bconfig.position = Math.max(0, bconfig.position);
29836             fc = this.btnContainer.childNodes[bconfig.position];
29837         }
29838          
29839         var btn = new Roo.Button(
29840             fc ? 
29841                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29842                 : this.btnContainer.appendChild(document.createElement("td")),
29843             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29844             bconfig
29845         );
29846         this.syncBodyHeight();
29847         if(!this.buttons){
29848             /**
29849              * Array of all the buttons that have been added to this dialog via addButton
29850              * @type Array
29851              */
29852             this.buttons = [];
29853         }
29854         this.buttons.push(btn);
29855         return btn;
29856     },
29857
29858     /**
29859      * Sets the default button to be focused when the dialog is displayed.
29860      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29861      * @return {Roo.BasicDialog} this
29862      */
29863     setDefaultButton : function(btn){
29864         this.defaultButton = btn;
29865         return this;
29866     },
29867
29868     // private
29869     getHeaderFooterHeight : function(safe){
29870         var height = 0;
29871         if(this.header){
29872            height += this.header.getHeight();
29873         }
29874         if(this.footer){
29875            var fm = this.footer.getMargins();
29876             height += (this.footer.getHeight()+fm.top+fm.bottom);
29877         }
29878         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29879         height += this.centerBg.getPadding("tb");
29880         return height;
29881     },
29882
29883     // private
29884     syncBodyHeight : function()
29885     {
29886         var bd = this.body, // the text
29887             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
29888             bw = this.bwrap;
29889         var height = this.size.height - this.getHeaderFooterHeight(false);
29890         bd.setHeight(height-bd.getMargins("tb"));
29891         var hh = this.header.getHeight();
29892         var h = this.size.height-hh;
29893         cb.setHeight(h);
29894         
29895         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29896         bw.setHeight(h-cb.getPadding("tb"));
29897         
29898         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29899         bd.setWidth(bw.getWidth(true));
29900         if(this.tabs){
29901             this.tabs.syncHeight();
29902             if(Roo.isIE){
29903                 this.tabs.el.repaint();
29904             }
29905         }
29906     },
29907
29908     /**
29909      * Restores the previous state of the dialog if Roo.state is configured.
29910      * @return {Roo.BasicDialog} this
29911      */
29912     restoreState : function(){
29913         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29914         if(box && box.width){
29915             this.xy = [box.x, box.y];
29916             this.resizeTo(box.width, box.height);
29917         }
29918         return this;
29919     },
29920
29921     // private
29922     beforeShow : function(){
29923         this.expand();
29924         if(this.fixedcenter){
29925             this.xy = this.el.getCenterXY(true);
29926         }
29927         if(this.modal){
29928             Roo.get(document.body).addClass("x-body-masked");
29929             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29930             this.mask.show();
29931         }
29932         this.constrainXY();
29933     },
29934
29935     // private
29936     animShow : function(){
29937         var b = Roo.get(this.animateTarget).getBox();
29938         this.proxy.setSize(b.width, b.height);
29939         this.proxy.setLocation(b.x, b.y);
29940         this.proxy.show();
29941         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29942                     true, .35, this.showEl.createDelegate(this));
29943     },
29944
29945     /**
29946      * Shows the dialog.
29947      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29948      * @return {Roo.BasicDialog} this
29949      */
29950     show : function(animateTarget){
29951         if (this.fireEvent("beforeshow", this) === false){
29952             return;
29953         }
29954         if(this.syncHeightBeforeShow){
29955             this.syncBodyHeight();
29956         }else if(this.firstShow){
29957             this.firstShow = false;
29958             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29959         }
29960         this.animateTarget = animateTarget || this.animateTarget;
29961         if(!this.el.isVisible()){
29962             this.beforeShow();
29963             if(this.animateTarget && Roo.get(this.animateTarget)){
29964                 this.animShow();
29965             }else{
29966                 this.showEl();
29967             }
29968         }
29969         return this;
29970     },
29971
29972     // private
29973     showEl : function(){
29974         this.proxy.hide();
29975         this.el.setXY(this.xy);
29976         this.el.show();
29977         this.adjustAssets(true);
29978         this.toFront();
29979         this.focus();
29980         // IE peekaboo bug - fix found by Dave Fenwick
29981         if(Roo.isIE){
29982             this.el.repaint();
29983         }
29984         this.fireEvent("show", this);
29985     },
29986
29987     /**
29988      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29989      * dialog itself will receive focus.
29990      */
29991     focus : function(){
29992         if(this.defaultButton){
29993             this.defaultButton.focus();
29994         }else{
29995             this.focusEl.focus();
29996         }
29997     },
29998
29999     // private
30000     constrainXY : function(){
30001         if(this.constraintoviewport !== false){
30002             if(!this.viewSize){
30003                 if(this.container){
30004                     var s = this.container.getSize();
30005                     this.viewSize = [s.width, s.height];
30006                 }else{
30007                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30008                 }
30009             }
30010             var s = Roo.get(this.container||document).getScroll();
30011
30012             var x = this.xy[0], y = this.xy[1];
30013             var w = this.size.width, h = this.size.height;
30014             var vw = this.viewSize[0], vh = this.viewSize[1];
30015             // only move it if it needs it
30016             var moved = false;
30017             // first validate right/bottom
30018             if(x + w > vw+s.left){
30019                 x = vw - w;
30020                 moved = true;
30021             }
30022             if(y + h > vh+s.top){
30023                 y = vh - h;
30024                 moved = true;
30025             }
30026             // then make sure top/left isn't negative
30027             if(x < s.left){
30028                 x = s.left;
30029                 moved = true;
30030             }
30031             if(y < s.top){
30032                 y = s.top;
30033                 moved = true;
30034             }
30035             if(moved){
30036                 // cache xy
30037                 this.xy = [x, y];
30038                 if(this.isVisible()){
30039                     this.el.setLocation(x, y);
30040                     this.adjustAssets();
30041                 }
30042             }
30043         }
30044     },
30045
30046     // private
30047     onDrag : function(){
30048         if(!this.proxyDrag){
30049             this.xy = this.el.getXY();
30050             this.adjustAssets();
30051         }
30052     },
30053
30054     // private
30055     adjustAssets : function(doShow){
30056         var x = this.xy[0], y = this.xy[1];
30057         var w = this.size.width, h = this.size.height;
30058         if(doShow === true){
30059             if(this.shadow){
30060                 this.shadow.show(this.el);
30061             }
30062             if(this.shim){
30063                 this.shim.show();
30064             }
30065         }
30066         if(this.shadow && this.shadow.isVisible()){
30067             this.shadow.show(this.el);
30068         }
30069         if(this.shim && this.shim.isVisible()){
30070             this.shim.setBounds(x, y, w, h);
30071         }
30072     },
30073
30074     // private
30075     adjustViewport : function(w, h){
30076         if(!w || !h){
30077             w = Roo.lib.Dom.getViewWidth();
30078             h = Roo.lib.Dom.getViewHeight();
30079         }
30080         // cache the size
30081         this.viewSize = [w, h];
30082         if(this.modal && this.mask.isVisible()){
30083             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30084             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30085         }
30086         if(this.isVisible()){
30087             this.constrainXY();
30088         }
30089     },
30090
30091     /**
30092      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30093      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30094      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30095      */
30096     destroy : function(removeEl){
30097         if(this.isVisible()){
30098             this.animateTarget = null;
30099             this.hide();
30100         }
30101         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30102         if(this.tabs){
30103             this.tabs.destroy(removeEl);
30104         }
30105         Roo.destroy(
30106              this.shim,
30107              this.proxy,
30108              this.resizer,
30109              this.close,
30110              this.mask
30111         );
30112         if(this.dd){
30113             this.dd.unreg();
30114         }
30115         if(this.buttons){
30116            for(var i = 0, len = this.buttons.length; i < len; i++){
30117                this.buttons[i].destroy();
30118            }
30119         }
30120         this.el.removeAllListeners();
30121         if(removeEl === true){
30122             this.el.update("");
30123             this.el.remove();
30124         }
30125         Roo.DialogManager.unregister(this);
30126     },
30127
30128     // private
30129     startMove : function(){
30130         if(this.proxyDrag){
30131             this.proxy.show();
30132         }
30133         if(this.constraintoviewport !== false){
30134             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30135         }
30136     },
30137
30138     // private
30139     endMove : function(){
30140         if(!this.proxyDrag){
30141             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30142         }else{
30143             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30144             this.proxy.hide();
30145         }
30146         this.refreshSize();
30147         this.adjustAssets();
30148         this.focus();
30149         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30150     },
30151
30152     /**
30153      * Brings this dialog to the front of any other visible dialogs
30154      * @return {Roo.BasicDialog} this
30155      */
30156     toFront : function(){
30157         Roo.DialogManager.bringToFront(this);
30158         return this;
30159     },
30160
30161     /**
30162      * Sends this dialog to the back (under) of any other visible dialogs
30163      * @return {Roo.BasicDialog} this
30164      */
30165     toBack : function(){
30166         Roo.DialogManager.sendToBack(this);
30167         return this;
30168     },
30169
30170     /**
30171      * Centers this dialog in the viewport
30172      * @return {Roo.BasicDialog} this
30173      */
30174     center : function(){
30175         var xy = this.el.getCenterXY(true);
30176         this.moveTo(xy[0], xy[1]);
30177         return this;
30178     },
30179
30180     /**
30181      * Moves the dialog's top-left corner to the specified point
30182      * @param {Number} x
30183      * @param {Number} y
30184      * @return {Roo.BasicDialog} this
30185      */
30186     moveTo : function(x, y){
30187         this.xy = [x,y];
30188         if(this.isVisible()){
30189             this.el.setXY(this.xy);
30190             this.adjustAssets();
30191         }
30192         return this;
30193     },
30194
30195     /**
30196      * Aligns the dialog to the specified element
30197      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30198      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30199      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30200      * @return {Roo.BasicDialog} this
30201      */
30202     alignTo : function(element, position, offsets){
30203         this.xy = this.el.getAlignToXY(element, position, offsets);
30204         if(this.isVisible()){
30205             this.el.setXY(this.xy);
30206             this.adjustAssets();
30207         }
30208         return this;
30209     },
30210
30211     /**
30212      * Anchors an element to another element and realigns it when the window is resized.
30213      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30214      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30215      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30216      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30217      * is a number, it is used as the buffer delay (defaults to 50ms).
30218      * @return {Roo.BasicDialog} this
30219      */
30220     anchorTo : function(el, alignment, offsets, monitorScroll){
30221         var action = function(){
30222             this.alignTo(el, alignment, offsets);
30223         };
30224         Roo.EventManager.onWindowResize(action, this);
30225         var tm = typeof monitorScroll;
30226         if(tm != 'undefined'){
30227             Roo.EventManager.on(window, 'scroll', action, this,
30228                 {buffer: tm == 'number' ? monitorScroll : 50});
30229         }
30230         action.call(this);
30231         return this;
30232     },
30233
30234     /**
30235      * Returns true if the dialog is visible
30236      * @return {Boolean}
30237      */
30238     isVisible : function(){
30239         return this.el.isVisible();
30240     },
30241
30242     // private
30243     animHide : function(callback){
30244         var b = Roo.get(this.animateTarget).getBox();
30245         this.proxy.show();
30246         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30247         this.el.hide();
30248         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30249                     this.hideEl.createDelegate(this, [callback]));
30250     },
30251
30252     /**
30253      * Hides the dialog.
30254      * @param {Function} callback (optional) Function to call when the dialog is hidden
30255      * @return {Roo.BasicDialog} this
30256      */
30257     hide : function(callback){
30258         if (this.fireEvent("beforehide", this) === false){
30259             return;
30260         }
30261         if(this.shadow){
30262             this.shadow.hide();
30263         }
30264         if(this.shim) {
30265           this.shim.hide();
30266         }
30267         // sometimes animateTarget seems to get set.. causing problems...
30268         // this just double checks..
30269         if(this.animateTarget && Roo.get(this.animateTarget)) {
30270            this.animHide(callback);
30271         }else{
30272             this.el.hide();
30273             this.hideEl(callback);
30274         }
30275         return this;
30276     },
30277
30278     // private
30279     hideEl : function(callback){
30280         this.proxy.hide();
30281         if(this.modal){
30282             this.mask.hide();
30283             Roo.get(document.body).removeClass("x-body-masked");
30284         }
30285         this.fireEvent("hide", this);
30286         if(typeof callback == "function"){
30287             callback();
30288         }
30289     },
30290
30291     // private
30292     hideAction : function(){
30293         this.setLeft("-10000px");
30294         this.setTop("-10000px");
30295         this.setStyle("visibility", "hidden");
30296     },
30297
30298     // private
30299     refreshSize : function(){
30300         this.size = this.el.getSize();
30301         this.xy = this.el.getXY();
30302         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30303     },
30304
30305     // private
30306     // z-index is managed by the DialogManager and may be overwritten at any time
30307     setZIndex : function(index){
30308         if(this.modal){
30309             this.mask.setStyle("z-index", index);
30310         }
30311         if(this.shim){
30312             this.shim.setStyle("z-index", ++index);
30313         }
30314         if(this.shadow){
30315             this.shadow.setZIndex(++index);
30316         }
30317         this.el.setStyle("z-index", ++index);
30318         if(this.proxy){
30319             this.proxy.setStyle("z-index", ++index);
30320         }
30321         if(this.resizer){
30322             this.resizer.proxy.setStyle("z-index", ++index);
30323         }
30324
30325         this.lastZIndex = index;
30326     },
30327
30328     /**
30329      * Returns the element for this dialog
30330      * @return {Roo.Element} The underlying dialog Element
30331      */
30332     getEl : function(){
30333         return this.el;
30334     }
30335 });
30336
30337 /**
30338  * @class Roo.DialogManager
30339  * Provides global access to BasicDialogs that have been created and
30340  * support for z-indexing (layering) multiple open dialogs.
30341  */
30342 Roo.DialogManager = function(){
30343     var list = {};
30344     var accessList = [];
30345     var front = null;
30346
30347     // private
30348     var sortDialogs = function(d1, d2){
30349         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30350     };
30351
30352     // private
30353     var orderDialogs = function(){
30354         accessList.sort(sortDialogs);
30355         var seed = Roo.DialogManager.zseed;
30356         for(var i = 0, len = accessList.length; i < len; i++){
30357             var dlg = accessList[i];
30358             if(dlg){
30359                 dlg.setZIndex(seed + (i*10));
30360             }
30361         }
30362     };
30363
30364     return {
30365         /**
30366          * The starting z-index for BasicDialogs (defaults to 9000)
30367          * @type Number The z-index value
30368          */
30369         zseed : 9000,
30370
30371         // private
30372         register : function(dlg){
30373             list[dlg.id] = dlg;
30374             accessList.push(dlg);
30375         },
30376
30377         // private
30378         unregister : function(dlg){
30379             delete list[dlg.id];
30380             var i=0;
30381             var len=0;
30382             if(!accessList.indexOf){
30383                 for(  i = 0, len = accessList.length; i < len; i++){
30384                     if(accessList[i] == dlg){
30385                         accessList.splice(i, 1);
30386                         return;
30387                     }
30388                 }
30389             }else{
30390                  i = accessList.indexOf(dlg);
30391                 if(i != -1){
30392                     accessList.splice(i, 1);
30393                 }
30394             }
30395         },
30396
30397         /**
30398          * Gets a registered dialog by id
30399          * @param {String/Object} id The id of the dialog or a dialog
30400          * @return {Roo.BasicDialog} this
30401          */
30402         get : function(id){
30403             return typeof id == "object" ? id : list[id];
30404         },
30405
30406         /**
30407          * Brings the specified dialog to the front
30408          * @param {String/Object} dlg The id of the dialog or a dialog
30409          * @return {Roo.BasicDialog} this
30410          */
30411         bringToFront : function(dlg){
30412             dlg = this.get(dlg);
30413             if(dlg != front){
30414                 front = dlg;
30415                 dlg._lastAccess = new Date().getTime();
30416                 orderDialogs();
30417             }
30418             return dlg;
30419         },
30420
30421         /**
30422          * Sends the specified dialog to the back
30423          * @param {String/Object} dlg The id of the dialog or a dialog
30424          * @return {Roo.BasicDialog} this
30425          */
30426         sendToBack : function(dlg){
30427             dlg = this.get(dlg);
30428             dlg._lastAccess = -(new Date().getTime());
30429             orderDialogs();
30430             return dlg;
30431         },
30432
30433         /**
30434          * Hides all dialogs
30435          */
30436         hideAll : function(){
30437             for(var id in list){
30438                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
30439                     list[id].hide();
30440                 }
30441             }
30442         }
30443     };
30444 }();
30445
30446 /**
30447  * @class Roo.LayoutDialog
30448  * @extends Roo.BasicDialog
30449  * Dialog which provides adjustments for working with a layout in a Dialog.
30450  * Add your necessary layout config options to the dialog's config.<br>
30451  * Example usage (including a nested layout):
30452  * <pre><code>
30453 if(!dialog){
30454     dialog = new Roo.LayoutDialog("download-dlg", {
30455         modal: true,
30456         width:600,
30457         height:450,
30458         shadow:true,
30459         minWidth:500,
30460         minHeight:350,
30461         autoTabs:true,
30462         proxyDrag:true,
30463         // layout config merges with the dialog config
30464         center:{
30465             tabPosition: "top",
30466             alwaysShowTabs: true
30467         }
30468     });
30469     dialog.addKeyListener(27, dialog.hide, dialog);
30470     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
30471     dialog.addButton("Build It!", this.getDownload, this);
30472
30473     // we can even add nested layouts
30474     var innerLayout = new Roo.BorderLayout("dl-inner", {
30475         east: {
30476             initialSize: 200,
30477             autoScroll:true,
30478             split:true
30479         },
30480         center: {
30481             autoScroll:true
30482         }
30483     });
30484     innerLayout.beginUpdate();
30485     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
30486     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
30487     innerLayout.endUpdate(true);
30488
30489     var layout = dialog.getLayout();
30490     layout.beginUpdate();
30491     layout.add("center", new Roo.ContentPanel("standard-panel",
30492                         {title: "Download the Source", fitToFrame:true}));
30493     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
30494                {title: "Build your own roo.js"}));
30495     layout.getRegion("center").showPanel(sp);
30496     layout.endUpdate();
30497 }
30498 </code></pre>
30499     * @constructor
30500     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
30501     * @param {Object} config configuration options
30502   */
30503 Roo.LayoutDialog = function(el, cfg){
30504     
30505     var config=  cfg;
30506     if (typeof(cfg) == 'undefined') {
30507         config = Roo.apply({}, el);
30508         // not sure why we use documentElement here.. - it should always be body.
30509         // IE7 borks horribly if we use documentElement.
30510         // webkit also does not like documentElement - it creates a body element...
30511         el = Roo.get( document.body || document.documentElement ).createChild();
30512         //config.autoCreate = true;
30513     }
30514     
30515     
30516     config.autoTabs = false;
30517     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
30518     this.body.setStyle({overflow:"hidden", position:"relative"});
30519     this.layout = new Roo.BorderLayout(this.body.dom, config);
30520     this.layout.monitorWindowResize = false;
30521     this.el.addClass("x-dlg-auto-layout");
30522     // fix case when center region overwrites center function
30523     this.center = Roo.BasicDialog.prototype.center;
30524     this.on("show", this.layout.layout, this.layout, true);
30525     if (config.items) {
30526         var xitems = config.items;
30527         delete config.items;
30528         Roo.each(xitems, this.addxtype, this);
30529     }
30530     
30531     
30532 };
30533 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
30534     /**
30535      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
30536      * @deprecated
30537      */
30538     endUpdate : function(){
30539         this.layout.endUpdate();
30540     },
30541
30542     /**
30543      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
30544      *  @deprecated
30545      */
30546     beginUpdate : function(){
30547         this.layout.beginUpdate();
30548     },
30549
30550     /**
30551      * Get the BorderLayout for this dialog
30552      * @return {Roo.BorderLayout}
30553      */
30554     getLayout : function(){
30555         return this.layout;
30556     },
30557
30558     showEl : function(){
30559         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
30560         if(Roo.isIE7){
30561             this.layout.layout();
30562         }
30563     },
30564
30565     // private
30566     // Use the syncHeightBeforeShow config option to control this automatically
30567     syncBodyHeight : function(){
30568         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
30569         if(this.layout){this.layout.layout();}
30570     },
30571     
30572       /**
30573      * Add an xtype element (actually adds to the layout.)
30574      * @return {Object} xdata xtype object data.
30575      */
30576     
30577     addxtype : function(c) {
30578         return this.layout.addxtype(c);
30579     }
30580 });/*
30581  * Based on:
30582  * Ext JS Library 1.1.1
30583  * Copyright(c) 2006-2007, Ext JS, LLC.
30584  *
30585  * Originally Released Under LGPL - original licence link has changed is not relivant.
30586  *
30587  * Fork - LGPL
30588  * <script type="text/javascript">
30589  */
30590  
30591 /**
30592  * @class Roo.MessageBox
30593  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
30594  * Example usage:
30595  *<pre><code>
30596 // Basic alert:
30597 Roo.Msg.alert('Status', 'Changes saved successfully.');
30598
30599 // Prompt for user data:
30600 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
30601     if (btn == 'ok'){
30602         // process text value...
30603     }
30604 });
30605
30606 // Show a dialog using config options:
30607 Roo.Msg.show({
30608    title:'Save Changes?',
30609    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
30610    buttons: Roo.Msg.YESNOCANCEL,
30611    fn: processResult,
30612    animEl: 'elId'
30613 });
30614 </code></pre>
30615  * @singleton
30616  */
30617 Roo.MessageBox = function(){
30618     var dlg, opt, mask, waitTimer;
30619     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30620     var buttons, activeTextEl, bwidth;
30621
30622     // private
30623     var handleButton = function(button){
30624         dlg.hide();
30625         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30626     };
30627
30628     // private
30629     var handleHide = function(){
30630         if(opt && opt.cls){
30631             dlg.el.removeClass(opt.cls);
30632         }
30633         if(waitTimer){
30634             Roo.TaskMgr.stop(waitTimer);
30635             waitTimer = null;
30636         }
30637     };
30638
30639     // private
30640     var updateButtons = function(b){
30641         var width = 0;
30642         if(!b){
30643             buttons["ok"].hide();
30644             buttons["cancel"].hide();
30645             buttons["yes"].hide();
30646             buttons["no"].hide();
30647             dlg.footer.dom.style.display = 'none';
30648             return width;
30649         }
30650         dlg.footer.dom.style.display = '';
30651         for(var k in buttons){
30652             if(typeof buttons[k] != "function"){
30653                 if(b[k]){
30654                     buttons[k].show();
30655                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30656                     width += buttons[k].el.getWidth()+15;
30657                 }else{
30658                     buttons[k].hide();
30659                 }
30660             }
30661         }
30662         return width;
30663     };
30664
30665     // private
30666     var handleEsc = function(d, k, e){
30667         if(opt && opt.closable !== false){
30668             dlg.hide();
30669         }
30670         if(e){
30671             e.stopEvent();
30672         }
30673     };
30674
30675     return {
30676         /**
30677          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30678          * @return {Roo.BasicDialog} The BasicDialog element
30679          */
30680         getDialog : function(){
30681            if(!dlg){
30682                 dlg = new Roo.BasicDialog("x-msg-box", {
30683                     autoCreate : true,
30684                     shadow: true,
30685                     draggable: true,
30686                     resizable:false,
30687                     constraintoviewport:false,
30688                     fixedcenter:true,
30689                     collapsible : false,
30690                     shim:true,
30691                     modal: true,
30692                     width:400, height:100,
30693                     buttonAlign:"center",
30694                     closeClick : function(){
30695                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30696                             handleButton("no");
30697                         }else{
30698                             handleButton("cancel");
30699                         }
30700                     }
30701                 });
30702                 dlg.on("hide", handleHide);
30703                 mask = dlg.mask;
30704                 dlg.addKeyListener(27, handleEsc);
30705                 buttons = {};
30706                 var bt = this.buttonText;
30707                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30708                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30709                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30710                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30711                 bodyEl = dlg.body.createChild({
30712
30713                     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>'
30714                 });
30715                 msgEl = bodyEl.dom.firstChild;
30716                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30717                 textboxEl.enableDisplayMode();
30718                 textboxEl.addKeyListener([10,13], function(){
30719                     if(dlg.isVisible() && opt && opt.buttons){
30720                         if(opt.buttons.ok){
30721                             handleButton("ok");
30722                         }else if(opt.buttons.yes){
30723                             handleButton("yes");
30724                         }
30725                     }
30726                 });
30727                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30728                 textareaEl.enableDisplayMode();
30729                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30730                 progressEl.enableDisplayMode();
30731                 var pf = progressEl.dom.firstChild;
30732                 if (pf) {
30733                     pp = Roo.get(pf.firstChild);
30734                     pp.setHeight(pf.offsetHeight);
30735                 }
30736                 
30737             }
30738             return dlg;
30739         },
30740
30741         /**
30742          * Updates the message box body text
30743          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30744          * the XHTML-compliant non-breaking space character '&amp;#160;')
30745          * @return {Roo.MessageBox} This message box
30746          */
30747         updateText : function(text){
30748             if(!dlg.isVisible() && !opt.width){
30749                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30750             }
30751             msgEl.innerHTML = text || '&#160;';
30752       
30753             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
30754             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
30755             var w = Math.max(
30756                     Math.min(opt.width || cw , this.maxWidth), 
30757                     Math.max(opt.minWidth || this.minWidth, bwidth)
30758             );
30759             if(opt.prompt){
30760                 activeTextEl.setWidth(w);
30761             }
30762             if(dlg.isVisible()){
30763                 dlg.fixedcenter = false;
30764             }
30765             // to big, make it scroll. = But as usual stupid IE does not support
30766             // !important..
30767             
30768             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
30769                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
30770                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
30771             } else {
30772                 bodyEl.dom.style.height = '';
30773                 bodyEl.dom.style.overflowY = '';
30774             }
30775             if (cw > w) {
30776                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
30777             } else {
30778                 bodyEl.dom.style.overflowX = '';
30779             }
30780             
30781             dlg.setContentSize(w, bodyEl.getHeight());
30782             if(dlg.isVisible()){
30783                 dlg.fixedcenter = true;
30784             }
30785             return this;
30786         },
30787
30788         /**
30789          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30790          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30791          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30792          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30793          * @return {Roo.MessageBox} This message box
30794          */
30795         updateProgress : function(value, text){
30796             if(text){
30797                 this.updateText(text);
30798             }
30799             if (pp) { // weird bug on my firefox - for some reason this is not defined
30800                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30801             }
30802             return this;
30803         },        
30804
30805         /**
30806          * Returns true if the message box is currently displayed
30807          * @return {Boolean} True if the message box is visible, else false
30808          */
30809         isVisible : function(){
30810             return dlg && dlg.isVisible();  
30811         },
30812
30813         /**
30814          * Hides the message box if it is displayed
30815          */
30816         hide : function(){
30817             if(this.isVisible()){
30818                 dlg.hide();
30819             }  
30820         },
30821
30822         /**
30823          * Displays a new message box, or reinitializes an existing message box, based on the config options
30824          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30825          * The following config object properties are supported:
30826          * <pre>
30827 Property    Type             Description
30828 ----------  ---------------  ------------------------------------------------------------------------------------
30829 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30830                                    closes (defaults to undefined)
30831 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30832                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30833 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30834                                    progress and wait dialogs will ignore this property and always hide the
30835                                    close button as they can only be closed programmatically.
30836 cls               String           A custom CSS class to apply to the message box element
30837 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30838                                    displayed (defaults to 75)
30839 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30840                                    function will be btn (the name of the button that was clicked, if applicable,
30841                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30842                                    Progress and wait dialogs will ignore this option since they do not respond to
30843                                    user actions and can only be closed programmatically, so any required function
30844                                    should be called by the same code after it closes the dialog.
30845 icon              String           A CSS class that provides a background image to be used as an icon for
30846                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30847 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30848 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30849 modal             Boolean          False to allow user interaction with the page while the message box is
30850                                    displayed (defaults to true)
30851 msg               String           A string that will replace the existing message box body text (defaults
30852                                    to the XHTML-compliant non-breaking space character '&#160;')
30853 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30854 progress          Boolean          True to display a progress bar (defaults to false)
30855 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30856 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30857 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30858 title             String           The title text
30859 value             String           The string value to set into the active textbox element if displayed
30860 wait              Boolean          True to display a progress bar (defaults to false)
30861 width             Number           The width of the dialog in pixels
30862 </pre>
30863          *
30864          * Example usage:
30865          * <pre><code>
30866 Roo.Msg.show({
30867    title: 'Address',
30868    msg: 'Please enter your address:',
30869    width: 300,
30870    buttons: Roo.MessageBox.OKCANCEL,
30871    multiline: true,
30872    fn: saveAddress,
30873    animEl: 'addAddressBtn'
30874 });
30875 </code></pre>
30876          * @param {Object} config Configuration options
30877          * @return {Roo.MessageBox} This message box
30878          */
30879         show : function(options)
30880         {
30881             
30882             // this causes nightmares if you show one dialog after another
30883             // especially on callbacks..
30884              
30885             if(this.isVisible()){
30886                 
30887                 this.hide();
30888                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
30889                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
30890                 Roo.log("New Dialog Message:" +  options.msg )
30891                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30892                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30893                 
30894             }
30895             var d = this.getDialog();
30896             opt = options;
30897             d.setTitle(opt.title || "&#160;");
30898             d.close.setDisplayed(opt.closable !== false);
30899             activeTextEl = textboxEl;
30900             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30901             if(opt.prompt){
30902                 if(opt.multiline){
30903                     textboxEl.hide();
30904                     textareaEl.show();
30905                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30906                         opt.multiline : this.defaultTextHeight);
30907                     activeTextEl = textareaEl;
30908                 }else{
30909                     textboxEl.show();
30910                     textareaEl.hide();
30911                 }
30912             }else{
30913                 textboxEl.hide();
30914                 textareaEl.hide();
30915             }
30916             progressEl.setDisplayed(opt.progress === true);
30917             this.updateProgress(0);
30918             activeTextEl.dom.value = opt.value || "";
30919             if(opt.prompt){
30920                 dlg.setDefaultButton(activeTextEl);
30921             }else{
30922                 var bs = opt.buttons;
30923                 var db = null;
30924                 if(bs && bs.ok){
30925                     db = buttons["ok"];
30926                 }else if(bs && bs.yes){
30927                     db = buttons["yes"];
30928                 }
30929                 dlg.setDefaultButton(db);
30930             }
30931             bwidth = updateButtons(opt.buttons);
30932             this.updateText(opt.msg);
30933             if(opt.cls){
30934                 d.el.addClass(opt.cls);
30935             }
30936             d.proxyDrag = opt.proxyDrag === true;
30937             d.modal = opt.modal !== false;
30938             d.mask = opt.modal !== false ? mask : false;
30939             if(!d.isVisible()){
30940                 // force it to the end of the z-index stack so it gets a cursor in FF
30941                 document.body.appendChild(dlg.el.dom);
30942                 d.animateTarget = null;
30943                 d.show(options.animEl);
30944             }
30945             return this;
30946         },
30947
30948         /**
30949          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30950          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30951          * and closing the message box when the process is complete.
30952          * @param {String} title The title bar text
30953          * @param {String} msg The message box body text
30954          * @return {Roo.MessageBox} This message box
30955          */
30956         progress : function(title, msg){
30957             this.show({
30958                 title : title,
30959                 msg : msg,
30960                 buttons: false,
30961                 progress:true,
30962                 closable:false,
30963                 minWidth: this.minProgressWidth,
30964                 modal : true
30965             });
30966             return this;
30967         },
30968
30969         /**
30970          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30971          * If a callback function is passed it will be called after the user clicks the button, and the
30972          * id of the button that was clicked will be passed as the only parameter to the callback
30973          * (could also be the top-right close button).
30974          * @param {String} title The title bar text
30975          * @param {String} msg The message box body text
30976          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30977          * @param {Object} scope (optional) The scope of the callback function
30978          * @return {Roo.MessageBox} This message box
30979          */
30980         alert : function(title, msg, fn, scope){
30981             this.show({
30982                 title : title,
30983                 msg : msg,
30984                 buttons: this.OK,
30985                 fn: fn,
30986                 scope : scope,
30987                 modal : true
30988             });
30989             return this;
30990         },
30991
30992         /**
30993          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30994          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30995          * You are responsible for closing the message box when the process is complete.
30996          * @param {String} msg The message box body text
30997          * @param {String} title (optional) The title bar text
30998          * @return {Roo.MessageBox} This message box
30999          */
31000         wait : function(msg, title){
31001             this.show({
31002                 title : title,
31003                 msg : msg,
31004                 buttons: false,
31005                 closable:false,
31006                 progress:true,
31007                 modal:true,
31008                 width:300,
31009                 wait:true
31010             });
31011             waitTimer = Roo.TaskMgr.start({
31012                 run: function(i){
31013                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31014                 },
31015                 interval: 1000
31016             });
31017             return this;
31018         },
31019
31020         /**
31021          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31022          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31023          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31024          * @param {String} title The title bar text
31025          * @param {String} msg The message box body text
31026          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31027          * @param {Object} scope (optional) The scope of the callback function
31028          * @return {Roo.MessageBox} This message box
31029          */
31030         confirm : function(title, msg, fn, scope){
31031             this.show({
31032                 title : title,
31033                 msg : msg,
31034                 buttons: this.YESNO,
31035                 fn: fn,
31036                 scope : scope,
31037                 modal : true
31038             });
31039             return this;
31040         },
31041
31042         /**
31043          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31044          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31045          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31046          * (could also be the top-right close button) and the text that was entered will be passed as the two
31047          * parameters to the callback.
31048          * @param {String} title The title bar text
31049          * @param {String} msg The message box body text
31050          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31051          * @param {Object} scope (optional) The scope of the callback function
31052          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31053          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31054          * @return {Roo.MessageBox} This message box
31055          */
31056         prompt : function(title, msg, fn, scope, multiline){
31057             this.show({
31058                 title : title,
31059                 msg : msg,
31060                 buttons: this.OKCANCEL,
31061                 fn: fn,
31062                 minWidth:250,
31063                 scope : scope,
31064                 prompt:true,
31065                 multiline: multiline,
31066                 modal : true
31067             });
31068             return this;
31069         },
31070
31071         /**
31072          * Button config that displays a single OK button
31073          * @type Object
31074          */
31075         OK : {ok:true},
31076         /**
31077          * Button config that displays Yes and No buttons
31078          * @type Object
31079          */
31080         YESNO : {yes:true, no:true},
31081         /**
31082          * Button config that displays OK and Cancel buttons
31083          * @type Object
31084          */
31085         OKCANCEL : {ok:true, cancel:true},
31086         /**
31087          * Button config that displays Yes, No and Cancel buttons
31088          * @type Object
31089          */
31090         YESNOCANCEL : {yes:true, no:true, cancel:true},
31091
31092         /**
31093          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31094          * @type Number
31095          */
31096         defaultTextHeight : 75,
31097         /**
31098          * The maximum width in pixels of the message box (defaults to 600)
31099          * @type Number
31100          */
31101         maxWidth : 600,
31102         /**
31103          * The minimum width in pixels of the message box (defaults to 100)
31104          * @type Number
31105          */
31106         minWidth : 100,
31107         /**
31108          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31109          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31110          * @type Number
31111          */
31112         minProgressWidth : 250,
31113         /**
31114          * An object containing the default button text strings that can be overriden for localized language support.
31115          * Supported properties are: ok, cancel, yes and no.
31116          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31117          * @type Object
31118          */
31119         buttonText : {
31120             ok : "OK",
31121             cancel : "Cancel",
31122             yes : "Yes",
31123             no : "No"
31124         }
31125     };
31126 }();
31127
31128 /**
31129  * Shorthand for {@link Roo.MessageBox}
31130  */
31131 Roo.Msg = Roo.MessageBox;/*
31132  * Based on:
31133  * Ext JS Library 1.1.1
31134  * Copyright(c) 2006-2007, Ext JS, LLC.
31135  *
31136  * Originally Released Under LGPL - original licence link has changed is not relivant.
31137  *
31138  * Fork - LGPL
31139  * <script type="text/javascript">
31140  */
31141 /**
31142  * @class Roo.QuickTips
31143  * Provides attractive and customizable tooltips for any element.
31144  * @singleton
31145  */
31146 Roo.QuickTips = function(){
31147     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31148     var ce, bd, xy, dd;
31149     var visible = false, disabled = true, inited = false;
31150     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31151     
31152     var onOver = function(e){
31153         if(disabled){
31154             return;
31155         }
31156         var t = e.getTarget();
31157         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31158             return;
31159         }
31160         if(ce && t == ce.el){
31161             clearTimeout(hideProc);
31162             return;
31163         }
31164         if(t && tagEls[t.id]){
31165             tagEls[t.id].el = t;
31166             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31167             return;
31168         }
31169         var ttp, et = Roo.fly(t);
31170         var ns = cfg.namespace;
31171         if(tm.interceptTitles && t.title){
31172             ttp = t.title;
31173             t.qtip = ttp;
31174             t.removeAttribute("title");
31175             e.preventDefault();
31176         }else{
31177             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31178         }
31179         if(ttp){
31180             showProc = show.defer(tm.showDelay, tm, [{
31181                 el: t, 
31182                 text: ttp, 
31183                 width: et.getAttributeNS(ns, cfg.width),
31184                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31185                 title: et.getAttributeNS(ns, cfg.title),
31186                     cls: et.getAttributeNS(ns, cfg.cls)
31187             }]);
31188         }
31189     };
31190     
31191     var onOut = function(e){
31192         clearTimeout(showProc);
31193         var t = e.getTarget();
31194         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31195             hideProc = setTimeout(hide, tm.hideDelay);
31196         }
31197     };
31198     
31199     var onMove = function(e){
31200         if(disabled){
31201             return;
31202         }
31203         xy = e.getXY();
31204         xy[1] += 18;
31205         if(tm.trackMouse && ce){
31206             el.setXY(xy);
31207         }
31208     };
31209     
31210     var onDown = function(e){
31211         clearTimeout(showProc);
31212         clearTimeout(hideProc);
31213         if(!e.within(el)){
31214             if(tm.hideOnClick){
31215                 hide();
31216                 tm.disable();
31217                 tm.enable.defer(100, tm);
31218             }
31219         }
31220     };
31221     
31222     var getPad = function(){
31223         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31224     };
31225
31226     var show = function(o){
31227         if(disabled){
31228             return;
31229         }
31230         clearTimeout(dismissProc);
31231         ce = o;
31232         if(removeCls){ // in case manually hidden
31233             el.removeClass(removeCls);
31234             removeCls = null;
31235         }
31236         if(ce.cls){
31237             el.addClass(ce.cls);
31238             removeCls = ce.cls;
31239         }
31240         if(ce.title){
31241             tipTitle.update(ce.title);
31242             tipTitle.show();
31243         }else{
31244             tipTitle.update('');
31245             tipTitle.hide();
31246         }
31247         el.dom.style.width  = tm.maxWidth+'px';
31248         //tipBody.dom.style.width = '';
31249         tipBodyText.update(o.text);
31250         var p = getPad(), w = ce.width;
31251         if(!w){
31252             var td = tipBodyText.dom;
31253             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31254             if(aw > tm.maxWidth){
31255                 w = tm.maxWidth;
31256             }else if(aw < tm.minWidth){
31257                 w = tm.minWidth;
31258             }else{
31259                 w = aw;
31260             }
31261         }
31262         //tipBody.setWidth(w);
31263         el.setWidth(parseInt(w, 10) + p);
31264         if(ce.autoHide === false){
31265             close.setDisplayed(true);
31266             if(dd){
31267                 dd.unlock();
31268             }
31269         }else{
31270             close.setDisplayed(false);
31271             if(dd){
31272                 dd.lock();
31273             }
31274         }
31275         if(xy){
31276             el.avoidY = xy[1]-18;
31277             el.setXY(xy);
31278         }
31279         if(tm.animate){
31280             el.setOpacity(.1);
31281             el.setStyle("visibility", "visible");
31282             el.fadeIn({callback: afterShow});
31283         }else{
31284             afterShow();
31285         }
31286     };
31287     
31288     var afterShow = function(){
31289         if(ce){
31290             el.show();
31291             esc.enable();
31292             if(tm.autoDismiss && ce.autoHide !== false){
31293                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31294             }
31295         }
31296     };
31297     
31298     var hide = function(noanim){
31299         clearTimeout(dismissProc);
31300         clearTimeout(hideProc);
31301         ce = null;
31302         if(el.isVisible()){
31303             esc.disable();
31304             if(noanim !== true && tm.animate){
31305                 el.fadeOut({callback: afterHide});
31306             }else{
31307                 afterHide();
31308             } 
31309         }
31310     };
31311     
31312     var afterHide = function(){
31313         el.hide();
31314         if(removeCls){
31315             el.removeClass(removeCls);
31316             removeCls = null;
31317         }
31318     };
31319     
31320     return {
31321         /**
31322         * @cfg {Number} minWidth
31323         * The minimum width of the quick tip (defaults to 40)
31324         */
31325        minWidth : 40,
31326         /**
31327         * @cfg {Number} maxWidth
31328         * The maximum width of the quick tip (defaults to 300)
31329         */
31330        maxWidth : 300,
31331         /**
31332         * @cfg {Boolean} interceptTitles
31333         * True to automatically use the element's DOM title value if available (defaults to false)
31334         */
31335        interceptTitles : false,
31336         /**
31337         * @cfg {Boolean} trackMouse
31338         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31339         */
31340        trackMouse : false,
31341         /**
31342         * @cfg {Boolean} hideOnClick
31343         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31344         */
31345        hideOnClick : true,
31346         /**
31347         * @cfg {Number} showDelay
31348         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31349         */
31350        showDelay : 500,
31351         /**
31352         * @cfg {Number} hideDelay
31353         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31354         */
31355        hideDelay : 200,
31356         /**
31357         * @cfg {Boolean} autoHide
31358         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31359         * Used in conjunction with hideDelay.
31360         */
31361        autoHide : true,
31362         /**
31363         * @cfg {Boolean}
31364         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31365         * (defaults to true).  Used in conjunction with autoDismissDelay.
31366         */
31367        autoDismiss : true,
31368         /**
31369         * @cfg {Number}
31370         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31371         */
31372        autoDismissDelay : 5000,
31373        /**
31374         * @cfg {Boolean} animate
31375         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
31376         */
31377        animate : false,
31378
31379        /**
31380         * @cfg {String} title
31381         * Title text to display (defaults to '').  This can be any valid HTML markup.
31382         */
31383         title: '',
31384        /**
31385         * @cfg {String} text
31386         * Body text to display (defaults to '').  This can be any valid HTML markup.
31387         */
31388         text : '',
31389        /**
31390         * @cfg {String} cls
31391         * A CSS class to apply to the base quick tip element (defaults to '').
31392         */
31393         cls : '',
31394        /**
31395         * @cfg {Number} width
31396         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
31397         * minWidth or maxWidth.
31398         */
31399         width : null,
31400
31401     /**
31402      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
31403      * or display QuickTips in a page.
31404      */
31405        init : function(){
31406           tm = Roo.QuickTips;
31407           cfg = tm.tagConfig;
31408           if(!inited){
31409               if(!Roo.isReady){ // allow calling of init() before onReady
31410                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
31411                   return;
31412               }
31413               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
31414               el.fxDefaults = {stopFx: true};
31415               // maximum custom styling
31416               //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>');
31417               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>');              
31418               tipTitle = el.child('h3');
31419               tipTitle.enableDisplayMode("block");
31420               tipBody = el.child('div.x-tip-bd');
31421               tipBodyText = el.child('div.x-tip-bd-inner');
31422               //bdLeft = el.child('div.x-tip-bd-left');
31423               //bdRight = el.child('div.x-tip-bd-right');
31424               close = el.child('div.x-tip-close');
31425               close.enableDisplayMode("block");
31426               close.on("click", hide);
31427               var d = Roo.get(document);
31428               d.on("mousedown", onDown);
31429               d.on("mouseover", onOver);
31430               d.on("mouseout", onOut);
31431               d.on("mousemove", onMove);
31432               esc = d.addKeyListener(27, hide);
31433               esc.disable();
31434               if(Roo.dd.DD){
31435                   dd = el.initDD("default", null, {
31436                       onDrag : function(){
31437                           el.sync();  
31438                       }
31439                   });
31440                   dd.setHandleElId(tipTitle.id);
31441                   dd.lock();
31442               }
31443               inited = true;
31444           }
31445           this.enable(); 
31446        },
31447
31448     /**
31449      * Configures a new quick tip instance and assigns it to a target element.  The following config options
31450      * are supported:
31451      * <pre>
31452 Property    Type                   Description
31453 ----------  ---------------------  ------------------------------------------------------------------------
31454 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
31455      * </ul>
31456      * @param {Object} config The config object
31457      */
31458        register : function(config){
31459            var cs = config instanceof Array ? config : arguments;
31460            for(var i = 0, len = cs.length; i < len; i++) {
31461                var c = cs[i];
31462                var target = c.target;
31463                if(target){
31464                    if(target instanceof Array){
31465                        for(var j = 0, jlen = target.length; j < jlen; j++){
31466                            tagEls[target[j]] = c;
31467                        }
31468                    }else{
31469                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
31470                    }
31471                }
31472            }
31473        },
31474
31475     /**
31476      * Removes this quick tip from its element and destroys it.
31477      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
31478      */
31479        unregister : function(el){
31480            delete tagEls[Roo.id(el)];
31481        },
31482
31483     /**
31484      * Enable this quick tip.
31485      */
31486        enable : function(){
31487            if(inited && disabled){
31488                locks.pop();
31489                if(locks.length < 1){
31490                    disabled = false;
31491                }
31492            }
31493        },
31494
31495     /**
31496      * Disable this quick tip.
31497      */
31498        disable : function(){
31499           disabled = true;
31500           clearTimeout(showProc);
31501           clearTimeout(hideProc);
31502           clearTimeout(dismissProc);
31503           if(ce){
31504               hide(true);
31505           }
31506           locks.push(1);
31507        },
31508
31509     /**
31510      * Returns true if the quick tip is enabled, else false.
31511      */
31512        isEnabled : function(){
31513             return !disabled;
31514        },
31515
31516         // private
31517        tagConfig : {
31518            namespace : "ext",
31519            attribute : "qtip",
31520            width : "width",
31521            target : "target",
31522            title : "qtitle",
31523            hide : "hide",
31524            cls : "qclass"
31525        }
31526    };
31527 }();
31528
31529 // backwards compat
31530 Roo.QuickTips.tips = Roo.QuickTips.register;/*
31531  * Based on:
31532  * Ext JS Library 1.1.1
31533  * Copyright(c) 2006-2007, Ext JS, LLC.
31534  *
31535  * Originally Released Under LGPL - original licence link has changed is not relivant.
31536  *
31537  * Fork - LGPL
31538  * <script type="text/javascript">
31539  */
31540  
31541
31542 /**
31543  * @class Roo.tree.TreePanel
31544  * @extends Roo.data.Tree
31545
31546  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
31547  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
31548  * @cfg {Boolean} enableDD true to enable drag and drop
31549  * @cfg {Boolean} enableDrag true to enable just drag
31550  * @cfg {Boolean} enableDrop true to enable just drop
31551  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
31552  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
31553  * @cfg {String} ddGroup The DD group this TreePanel belongs to
31554  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
31555  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
31556  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
31557  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
31558  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
31559  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
31560  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
31561  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
31562  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
31563  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
31564  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
31565  * @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>
31566  * @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>
31567  * 
31568  * @constructor
31569  * @param {String/HTMLElement/Element} el The container element
31570  * @param {Object} config
31571  */
31572 Roo.tree.TreePanel = function(el, config){
31573     var root = false;
31574     var loader = false;
31575     if (config.root) {
31576         root = config.root;
31577         delete config.root;
31578     }
31579     if (config.loader) {
31580         loader = config.loader;
31581         delete config.loader;
31582     }
31583     
31584     Roo.apply(this, config);
31585     Roo.tree.TreePanel.superclass.constructor.call(this);
31586     this.el = Roo.get(el);
31587     this.el.addClass('x-tree');
31588     //console.log(root);
31589     if (root) {
31590         this.setRootNode( Roo.factory(root, Roo.tree));
31591     }
31592     if (loader) {
31593         this.loader = Roo.factory(loader, Roo.tree);
31594     }
31595    /**
31596     * Read-only. The id of the container element becomes this TreePanel's id.
31597     */
31598     this.id = this.el.id;
31599     this.addEvents({
31600         /**
31601         * @event beforeload
31602         * Fires before a node is loaded, return false to cancel
31603         * @param {Node} node The node being loaded
31604         */
31605         "beforeload" : true,
31606         /**
31607         * @event load
31608         * Fires when a node is loaded
31609         * @param {Node} node The node that was loaded
31610         */
31611         "load" : true,
31612         /**
31613         * @event textchange
31614         * Fires when the text for a node is changed
31615         * @param {Node} node The node
31616         * @param {String} text The new text
31617         * @param {String} oldText The old text
31618         */
31619         "textchange" : true,
31620         /**
31621         * @event beforeexpand
31622         * Fires before a node is expanded, return false to cancel.
31623         * @param {Node} node The node
31624         * @param {Boolean} deep
31625         * @param {Boolean} anim
31626         */
31627         "beforeexpand" : true,
31628         /**
31629         * @event beforecollapse
31630         * Fires before a node is collapsed, return false to cancel.
31631         * @param {Node} node The node
31632         * @param {Boolean} deep
31633         * @param {Boolean} anim
31634         */
31635         "beforecollapse" : true,
31636         /**
31637         * @event expand
31638         * Fires when a node is expanded
31639         * @param {Node} node The node
31640         */
31641         "expand" : true,
31642         /**
31643         * @event disabledchange
31644         * Fires when the disabled status of a node changes
31645         * @param {Node} node The node
31646         * @param {Boolean} disabled
31647         */
31648         "disabledchange" : true,
31649         /**
31650         * @event collapse
31651         * Fires when a node is collapsed
31652         * @param {Node} node The node
31653         */
31654         "collapse" : true,
31655         /**
31656         * @event beforeclick
31657         * Fires before click processing on a node. Return false to cancel the default action.
31658         * @param {Node} node The node
31659         * @param {Roo.EventObject} e The event object
31660         */
31661         "beforeclick":true,
31662         /**
31663         * @event checkchange
31664         * Fires when a node with a checkbox's checked property changes
31665         * @param {Node} this This node
31666         * @param {Boolean} checked
31667         */
31668         "checkchange":true,
31669         /**
31670         * @event click
31671         * Fires when a node is clicked
31672         * @param {Node} node The node
31673         * @param {Roo.EventObject} e The event object
31674         */
31675         "click":true,
31676         /**
31677         * @event dblclick
31678         * Fires when a node is double clicked
31679         * @param {Node} node The node
31680         * @param {Roo.EventObject} e The event object
31681         */
31682         "dblclick":true,
31683         /**
31684         * @event contextmenu
31685         * Fires when a node is right clicked
31686         * @param {Node} node The node
31687         * @param {Roo.EventObject} e The event object
31688         */
31689         "contextmenu":true,
31690         /**
31691         * @event beforechildrenrendered
31692         * Fires right before the child nodes for a node are rendered
31693         * @param {Node} node The node
31694         */
31695         "beforechildrenrendered":true,
31696         /**
31697         * @event startdrag
31698         * Fires when a node starts being dragged
31699         * @param {Roo.tree.TreePanel} this
31700         * @param {Roo.tree.TreeNode} node
31701         * @param {event} e The raw browser event
31702         */ 
31703        "startdrag" : true,
31704        /**
31705         * @event enddrag
31706         * Fires when a drag operation is complete
31707         * @param {Roo.tree.TreePanel} this
31708         * @param {Roo.tree.TreeNode} node
31709         * @param {event} e The raw browser event
31710         */
31711        "enddrag" : true,
31712        /**
31713         * @event dragdrop
31714         * Fires when a dragged node is dropped on a valid DD target
31715         * @param {Roo.tree.TreePanel} this
31716         * @param {Roo.tree.TreeNode} node
31717         * @param {DD} dd The dd it was dropped on
31718         * @param {event} e The raw browser event
31719         */
31720        "dragdrop" : true,
31721        /**
31722         * @event beforenodedrop
31723         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31724         * passed to handlers has the following properties:<br />
31725         * <ul style="padding:5px;padding-left:16px;">
31726         * <li>tree - The TreePanel</li>
31727         * <li>target - The node being targeted for the drop</li>
31728         * <li>data - The drag data from the drag source</li>
31729         * <li>point - The point of the drop - append, above or below</li>
31730         * <li>source - The drag source</li>
31731         * <li>rawEvent - Raw mouse event</li>
31732         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31733         * to be inserted by setting them on this object.</li>
31734         * <li>cancel - Set this to true to cancel the drop.</li>
31735         * </ul>
31736         * @param {Object} dropEvent
31737         */
31738        "beforenodedrop" : true,
31739        /**
31740         * @event nodedrop
31741         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31742         * passed to handlers has the following properties:<br />
31743         * <ul style="padding:5px;padding-left:16px;">
31744         * <li>tree - The TreePanel</li>
31745         * <li>target - The node being targeted for the drop</li>
31746         * <li>data - The drag data from the drag source</li>
31747         * <li>point - The point of the drop - append, above or below</li>
31748         * <li>source - The drag source</li>
31749         * <li>rawEvent - Raw mouse event</li>
31750         * <li>dropNode - Dropped node(s).</li>
31751         * </ul>
31752         * @param {Object} dropEvent
31753         */
31754        "nodedrop" : true,
31755         /**
31756         * @event nodedragover
31757         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31758         * passed to handlers has the following properties:<br />
31759         * <ul style="padding:5px;padding-left:16px;">
31760         * <li>tree - The TreePanel</li>
31761         * <li>target - The node being targeted for the drop</li>
31762         * <li>data - The drag data from the drag source</li>
31763         * <li>point - The point of the drop - append, above or below</li>
31764         * <li>source - The drag source</li>
31765         * <li>rawEvent - Raw mouse event</li>
31766         * <li>dropNode - Drop node(s) provided by the source.</li>
31767         * <li>cancel - Set this to true to signal drop not allowed.</li>
31768         * </ul>
31769         * @param {Object} dragOverEvent
31770         */
31771        "nodedragover" : true
31772         
31773     });
31774     if(this.singleExpand){
31775        this.on("beforeexpand", this.restrictExpand, this);
31776     }
31777     if (this.editor) {
31778         this.editor.tree = this;
31779         this.editor = Roo.factory(this.editor, Roo.tree);
31780     }
31781     
31782     if (this.selModel) {
31783         this.selModel = Roo.factory(this.selModel, Roo.tree);
31784     }
31785    
31786 };
31787 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31788     rootVisible : true,
31789     animate: Roo.enableFx,
31790     lines : true,
31791     enableDD : false,
31792     hlDrop : Roo.enableFx,
31793   
31794     renderer: false,
31795     
31796     rendererTip: false,
31797     // private
31798     restrictExpand : function(node){
31799         var p = node.parentNode;
31800         if(p){
31801             if(p.expandedChild && p.expandedChild.parentNode == p){
31802                 p.expandedChild.collapse();
31803             }
31804             p.expandedChild = node;
31805         }
31806     },
31807
31808     // private override
31809     setRootNode : function(node){
31810         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31811         if(!this.rootVisible){
31812             node.ui = new Roo.tree.RootTreeNodeUI(node);
31813         }
31814         return node;
31815     },
31816
31817     /**
31818      * Returns the container element for this TreePanel
31819      */
31820     getEl : function(){
31821         return this.el;
31822     },
31823
31824     /**
31825      * Returns the default TreeLoader for this TreePanel
31826      */
31827     getLoader : function(){
31828         return this.loader;
31829     },
31830
31831     /**
31832      * Expand all nodes
31833      */
31834     expandAll : function(){
31835         this.root.expand(true);
31836     },
31837
31838     /**
31839      * Collapse all nodes
31840      */
31841     collapseAll : function(){
31842         this.root.collapse(true);
31843     },
31844
31845     /**
31846      * Returns the selection model used by this TreePanel
31847      */
31848     getSelectionModel : function(){
31849         if(!this.selModel){
31850             this.selModel = new Roo.tree.DefaultSelectionModel();
31851         }
31852         return this.selModel;
31853     },
31854
31855     /**
31856      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31857      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31858      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31859      * @return {Array}
31860      */
31861     getChecked : function(a, startNode){
31862         startNode = startNode || this.root;
31863         var r = [];
31864         var f = function(){
31865             if(this.attributes.checked){
31866                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31867             }
31868         }
31869         startNode.cascade(f);
31870         return r;
31871     },
31872
31873     /**
31874      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31875      * @param {String} path
31876      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31877      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31878      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31879      */
31880     expandPath : function(path, attr, callback){
31881         attr = attr || "id";
31882         var keys = path.split(this.pathSeparator);
31883         var curNode = this.root;
31884         if(curNode.attributes[attr] != keys[1]){ // invalid root
31885             if(callback){
31886                 callback(false, null);
31887             }
31888             return;
31889         }
31890         var index = 1;
31891         var f = function(){
31892             if(++index == keys.length){
31893                 if(callback){
31894                     callback(true, curNode);
31895                 }
31896                 return;
31897             }
31898             var c = curNode.findChild(attr, keys[index]);
31899             if(!c){
31900                 if(callback){
31901                     callback(false, curNode);
31902                 }
31903                 return;
31904             }
31905             curNode = c;
31906             c.expand(false, false, f);
31907         };
31908         curNode.expand(false, false, f);
31909     },
31910
31911     /**
31912      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31913      * @param {String} path
31914      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31915      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31916      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31917      */
31918     selectPath : function(path, attr, callback){
31919         attr = attr || "id";
31920         var keys = path.split(this.pathSeparator);
31921         var v = keys.pop();
31922         if(keys.length > 0){
31923             var f = function(success, node){
31924                 if(success && node){
31925                     var n = node.findChild(attr, v);
31926                     if(n){
31927                         n.select();
31928                         if(callback){
31929                             callback(true, n);
31930                         }
31931                     }else if(callback){
31932                         callback(false, n);
31933                     }
31934                 }else{
31935                     if(callback){
31936                         callback(false, n);
31937                     }
31938                 }
31939             };
31940             this.expandPath(keys.join(this.pathSeparator), attr, f);
31941         }else{
31942             this.root.select();
31943             if(callback){
31944                 callback(true, this.root);
31945             }
31946         }
31947     },
31948
31949     getTreeEl : function(){
31950         return this.el;
31951     },
31952
31953     /**
31954      * Trigger rendering of this TreePanel
31955      */
31956     render : function(){
31957         if (this.innerCt) {
31958             return this; // stop it rendering more than once!!
31959         }
31960         
31961         this.innerCt = this.el.createChild({tag:"ul",
31962                cls:"x-tree-root-ct " +
31963                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31964
31965         if(this.containerScroll){
31966             Roo.dd.ScrollManager.register(this.el);
31967         }
31968         if((this.enableDD || this.enableDrop) && !this.dropZone){
31969            /**
31970             * The dropZone used by this tree if drop is enabled
31971             * @type Roo.tree.TreeDropZone
31972             */
31973              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31974                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31975            });
31976         }
31977         if((this.enableDD || this.enableDrag) && !this.dragZone){
31978            /**
31979             * The dragZone used by this tree if drag is enabled
31980             * @type Roo.tree.TreeDragZone
31981             */
31982             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31983                ddGroup: this.ddGroup || "TreeDD",
31984                scroll: this.ddScroll
31985            });
31986         }
31987         this.getSelectionModel().init(this);
31988         if (!this.root) {
31989             Roo.log("ROOT not set in tree");
31990             return this;
31991         }
31992         this.root.render();
31993         if(!this.rootVisible){
31994             this.root.renderChildren();
31995         }
31996         return this;
31997     }
31998 });/*
31999  * Based on:
32000  * Ext JS Library 1.1.1
32001  * Copyright(c) 2006-2007, Ext JS, LLC.
32002  *
32003  * Originally Released Under LGPL - original licence link has changed is not relivant.
32004  *
32005  * Fork - LGPL
32006  * <script type="text/javascript">
32007  */
32008  
32009
32010 /**
32011  * @class Roo.tree.DefaultSelectionModel
32012  * @extends Roo.util.Observable
32013  * The default single selection for a TreePanel.
32014  * @param {Object} cfg Configuration
32015  */
32016 Roo.tree.DefaultSelectionModel = function(cfg){
32017    this.selNode = null;
32018    
32019    
32020    
32021    this.addEvents({
32022        /**
32023         * @event selectionchange
32024         * Fires when the selected node changes
32025         * @param {DefaultSelectionModel} this
32026         * @param {TreeNode} node the new selection
32027         */
32028        "selectionchange" : true,
32029
32030        /**
32031         * @event beforeselect
32032         * Fires before the selected node changes, return false to cancel the change
32033         * @param {DefaultSelectionModel} this
32034         * @param {TreeNode} node the new selection
32035         * @param {TreeNode} node the old selection
32036         */
32037        "beforeselect" : true
32038    });
32039    
32040     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32041 };
32042
32043 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32044     init : function(tree){
32045         this.tree = tree;
32046         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32047         tree.on("click", this.onNodeClick, this);
32048     },
32049     
32050     onNodeClick : function(node, e){
32051         if (e.ctrlKey && this.selNode == node)  {
32052             this.unselect(node);
32053             return;
32054         }
32055         this.select(node);
32056     },
32057     
32058     /**
32059      * Select a node.
32060      * @param {TreeNode} node The node to select
32061      * @return {TreeNode} The selected node
32062      */
32063     select : function(node){
32064         var last = this.selNode;
32065         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32066             if(last){
32067                 last.ui.onSelectedChange(false);
32068             }
32069             this.selNode = node;
32070             node.ui.onSelectedChange(true);
32071             this.fireEvent("selectionchange", this, node, last);
32072         }
32073         return node;
32074     },
32075     
32076     /**
32077      * Deselect a node.
32078      * @param {TreeNode} node The node to unselect
32079      */
32080     unselect : function(node){
32081         if(this.selNode == node){
32082             this.clearSelections();
32083         }    
32084     },
32085     
32086     /**
32087      * Clear all selections
32088      */
32089     clearSelections : function(){
32090         var n = this.selNode;
32091         if(n){
32092             n.ui.onSelectedChange(false);
32093             this.selNode = null;
32094             this.fireEvent("selectionchange", this, null);
32095         }
32096         return n;
32097     },
32098     
32099     /**
32100      * Get the selected node
32101      * @return {TreeNode} The selected node
32102      */
32103     getSelectedNode : function(){
32104         return this.selNode;    
32105     },
32106     
32107     /**
32108      * Returns true if the node is selected
32109      * @param {TreeNode} node The node to check
32110      * @return {Boolean}
32111      */
32112     isSelected : function(node){
32113         return this.selNode == node;  
32114     },
32115
32116     /**
32117      * Selects the node above the selected node in the tree, intelligently walking the nodes
32118      * @return TreeNode The new selection
32119      */
32120     selectPrevious : function(){
32121         var s = this.selNode || this.lastSelNode;
32122         if(!s){
32123             return null;
32124         }
32125         var ps = s.previousSibling;
32126         if(ps){
32127             if(!ps.isExpanded() || ps.childNodes.length < 1){
32128                 return this.select(ps);
32129             } else{
32130                 var lc = ps.lastChild;
32131                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32132                     lc = lc.lastChild;
32133                 }
32134                 return this.select(lc);
32135             }
32136         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32137             return this.select(s.parentNode);
32138         }
32139         return null;
32140     },
32141
32142     /**
32143      * Selects the node above the selected node in the tree, intelligently walking the nodes
32144      * @return TreeNode The new selection
32145      */
32146     selectNext : function(){
32147         var s = this.selNode || this.lastSelNode;
32148         if(!s){
32149             return null;
32150         }
32151         if(s.firstChild && s.isExpanded()){
32152              return this.select(s.firstChild);
32153          }else if(s.nextSibling){
32154              return this.select(s.nextSibling);
32155          }else if(s.parentNode){
32156             var newS = null;
32157             s.parentNode.bubble(function(){
32158                 if(this.nextSibling){
32159                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32160                     return false;
32161                 }
32162             });
32163             return newS;
32164          }
32165         return null;
32166     },
32167
32168     onKeyDown : function(e){
32169         var s = this.selNode || this.lastSelNode;
32170         // undesirable, but required
32171         var sm = this;
32172         if(!s){
32173             return;
32174         }
32175         var k = e.getKey();
32176         switch(k){
32177              case e.DOWN:
32178                  e.stopEvent();
32179                  this.selectNext();
32180              break;
32181              case e.UP:
32182                  e.stopEvent();
32183                  this.selectPrevious();
32184              break;
32185              case e.RIGHT:
32186                  e.preventDefault();
32187                  if(s.hasChildNodes()){
32188                      if(!s.isExpanded()){
32189                          s.expand();
32190                      }else if(s.firstChild){
32191                          this.select(s.firstChild, e);
32192                      }
32193                  }
32194              break;
32195              case e.LEFT:
32196                  e.preventDefault();
32197                  if(s.hasChildNodes() && s.isExpanded()){
32198                      s.collapse();
32199                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32200                      this.select(s.parentNode, e);
32201                  }
32202              break;
32203         };
32204     }
32205 });
32206
32207 /**
32208  * @class Roo.tree.MultiSelectionModel
32209  * @extends Roo.util.Observable
32210  * Multi selection for a TreePanel.
32211  * @param {Object} cfg Configuration
32212  */
32213 Roo.tree.MultiSelectionModel = function(){
32214    this.selNodes = [];
32215    this.selMap = {};
32216    this.addEvents({
32217        /**
32218         * @event selectionchange
32219         * Fires when the selected nodes change
32220         * @param {MultiSelectionModel} this
32221         * @param {Array} nodes Array of the selected nodes
32222         */
32223        "selectionchange" : true
32224    });
32225    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32226    
32227 };
32228
32229 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32230     init : function(tree){
32231         this.tree = tree;
32232         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32233         tree.on("click", this.onNodeClick, this);
32234     },
32235     
32236     onNodeClick : function(node, e){
32237         this.select(node, e, e.ctrlKey);
32238     },
32239     
32240     /**
32241      * Select a node.
32242      * @param {TreeNode} node The node to select
32243      * @param {EventObject} e (optional) An event associated with the selection
32244      * @param {Boolean} keepExisting True to retain existing selections
32245      * @return {TreeNode} The selected node
32246      */
32247     select : function(node, e, keepExisting){
32248         if(keepExisting !== true){
32249             this.clearSelections(true);
32250         }
32251         if(this.isSelected(node)){
32252             this.lastSelNode = node;
32253             return node;
32254         }
32255         this.selNodes.push(node);
32256         this.selMap[node.id] = node;
32257         this.lastSelNode = node;
32258         node.ui.onSelectedChange(true);
32259         this.fireEvent("selectionchange", this, this.selNodes);
32260         return node;
32261     },
32262     
32263     /**
32264      * Deselect a node.
32265      * @param {TreeNode} node The node to unselect
32266      */
32267     unselect : function(node){
32268         if(this.selMap[node.id]){
32269             node.ui.onSelectedChange(false);
32270             var sn = this.selNodes;
32271             var index = -1;
32272             if(sn.indexOf){
32273                 index = sn.indexOf(node);
32274             }else{
32275                 for(var i = 0, len = sn.length; i < len; i++){
32276                     if(sn[i] == node){
32277                         index = i;
32278                         break;
32279                     }
32280                 }
32281             }
32282             if(index != -1){
32283                 this.selNodes.splice(index, 1);
32284             }
32285             delete this.selMap[node.id];
32286             this.fireEvent("selectionchange", this, this.selNodes);
32287         }
32288     },
32289     
32290     /**
32291      * Clear all selections
32292      */
32293     clearSelections : function(suppressEvent){
32294         var sn = this.selNodes;
32295         if(sn.length > 0){
32296             for(var i = 0, len = sn.length; i < len; i++){
32297                 sn[i].ui.onSelectedChange(false);
32298             }
32299             this.selNodes = [];
32300             this.selMap = {};
32301             if(suppressEvent !== true){
32302                 this.fireEvent("selectionchange", this, this.selNodes);
32303             }
32304         }
32305     },
32306     
32307     /**
32308      * Returns true if the node is selected
32309      * @param {TreeNode} node The node to check
32310      * @return {Boolean}
32311      */
32312     isSelected : function(node){
32313         return this.selMap[node.id] ? true : false;  
32314     },
32315     
32316     /**
32317      * Returns an array of the selected nodes
32318      * @return {Array}
32319      */
32320     getSelectedNodes : function(){
32321         return this.selNodes;    
32322     },
32323
32324     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32325
32326     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32327
32328     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32329 });/*
32330  * Based on:
32331  * Ext JS Library 1.1.1
32332  * Copyright(c) 2006-2007, Ext JS, LLC.
32333  *
32334  * Originally Released Under LGPL - original licence link has changed is not relivant.
32335  *
32336  * Fork - LGPL
32337  * <script type="text/javascript">
32338  */
32339  
32340 /**
32341  * @class Roo.tree.TreeNode
32342  * @extends Roo.data.Node
32343  * @cfg {String} text The text for this node
32344  * @cfg {Boolean} expanded true to start the node expanded
32345  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32346  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32347  * @cfg {Boolean} disabled true to start the node disabled
32348  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32349  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32350  * @cfg {String} cls A css class to be added to the node
32351  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32352  * @cfg {String} href URL of the link used for the node (defaults to #)
32353  * @cfg {String} hrefTarget target frame for the link
32354  * @cfg {String} qtip An Ext QuickTip for the node
32355  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32356  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32357  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32358  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32359  * (defaults to undefined with no checkbox rendered)
32360  * @constructor
32361  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32362  */
32363 Roo.tree.TreeNode = function(attributes){
32364     attributes = attributes || {};
32365     if(typeof attributes == "string"){
32366         attributes = {text: attributes};
32367     }
32368     this.childrenRendered = false;
32369     this.rendered = false;
32370     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32371     this.expanded = attributes.expanded === true;
32372     this.isTarget = attributes.isTarget !== false;
32373     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32374     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32375
32376     /**
32377      * Read-only. The text for this node. To change it use setText().
32378      * @type String
32379      */
32380     this.text = attributes.text;
32381     /**
32382      * True if this node is disabled.
32383      * @type Boolean
32384      */
32385     this.disabled = attributes.disabled === true;
32386
32387     this.addEvents({
32388         /**
32389         * @event textchange
32390         * Fires when the text for this node is changed
32391         * @param {Node} this This node
32392         * @param {String} text The new text
32393         * @param {String} oldText The old text
32394         */
32395         "textchange" : true,
32396         /**
32397         * @event beforeexpand
32398         * Fires before this node is expanded, return false to cancel.
32399         * @param {Node} this This node
32400         * @param {Boolean} deep
32401         * @param {Boolean} anim
32402         */
32403         "beforeexpand" : true,
32404         /**
32405         * @event beforecollapse
32406         * Fires before this node is collapsed, return false to cancel.
32407         * @param {Node} this This node
32408         * @param {Boolean} deep
32409         * @param {Boolean} anim
32410         */
32411         "beforecollapse" : true,
32412         /**
32413         * @event expand
32414         * Fires when this node is expanded
32415         * @param {Node} this This node
32416         */
32417         "expand" : true,
32418         /**
32419         * @event disabledchange
32420         * Fires when the disabled status of this node changes
32421         * @param {Node} this This node
32422         * @param {Boolean} disabled
32423         */
32424         "disabledchange" : true,
32425         /**
32426         * @event collapse
32427         * Fires when this node is collapsed
32428         * @param {Node} this This node
32429         */
32430         "collapse" : true,
32431         /**
32432         * @event beforeclick
32433         * Fires before click processing. Return false to cancel the default action.
32434         * @param {Node} this This node
32435         * @param {Roo.EventObject} e The event object
32436         */
32437         "beforeclick":true,
32438         /**
32439         * @event checkchange
32440         * Fires when a node with a checkbox's checked property changes
32441         * @param {Node} this This node
32442         * @param {Boolean} checked
32443         */
32444         "checkchange":true,
32445         /**
32446         * @event click
32447         * Fires when this node is clicked
32448         * @param {Node} this This node
32449         * @param {Roo.EventObject} e The event object
32450         */
32451         "click":true,
32452         /**
32453         * @event dblclick
32454         * Fires when this node is double clicked
32455         * @param {Node} this This node
32456         * @param {Roo.EventObject} e The event object
32457         */
32458         "dblclick":true,
32459         /**
32460         * @event contextmenu
32461         * Fires when this node is right clicked
32462         * @param {Node} this This node
32463         * @param {Roo.EventObject} e The event object
32464         */
32465         "contextmenu":true,
32466         /**
32467         * @event beforechildrenrendered
32468         * Fires right before the child nodes for this node are rendered
32469         * @param {Node} this This node
32470         */
32471         "beforechildrenrendered":true
32472     });
32473
32474     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
32475
32476     /**
32477      * Read-only. The UI for this node
32478      * @type TreeNodeUI
32479      */
32480     this.ui = new uiClass(this);
32481     
32482     // finally support items[]
32483     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
32484         return;
32485     }
32486     
32487     
32488     Roo.each(this.attributes.items, function(c) {
32489         this.appendChild(Roo.factory(c,Roo.Tree));
32490     }, this);
32491     delete this.attributes.items;
32492     
32493     
32494     
32495 };
32496 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
32497     preventHScroll: true,
32498     /**
32499      * Returns true if this node is expanded
32500      * @return {Boolean}
32501      */
32502     isExpanded : function(){
32503         return this.expanded;
32504     },
32505
32506     /**
32507      * Returns the UI object for this node
32508      * @return {TreeNodeUI}
32509      */
32510     getUI : function(){
32511         return this.ui;
32512     },
32513
32514     // private override
32515     setFirstChild : function(node){
32516         var of = this.firstChild;
32517         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
32518         if(this.childrenRendered && of && node != of){
32519             of.renderIndent(true, true);
32520         }
32521         if(this.rendered){
32522             this.renderIndent(true, true);
32523         }
32524     },
32525
32526     // private override
32527     setLastChild : function(node){
32528         var ol = this.lastChild;
32529         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
32530         if(this.childrenRendered && ol && node != ol){
32531             ol.renderIndent(true, true);
32532         }
32533         if(this.rendered){
32534             this.renderIndent(true, true);
32535         }
32536     },
32537
32538     // these methods are overridden to provide lazy rendering support
32539     // private override
32540     appendChild : function()
32541     {
32542         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
32543         if(node && this.childrenRendered){
32544             node.render();
32545         }
32546         this.ui.updateExpandIcon();
32547         return node;
32548     },
32549
32550     // private override
32551     removeChild : function(node){
32552         this.ownerTree.getSelectionModel().unselect(node);
32553         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
32554         // if it's been rendered remove dom node
32555         if(this.childrenRendered){
32556             node.ui.remove();
32557         }
32558         if(this.childNodes.length < 1){
32559             this.collapse(false, false);
32560         }else{
32561             this.ui.updateExpandIcon();
32562         }
32563         if(!this.firstChild) {
32564             this.childrenRendered = false;
32565         }
32566         return node;
32567     },
32568
32569     // private override
32570     insertBefore : function(node, refNode){
32571         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
32572         if(newNode && refNode && this.childrenRendered){
32573             node.render();
32574         }
32575         this.ui.updateExpandIcon();
32576         return newNode;
32577     },
32578
32579     /**
32580      * Sets the text for this node
32581      * @param {String} text
32582      */
32583     setText : function(text){
32584         var oldText = this.text;
32585         this.text = text;
32586         this.attributes.text = text;
32587         if(this.rendered){ // event without subscribing
32588             this.ui.onTextChange(this, text, oldText);
32589         }
32590         this.fireEvent("textchange", this, text, oldText);
32591     },
32592
32593     /**
32594      * Triggers selection of this node
32595      */
32596     select : function(){
32597         this.getOwnerTree().getSelectionModel().select(this);
32598     },
32599
32600     /**
32601      * Triggers deselection of this node
32602      */
32603     unselect : function(){
32604         this.getOwnerTree().getSelectionModel().unselect(this);
32605     },
32606
32607     /**
32608      * Returns true if this node is selected
32609      * @return {Boolean}
32610      */
32611     isSelected : function(){
32612         return this.getOwnerTree().getSelectionModel().isSelected(this);
32613     },
32614
32615     /**
32616      * Expand this node.
32617      * @param {Boolean} deep (optional) True to expand all children as well
32618      * @param {Boolean} anim (optional) false to cancel the default animation
32619      * @param {Function} callback (optional) A callback to be called when
32620      * expanding this node completes (does not wait for deep expand to complete).
32621      * Called with 1 parameter, this node.
32622      */
32623     expand : function(deep, anim, callback){
32624         if(!this.expanded){
32625             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
32626                 return;
32627             }
32628             if(!this.childrenRendered){
32629                 this.renderChildren();
32630             }
32631             this.expanded = true;
32632             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
32633                 this.ui.animExpand(function(){
32634                     this.fireEvent("expand", this);
32635                     if(typeof callback == "function"){
32636                         callback(this);
32637                     }
32638                     if(deep === true){
32639                         this.expandChildNodes(true);
32640                     }
32641                 }.createDelegate(this));
32642                 return;
32643             }else{
32644                 this.ui.expand();
32645                 this.fireEvent("expand", this);
32646                 if(typeof callback == "function"){
32647                     callback(this);
32648                 }
32649             }
32650         }else{
32651            if(typeof callback == "function"){
32652                callback(this);
32653            }
32654         }
32655         if(deep === true){
32656             this.expandChildNodes(true);
32657         }
32658     },
32659
32660     isHiddenRoot : function(){
32661         return this.isRoot && !this.getOwnerTree().rootVisible;
32662     },
32663
32664     /**
32665      * Collapse this node.
32666      * @param {Boolean} deep (optional) True to collapse all children as well
32667      * @param {Boolean} anim (optional) false to cancel the default animation
32668      */
32669     collapse : function(deep, anim){
32670         if(this.expanded && !this.isHiddenRoot()){
32671             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32672                 return;
32673             }
32674             this.expanded = false;
32675             if((this.getOwnerTree().animate && anim !== false) || anim){
32676                 this.ui.animCollapse(function(){
32677                     this.fireEvent("collapse", this);
32678                     if(deep === true){
32679                         this.collapseChildNodes(true);
32680                     }
32681                 }.createDelegate(this));
32682                 return;
32683             }else{
32684                 this.ui.collapse();
32685                 this.fireEvent("collapse", this);
32686             }
32687         }
32688         if(deep === true){
32689             var cs = this.childNodes;
32690             for(var i = 0, len = cs.length; i < len; i++) {
32691                 cs[i].collapse(true, false);
32692             }
32693         }
32694     },
32695
32696     // private
32697     delayedExpand : function(delay){
32698         if(!this.expandProcId){
32699             this.expandProcId = this.expand.defer(delay, this);
32700         }
32701     },
32702
32703     // private
32704     cancelExpand : function(){
32705         if(this.expandProcId){
32706             clearTimeout(this.expandProcId);
32707         }
32708         this.expandProcId = false;
32709     },
32710
32711     /**
32712      * Toggles expanded/collapsed state of the node
32713      */
32714     toggle : function(){
32715         if(this.expanded){
32716             this.collapse();
32717         }else{
32718             this.expand();
32719         }
32720     },
32721
32722     /**
32723      * Ensures all parent nodes are expanded
32724      */
32725     ensureVisible : function(callback){
32726         var tree = this.getOwnerTree();
32727         tree.expandPath(this.parentNode.getPath(), false, function(){
32728             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32729             Roo.callback(callback);
32730         }.createDelegate(this));
32731     },
32732
32733     /**
32734      * Expand all child nodes
32735      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32736      */
32737     expandChildNodes : function(deep){
32738         var cs = this.childNodes;
32739         for(var i = 0, len = cs.length; i < len; i++) {
32740                 cs[i].expand(deep);
32741         }
32742     },
32743
32744     /**
32745      * Collapse all child nodes
32746      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32747      */
32748     collapseChildNodes : function(deep){
32749         var cs = this.childNodes;
32750         for(var i = 0, len = cs.length; i < len; i++) {
32751                 cs[i].collapse(deep);
32752         }
32753     },
32754
32755     /**
32756      * Disables this node
32757      */
32758     disable : function(){
32759         this.disabled = true;
32760         this.unselect();
32761         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32762             this.ui.onDisableChange(this, true);
32763         }
32764         this.fireEvent("disabledchange", this, true);
32765     },
32766
32767     /**
32768      * Enables this node
32769      */
32770     enable : function(){
32771         this.disabled = false;
32772         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32773             this.ui.onDisableChange(this, false);
32774         }
32775         this.fireEvent("disabledchange", this, false);
32776     },
32777
32778     // private
32779     renderChildren : function(suppressEvent){
32780         if(suppressEvent !== false){
32781             this.fireEvent("beforechildrenrendered", this);
32782         }
32783         var cs = this.childNodes;
32784         for(var i = 0, len = cs.length; i < len; i++){
32785             cs[i].render(true);
32786         }
32787         this.childrenRendered = true;
32788     },
32789
32790     // private
32791     sort : function(fn, scope){
32792         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32793         if(this.childrenRendered){
32794             var cs = this.childNodes;
32795             for(var i = 0, len = cs.length; i < len; i++){
32796                 cs[i].render(true);
32797             }
32798         }
32799     },
32800
32801     // private
32802     render : function(bulkRender){
32803         this.ui.render(bulkRender);
32804         if(!this.rendered){
32805             this.rendered = true;
32806             if(this.expanded){
32807                 this.expanded = false;
32808                 this.expand(false, false);
32809             }
32810         }
32811     },
32812
32813     // private
32814     renderIndent : function(deep, refresh){
32815         if(refresh){
32816             this.ui.childIndent = null;
32817         }
32818         this.ui.renderIndent();
32819         if(deep === true && this.childrenRendered){
32820             var cs = this.childNodes;
32821             for(var i = 0, len = cs.length; i < len; i++){
32822                 cs[i].renderIndent(true, refresh);
32823             }
32824         }
32825     }
32826 });/*
32827  * Based on:
32828  * Ext JS Library 1.1.1
32829  * Copyright(c) 2006-2007, Ext JS, LLC.
32830  *
32831  * Originally Released Under LGPL - original licence link has changed is not relivant.
32832  *
32833  * Fork - LGPL
32834  * <script type="text/javascript">
32835  */
32836  
32837 /**
32838  * @class Roo.tree.AsyncTreeNode
32839  * @extends Roo.tree.TreeNode
32840  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32841  * @constructor
32842  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32843  */
32844  Roo.tree.AsyncTreeNode = function(config){
32845     this.loaded = false;
32846     this.loading = false;
32847     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32848     /**
32849     * @event beforeload
32850     * Fires before this node is loaded, return false to cancel
32851     * @param {Node} this This node
32852     */
32853     this.addEvents({'beforeload':true, 'load': true});
32854     /**
32855     * @event load
32856     * Fires when this node is loaded
32857     * @param {Node} this This node
32858     */
32859     /**
32860      * The loader used by this node (defaults to using the tree's defined loader)
32861      * @type TreeLoader
32862      * @property loader
32863      */
32864 };
32865 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32866     expand : function(deep, anim, callback){
32867         if(this.loading){ // if an async load is already running, waiting til it's done
32868             var timer;
32869             var f = function(){
32870                 if(!this.loading){ // done loading
32871                     clearInterval(timer);
32872                     this.expand(deep, anim, callback);
32873                 }
32874             }.createDelegate(this);
32875             timer = setInterval(f, 200);
32876             return;
32877         }
32878         if(!this.loaded){
32879             if(this.fireEvent("beforeload", this) === false){
32880                 return;
32881             }
32882             this.loading = true;
32883             this.ui.beforeLoad(this);
32884             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32885             if(loader){
32886                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32887                 return;
32888             }
32889         }
32890         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32891     },
32892     
32893     /**
32894      * Returns true if this node is currently loading
32895      * @return {Boolean}
32896      */
32897     isLoading : function(){
32898         return this.loading;  
32899     },
32900     
32901     loadComplete : function(deep, anim, callback){
32902         this.loading = false;
32903         this.loaded = true;
32904         this.ui.afterLoad(this);
32905         this.fireEvent("load", this);
32906         this.expand(deep, anim, callback);
32907     },
32908     
32909     /**
32910      * Returns true if this node has been loaded
32911      * @return {Boolean}
32912      */
32913     isLoaded : function(){
32914         return this.loaded;
32915     },
32916     
32917     hasChildNodes : function(){
32918         if(!this.isLeaf() && !this.loaded){
32919             return true;
32920         }else{
32921             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32922         }
32923     },
32924
32925     /**
32926      * Trigger a reload for this node
32927      * @param {Function} callback
32928      */
32929     reload : function(callback){
32930         this.collapse(false, false);
32931         while(this.firstChild){
32932             this.removeChild(this.firstChild);
32933         }
32934         this.childrenRendered = false;
32935         this.loaded = false;
32936         if(this.isHiddenRoot()){
32937             this.expanded = false;
32938         }
32939         this.expand(false, false, callback);
32940     }
32941 });/*
32942  * Based on:
32943  * Ext JS Library 1.1.1
32944  * Copyright(c) 2006-2007, Ext JS, LLC.
32945  *
32946  * Originally Released Under LGPL - original licence link has changed is not relivant.
32947  *
32948  * Fork - LGPL
32949  * <script type="text/javascript">
32950  */
32951  
32952 /**
32953  * @class Roo.tree.TreeNodeUI
32954  * @constructor
32955  * @param {Object} node The node to render
32956  * The TreeNode UI implementation is separate from the
32957  * tree implementation. Unless you are customizing the tree UI,
32958  * you should never have to use this directly.
32959  */
32960 Roo.tree.TreeNodeUI = function(node){
32961     this.node = node;
32962     this.rendered = false;
32963     this.animating = false;
32964     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32965 };
32966
32967 Roo.tree.TreeNodeUI.prototype = {
32968     removeChild : function(node){
32969         if(this.rendered){
32970             this.ctNode.removeChild(node.ui.getEl());
32971         }
32972     },
32973
32974     beforeLoad : function(){
32975          this.addClass("x-tree-node-loading");
32976     },
32977
32978     afterLoad : function(){
32979          this.removeClass("x-tree-node-loading");
32980     },
32981
32982     onTextChange : function(node, text, oldText){
32983         if(this.rendered){
32984             this.textNode.innerHTML = text;
32985         }
32986     },
32987
32988     onDisableChange : function(node, state){
32989         this.disabled = state;
32990         if(state){
32991             this.addClass("x-tree-node-disabled");
32992         }else{
32993             this.removeClass("x-tree-node-disabled");
32994         }
32995     },
32996
32997     onSelectedChange : function(state){
32998         if(state){
32999             this.focus();
33000             this.addClass("x-tree-selected");
33001         }else{
33002             //this.blur();
33003             this.removeClass("x-tree-selected");
33004         }
33005     },
33006
33007     onMove : function(tree, node, oldParent, newParent, index, refNode){
33008         this.childIndent = null;
33009         if(this.rendered){
33010             var targetNode = newParent.ui.getContainer();
33011             if(!targetNode){//target not rendered
33012                 this.holder = document.createElement("div");
33013                 this.holder.appendChild(this.wrap);
33014                 return;
33015             }
33016             var insertBefore = refNode ? refNode.ui.getEl() : null;
33017             if(insertBefore){
33018                 targetNode.insertBefore(this.wrap, insertBefore);
33019             }else{
33020                 targetNode.appendChild(this.wrap);
33021             }
33022             this.node.renderIndent(true);
33023         }
33024     },
33025
33026     addClass : function(cls){
33027         if(this.elNode){
33028             Roo.fly(this.elNode).addClass(cls);
33029         }
33030     },
33031
33032     removeClass : function(cls){
33033         if(this.elNode){
33034             Roo.fly(this.elNode).removeClass(cls);
33035         }
33036     },
33037
33038     remove : function(){
33039         if(this.rendered){
33040             this.holder = document.createElement("div");
33041             this.holder.appendChild(this.wrap);
33042         }
33043     },
33044
33045     fireEvent : function(){
33046         return this.node.fireEvent.apply(this.node, arguments);
33047     },
33048
33049     initEvents : function(){
33050         this.node.on("move", this.onMove, this);
33051         var E = Roo.EventManager;
33052         var a = this.anchor;
33053
33054         var el = Roo.fly(a, '_treeui');
33055
33056         if(Roo.isOpera){ // opera render bug ignores the CSS
33057             el.setStyle("text-decoration", "none");
33058         }
33059
33060         el.on("click", this.onClick, this);
33061         el.on("dblclick", this.onDblClick, this);
33062
33063         if(this.checkbox){
33064             Roo.EventManager.on(this.checkbox,
33065                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33066         }
33067
33068         el.on("contextmenu", this.onContextMenu, this);
33069
33070         var icon = Roo.fly(this.iconNode);
33071         icon.on("click", this.onClick, this);
33072         icon.on("dblclick", this.onDblClick, this);
33073         icon.on("contextmenu", this.onContextMenu, this);
33074         E.on(this.ecNode, "click", this.ecClick, this, true);
33075
33076         if(this.node.disabled){
33077             this.addClass("x-tree-node-disabled");
33078         }
33079         if(this.node.hidden){
33080             this.addClass("x-tree-node-disabled");
33081         }
33082         var ot = this.node.getOwnerTree();
33083         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33084         if(dd && (!this.node.isRoot || ot.rootVisible)){
33085             Roo.dd.Registry.register(this.elNode, {
33086                 node: this.node,
33087                 handles: this.getDDHandles(),
33088                 isHandle: false
33089             });
33090         }
33091     },
33092
33093     getDDHandles : function(){
33094         return [this.iconNode, this.textNode];
33095     },
33096
33097     hide : function(){
33098         if(this.rendered){
33099             this.wrap.style.display = "none";
33100         }
33101     },
33102
33103     show : function(){
33104         if(this.rendered){
33105             this.wrap.style.display = "";
33106         }
33107     },
33108
33109     onContextMenu : function(e){
33110         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33111             e.preventDefault();
33112             this.focus();
33113             this.fireEvent("contextmenu", this.node, e);
33114         }
33115     },
33116
33117     onClick : function(e){
33118         if(this.dropping){
33119             e.stopEvent();
33120             return;
33121         }
33122         if(this.fireEvent("beforeclick", this.node, e) !== false){
33123             if(!this.disabled && this.node.attributes.href){
33124                 this.fireEvent("click", this.node, e);
33125                 return;
33126             }
33127             e.preventDefault();
33128             if(this.disabled){
33129                 return;
33130             }
33131
33132             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33133                 this.node.toggle();
33134             }
33135
33136             this.fireEvent("click", this.node, e);
33137         }else{
33138             e.stopEvent();
33139         }
33140     },
33141
33142     onDblClick : function(e){
33143         e.preventDefault();
33144         if(this.disabled){
33145             return;
33146         }
33147         if(this.checkbox){
33148             this.toggleCheck();
33149         }
33150         if(!this.animating && this.node.hasChildNodes()){
33151             this.node.toggle();
33152         }
33153         this.fireEvent("dblclick", this.node, e);
33154     },
33155
33156     onCheckChange : function(){
33157         var checked = this.checkbox.checked;
33158         this.node.attributes.checked = checked;
33159         this.fireEvent('checkchange', this.node, checked);
33160     },
33161
33162     ecClick : function(e){
33163         if(!this.animating && this.node.hasChildNodes()){
33164             this.node.toggle();
33165         }
33166     },
33167
33168     startDrop : function(){
33169         this.dropping = true;
33170     },
33171
33172     // delayed drop so the click event doesn't get fired on a drop
33173     endDrop : function(){
33174        setTimeout(function(){
33175            this.dropping = false;
33176        }.createDelegate(this), 50);
33177     },
33178
33179     expand : function(){
33180         this.updateExpandIcon();
33181         this.ctNode.style.display = "";
33182     },
33183
33184     focus : function(){
33185         if(!this.node.preventHScroll){
33186             try{this.anchor.focus();
33187             }catch(e){}
33188         }else if(!Roo.isIE){
33189             try{
33190                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33191                 var l = noscroll.scrollLeft;
33192                 this.anchor.focus();
33193                 noscroll.scrollLeft = l;
33194             }catch(e){}
33195         }
33196     },
33197
33198     toggleCheck : function(value){
33199         var cb = this.checkbox;
33200         if(cb){
33201             cb.checked = (value === undefined ? !cb.checked : value);
33202         }
33203     },
33204
33205     blur : function(){
33206         try{
33207             this.anchor.blur();
33208         }catch(e){}
33209     },
33210
33211     animExpand : function(callback){
33212         var ct = Roo.get(this.ctNode);
33213         ct.stopFx();
33214         if(!this.node.hasChildNodes()){
33215             this.updateExpandIcon();
33216             this.ctNode.style.display = "";
33217             Roo.callback(callback);
33218             return;
33219         }
33220         this.animating = true;
33221         this.updateExpandIcon();
33222
33223         ct.slideIn('t', {
33224            callback : function(){
33225                this.animating = false;
33226                Roo.callback(callback);
33227             },
33228             scope: this,
33229             duration: this.node.ownerTree.duration || .25
33230         });
33231     },
33232
33233     highlight : function(){
33234         var tree = this.node.getOwnerTree();
33235         Roo.fly(this.wrap).highlight(
33236             tree.hlColor || "C3DAF9",
33237             {endColor: tree.hlBaseColor}
33238         );
33239     },
33240
33241     collapse : function(){
33242         this.updateExpandIcon();
33243         this.ctNode.style.display = "none";
33244     },
33245
33246     animCollapse : function(callback){
33247         var ct = Roo.get(this.ctNode);
33248         ct.enableDisplayMode('block');
33249         ct.stopFx();
33250
33251         this.animating = true;
33252         this.updateExpandIcon();
33253
33254         ct.slideOut('t', {
33255             callback : function(){
33256                this.animating = false;
33257                Roo.callback(callback);
33258             },
33259             scope: this,
33260             duration: this.node.ownerTree.duration || .25
33261         });
33262     },
33263
33264     getContainer : function(){
33265         return this.ctNode;
33266     },
33267
33268     getEl : function(){
33269         return this.wrap;
33270     },
33271
33272     appendDDGhost : function(ghostNode){
33273         ghostNode.appendChild(this.elNode.cloneNode(true));
33274     },
33275
33276     getDDRepairXY : function(){
33277         return Roo.lib.Dom.getXY(this.iconNode);
33278     },
33279
33280     onRender : function(){
33281         this.render();
33282     },
33283
33284     render : function(bulkRender){
33285         var n = this.node, a = n.attributes;
33286         var targetNode = n.parentNode ?
33287               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33288
33289         if(!this.rendered){
33290             this.rendered = true;
33291
33292             this.renderElements(n, a, targetNode, bulkRender);
33293
33294             if(a.qtip){
33295                if(this.textNode.setAttributeNS){
33296                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33297                    if(a.qtipTitle){
33298                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33299                    }
33300                }else{
33301                    this.textNode.setAttribute("ext:qtip", a.qtip);
33302                    if(a.qtipTitle){
33303                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33304                    }
33305                }
33306             }else if(a.qtipCfg){
33307                 a.qtipCfg.target = Roo.id(this.textNode);
33308                 Roo.QuickTips.register(a.qtipCfg);
33309             }
33310             this.initEvents();
33311             if(!this.node.expanded){
33312                 this.updateExpandIcon();
33313             }
33314         }else{
33315             if(bulkRender === true) {
33316                 targetNode.appendChild(this.wrap);
33317             }
33318         }
33319     },
33320
33321     renderElements : function(n, a, targetNode, bulkRender)
33322     {
33323         // add some indent caching, this helps performance when rendering a large tree
33324         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33325         var t = n.getOwnerTree();
33326         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33327         if (typeof(n.attributes.html) != 'undefined') {
33328             txt = n.attributes.html;
33329         }
33330         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33331         var cb = typeof a.checked == 'boolean';
33332         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33333         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33334             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33335             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33336             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33337             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33338             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33339              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33340                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33341             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33342             "</li>"];
33343
33344         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33345             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33346                                 n.nextSibling.ui.getEl(), buf.join(""));
33347         }else{
33348             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33349         }
33350
33351         this.elNode = this.wrap.childNodes[0];
33352         this.ctNode = this.wrap.childNodes[1];
33353         var cs = this.elNode.childNodes;
33354         this.indentNode = cs[0];
33355         this.ecNode = cs[1];
33356         this.iconNode = cs[2];
33357         var index = 3;
33358         if(cb){
33359             this.checkbox = cs[3];
33360             index++;
33361         }
33362         this.anchor = cs[index];
33363         this.textNode = cs[index].firstChild;
33364     },
33365
33366     getAnchor : function(){
33367         return this.anchor;
33368     },
33369
33370     getTextEl : function(){
33371         return this.textNode;
33372     },
33373
33374     getIconEl : function(){
33375         return this.iconNode;
33376     },
33377
33378     isChecked : function(){
33379         return this.checkbox ? this.checkbox.checked : false;
33380     },
33381
33382     updateExpandIcon : function(){
33383         if(this.rendered){
33384             var n = this.node, c1, c2;
33385             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
33386             var hasChild = n.hasChildNodes();
33387             if(hasChild){
33388                 if(n.expanded){
33389                     cls += "-minus";
33390                     c1 = "x-tree-node-collapsed";
33391                     c2 = "x-tree-node-expanded";
33392                 }else{
33393                     cls += "-plus";
33394                     c1 = "x-tree-node-expanded";
33395                     c2 = "x-tree-node-collapsed";
33396                 }
33397                 if(this.wasLeaf){
33398                     this.removeClass("x-tree-node-leaf");
33399                     this.wasLeaf = false;
33400                 }
33401                 if(this.c1 != c1 || this.c2 != c2){
33402                     Roo.fly(this.elNode).replaceClass(c1, c2);
33403                     this.c1 = c1; this.c2 = c2;
33404                 }
33405             }else{
33406                 // this changes non-leafs into leafs if they have no children.
33407                 // it's not very rational behaviour..
33408                 
33409                 if(!this.wasLeaf && this.node.leaf){
33410                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
33411                     delete this.c1;
33412                     delete this.c2;
33413                     this.wasLeaf = true;
33414                 }
33415             }
33416             var ecc = "x-tree-ec-icon "+cls;
33417             if(this.ecc != ecc){
33418                 this.ecNode.className = ecc;
33419                 this.ecc = ecc;
33420             }
33421         }
33422     },
33423
33424     getChildIndent : function(){
33425         if(!this.childIndent){
33426             var buf = [];
33427             var p = this.node;
33428             while(p){
33429                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
33430                     if(!p.isLast()) {
33431                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
33432                     } else {
33433                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
33434                     }
33435                 }
33436                 p = p.parentNode;
33437             }
33438             this.childIndent = buf.join("");
33439         }
33440         return this.childIndent;
33441     },
33442
33443     renderIndent : function(){
33444         if(this.rendered){
33445             var indent = "";
33446             var p = this.node.parentNode;
33447             if(p){
33448                 indent = p.ui.getChildIndent();
33449             }
33450             if(this.indentMarkup != indent){ // don't rerender if not required
33451                 this.indentNode.innerHTML = indent;
33452                 this.indentMarkup = indent;
33453             }
33454             this.updateExpandIcon();
33455         }
33456     }
33457 };
33458
33459 Roo.tree.RootTreeNodeUI = function(){
33460     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
33461 };
33462 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
33463     render : function(){
33464         if(!this.rendered){
33465             var targetNode = this.node.ownerTree.innerCt.dom;
33466             this.node.expanded = true;
33467             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
33468             this.wrap = this.ctNode = targetNode.firstChild;
33469         }
33470     },
33471     collapse : function(){
33472     },
33473     expand : function(){
33474     }
33475 });/*
33476  * Based on:
33477  * Ext JS Library 1.1.1
33478  * Copyright(c) 2006-2007, Ext JS, LLC.
33479  *
33480  * Originally Released Under LGPL - original licence link has changed is not relivant.
33481  *
33482  * Fork - LGPL
33483  * <script type="text/javascript">
33484  */
33485 /**
33486  * @class Roo.tree.TreeLoader
33487  * @extends Roo.util.Observable
33488  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
33489  * nodes from a specified URL. The response must be a javascript Array definition
33490  * who's elements are node definition objects. eg:
33491  * <pre><code>
33492 {  success : true,
33493    data :      [
33494    
33495     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
33496     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
33497     ]
33498 }
33499
33500
33501 </code></pre>
33502  * <br><br>
33503  * The old style respose with just an array is still supported, but not recommended.
33504  * <br><br>
33505  *
33506  * A server request is sent, and child nodes are loaded only when a node is expanded.
33507  * The loading node's id is passed to the server under the parameter name "node" to
33508  * enable the server to produce the correct child nodes.
33509  * <br><br>
33510  * To pass extra parameters, an event handler may be attached to the "beforeload"
33511  * event, and the parameters specified in the TreeLoader's baseParams property:
33512  * <pre><code>
33513     myTreeLoader.on("beforeload", function(treeLoader, node) {
33514         this.baseParams.category = node.attributes.category;
33515     }, this);
33516 </code></pre><
33517  * This would pass an HTTP parameter called "category" to the server containing
33518  * the value of the Node's "category" attribute.
33519  * @constructor
33520  * Creates a new Treeloader.
33521  * @param {Object} config A config object containing config properties.
33522  */
33523 Roo.tree.TreeLoader = function(config){
33524     this.baseParams = {};
33525     this.requestMethod = "POST";
33526     Roo.apply(this, config);
33527
33528     this.addEvents({
33529     
33530         /**
33531          * @event beforeload
33532          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
33533          * @param {Object} This TreeLoader object.
33534          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33535          * @param {Object} callback The callback function specified in the {@link #load} call.
33536          */
33537         beforeload : true,
33538         /**
33539          * @event load
33540          * Fires when the node has been successfuly loaded.
33541          * @param {Object} This TreeLoader object.
33542          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33543          * @param {Object} response The response object containing the data from the server.
33544          */
33545         load : true,
33546         /**
33547          * @event loadexception
33548          * Fires if the network request failed.
33549          * @param {Object} This TreeLoader object.
33550          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
33551          * @param {Object} response The response object containing the data from the server.
33552          */
33553         loadexception : true,
33554         /**
33555          * @event create
33556          * Fires before a node is created, enabling you to return custom Node types 
33557          * @param {Object} This TreeLoader object.
33558          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
33559          */
33560         create : true
33561     });
33562
33563     Roo.tree.TreeLoader.superclass.constructor.call(this);
33564 };
33565
33566 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
33567     /**
33568     * @cfg {String} dataUrl The URL from which to request a Json string which
33569     * specifies an array of node definition object representing the child nodes
33570     * to be loaded.
33571     */
33572     /**
33573     * @cfg {String} requestMethod either GET or POST
33574     * defaults to POST (due to BC)
33575     * to be loaded.
33576     */
33577     /**
33578     * @cfg {Object} baseParams (optional) An object containing properties which
33579     * specify HTTP parameters to be passed to each request for child nodes.
33580     */
33581     /**
33582     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
33583     * created by this loader. If the attributes sent by the server have an attribute in this object,
33584     * they take priority.
33585     */
33586     /**
33587     * @cfg {Object} uiProviders (optional) An object containing properties which
33588     * 
33589     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
33590     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
33591     * <i>uiProvider</i> attribute of a returned child node is a string rather
33592     * than a reference to a TreeNodeUI implementation, this that string value
33593     * is used as a property name in the uiProviders object. You can define the provider named
33594     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
33595     */
33596     uiProviders : {},
33597
33598     /**
33599     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
33600     * child nodes before loading.
33601     */
33602     clearOnLoad : true,
33603
33604     /**
33605     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
33606     * property on loading, rather than expecting an array. (eg. more compatible to a standard
33607     * Grid query { data : [ .....] }
33608     */
33609     
33610     root : false,
33611      /**
33612     * @cfg {String} queryParam (optional) 
33613     * Name of the query as it will be passed on the querystring (defaults to 'node')
33614     * eg. the request will be ?node=[id]
33615     */
33616     
33617     
33618     queryParam: false,
33619     
33620     /**
33621      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
33622      * This is called automatically when a node is expanded, but may be used to reload
33623      * a node (or append new children if the {@link #clearOnLoad} option is false.)
33624      * @param {Roo.tree.TreeNode} node
33625      * @param {Function} callback
33626      */
33627     load : function(node, callback){
33628         if(this.clearOnLoad){
33629             while(node.firstChild){
33630                 node.removeChild(node.firstChild);
33631             }
33632         }
33633         if(node.attributes.children){ // preloaded json children
33634             var cs = node.attributes.children;
33635             for(var i = 0, len = cs.length; i < len; i++){
33636                 node.appendChild(this.createNode(cs[i]));
33637             }
33638             if(typeof callback == "function"){
33639                 callback();
33640             }
33641         }else if(this.dataUrl){
33642             this.requestData(node, callback);
33643         }
33644     },
33645
33646     getParams: function(node){
33647         var buf = [], bp = this.baseParams;
33648         for(var key in bp){
33649             if(typeof bp[key] != "function"){
33650                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
33651             }
33652         }
33653         var n = this.queryParam === false ? 'node' : this.queryParam;
33654         buf.push(n + "=", encodeURIComponent(node.id));
33655         return buf.join("");
33656     },
33657
33658     requestData : function(node, callback){
33659         if(this.fireEvent("beforeload", this, node, callback) !== false){
33660             this.transId = Roo.Ajax.request({
33661                 method:this.requestMethod,
33662                 url: this.dataUrl||this.url,
33663                 success: this.handleResponse,
33664                 failure: this.handleFailure,
33665                 scope: this,
33666                 argument: {callback: callback, node: node},
33667                 params: this.getParams(node)
33668             });
33669         }else{
33670             // if the load is cancelled, make sure we notify
33671             // the node that we are done
33672             if(typeof callback == "function"){
33673                 callback();
33674             }
33675         }
33676     },
33677
33678     isLoading : function(){
33679         return this.transId ? true : false;
33680     },
33681
33682     abort : function(){
33683         if(this.isLoading()){
33684             Roo.Ajax.abort(this.transId);
33685         }
33686     },
33687
33688     // private
33689     createNode : function(attr)
33690     {
33691         // apply baseAttrs, nice idea Corey!
33692         if(this.baseAttrs){
33693             Roo.applyIf(attr, this.baseAttrs);
33694         }
33695         if(this.applyLoader !== false){
33696             attr.loader = this;
33697         }
33698         // uiProvider = depreciated..
33699         
33700         if(typeof(attr.uiProvider) == 'string'){
33701            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33702                 /**  eval:var:attr */ eval(attr.uiProvider);
33703         }
33704         if(typeof(this.uiProviders['default']) != 'undefined') {
33705             attr.uiProvider = this.uiProviders['default'];
33706         }
33707         
33708         this.fireEvent('create', this, attr);
33709         
33710         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33711         return(attr.leaf ?
33712                         new Roo.tree.TreeNode(attr) :
33713                         new Roo.tree.AsyncTreeNode(attr));
33714     },
33715
33716     processResponse : function(response, node, callback)
33717     {
33718         var json = response.responseText;
33719         try {
33720             
33721             var o = Roo.decode(json);
33722             
33723             if (this.root === false && typeof(o.success) != undefined) {
33724                 this.root = 'data'; // the default behaviour for list like data..
33725                 }
33726                 
33727             if (this.root !== false &&  !o.success) {
33728                 // it's a failure condition.
33729                 var a = response.argument;
33730                 this.fireEvent("loadexception", this, a.node, response);
33731                 Roo.log("Load failed - should have a handler really");
33732                 return;
33733             }
33734             
33735             
33736             
33737             if (this.root !== false) {
33738                  o = o[this.root];
33739             }
33740             
33741             for(var i = 0, len = o.length; i < len; i++){
33742                 var n = this.createNode(o[i]);
33743                 if(n){
33744                     node.appendChild(n);
33745                 }
33746             }
33747             if(typeof callback == "function"){
33748                 callback(this, node);
33749             }
33750         }catch(e){
33751             this.handleFailure(response);
33752         }
33753     },
33754
33755     handleResponse : function(response){
33756         this.transId = false;
33757         var a = response.argument;
33758         this.processResponse(response, a.node, a.callback);
33759         this.fireEvent("load", this, a.node, response);
33760     },
33761
33762     handleFailure : function(response)
33763     {
33764         // should handle failure better..
33765         this.transId = false;
33766         var a = response.argument;
33767         this.fireEvent("loadexception", this, a.node, response);
33768         if(typeof a.callback == "function"){
33769             a.callback(this, a.node);
33770         }
33771     }
33772 });/*
33773  * Based on:
33774  * Ext JS Library 1.1.1
33775  * Copyright(c) 2006-2007, Ext JS, LLC.
33776  *
33777  * Originally Released Under LGPL - original licence link has changed is not relivant.
33778  *
33779  * Fork - LGPL
33780  * <script type="text/javascript">
33781  */
33782
33783 /**
33784 * @class Roo.tree.TreeFilter
33785 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33786 * @param {TreePanel} tree
33787 * @param {Object} config (optional)
33788  */
33789 Roo.tree.TreeFilter = function(tree, config){
33790     this.tree = tree;
33791     this.filtered = {};
33792     Roo.apply(this, config);
33793 };
33794
33795 Roo.tree.TreeFilter.prototype = {
33796     clearBlank:false,
33797     reverse:false,
33798     autoClear:false,
33799     remove:false,
33800
33801      /**
33802      * Filter the data by a specific attribute.
33803      * @param {String/RegExp} value Either string that the attribute value
33804      * should start with or a RegExp to test against the attribute
33805      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33806      * @param {TreeNode} startNode (optional) The node to start the filter at.
33807      */
33808     filter : function(value, attr, startNode){
33809         attr = attr || "text";
33810         var f;
33811         if(typeof value == "string"){
33812             var vlen = value.length;
33813             // auto clear empty filter
33814             if(vlen == 0 && this.clearBlank){
33815                 this.clear();
33816                 return;
33817             }
33818             value = value.toLowerCase();
33819             f = function(n){
33820                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33821             };
33822         }else if(value.exec){ // regex?
33823             f = function(n){
33824                 return value.test(n.attributes[attr]);
33825             };
33826         }else{
33827             throw 'Illegal filter type, must be string or regex';
33828         }
33829         this.filterBy(f, null, startNode);
33830         },
33831
33832     /**
33833      * Filter by a function. The passed function will be called with each
33834      * node in the tree (or from the startNode). If the function returns true, the node is kept
33835      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33836      * @param {Function} fn The filter function
33837      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33838      */
33839     filterBy : function(fn, scope, startNode){
33840         startNode = startNode || this.tree.root;
33841         if(this.autoClear){
33842             this.clear();
33843         }
33844         var af = this.filtered, rv = this.reverse;
33845         var f = function(n){
33846             if(n == startNode){
33847                 return true;
33848             }
33849             if(af[n.id]){
33850                 return false;
33851             }
33852             var m = fn.call(scope || n, n);
33853             if(!m || rv){
33854                 af[n.id] = n;
33855                 n.ui.hide();
33856                 return false;
33857             }
33858             return true;
33859         };
33860         startNode.cascade(f);
33861         if(this.remove){
33862            for(var id in af){
33863                if(typeof id != "function"){
33864                    var n = af[id];
33865                    if(n && n.parentNode){
33866                        n.parentNode.removeChild(n);
33867                    }
33868                }
33869            }
33870         }
33871     },
33872
33873     /**
33874      * Clears the current filter. Note: with the "remove" option
33875      * set a filter cannot be cleared.
33876      */
33877     clear : function(){
33878         var t = this.tree;
33879         var af = this.filtered;
33880         for(var id in af){
33881             if(typeof id != "function"){
33882                 var n = af[id];
33883                 if(n){
33884                     n.ui.show();
33885                 }
33886             }
33887         }
33888         this.filtered = {};
33889     }
33890 };
33891 /*
33892  * Based on:
33893  * Ext JS Library 1.1.1
33894  * Copyright(c) 2006-2007, Ext JS, LLC.
33895  *
33896  * Originally Released Under LGPL - original licence link has changed is not relivant.
33897  *
33898  * Fork - LGPL
33899  * <script type="text/javascript">
33900  */
33901  
33902
33903 /**
33904  * @class Roo.tree.TreeSorter
33905  * Provides sorting of nodes in a TreePanel
33906  * 
33907  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33908  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33909  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33910  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33911  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33912  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33913  * @constructor
33914  * @param {TreePanel} tree
33915  * @param {Object} config
33916  */
33917 Roo.tree.TreeSorter = function(tree, config){
33918     Roo.apply(this, config);
33919     tree.on("beforechildrenrendered", this.doSort, this);
33920     tree.on("append", this.updateSort, this);
33921     tree.on("insert", this.updateSort, this);
33922     
33923     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33924     var p = this.property || "text";
33925     var sortType = this.sortType;
33926     var fs = this.folderSort;
33927     var cs = this.caseSensitive === true;
33928     var leafAttr = this.leafAttr || 'leaf';
33929
33930     this.sortFn = function(n1, n2){
33931         if(fs){
33932             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33933                 return 1;
33934             }
33935             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33936                 return -1;
33937             }
33938         }
33939         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33940         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33941         if(v1 < v2){
33942                         return dsc ? +1 : -1;
33943                 }else if(v1 > v2){
33944                         return dsc ? -1 : +1;
33945         }else{
33946                 return 0;
33947         }
33948     };
33949 };
33950
33951 Roo.tree.TreeSorter.prototype = {
33952     doSort : function(node){
33953         node.sort(this.sortFn);
33954     },
33955     
33956     compareNodes : function(n1, n2){
33957         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33958     },
33959     
33960     updateSort : function(tree, node){
33961         if(node.childrenRendered){
33962             this.doSort.defer(1, this, [node]);
33963         }
33964     }
33965 };/*
33966  * Based on:
33967  * Ext JS Library 1.1.1
33968  * Copyright(c) 2006-2007, Ext JS, LLC.
33969  *
33970  * Originally Released Under LGPL - original licence link has changed is not relivant.
33971  *
33972  * Fork - LGPL
33973  * <script type="text/javascript">
33974  */
33975
33976 if(Roo.dd.DropZone){
33977     
33978 Roo.tree.TreeDropZone = function(tree, config){
33979     this.allowParentInsert = false;
33980     this.allowContainerDrop = false;
33981     this.appendOnly = false;
33982     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33983     this.tree = tree;
33984     this.lastInsertClass = "x-tree-no-status";
33985     this.dragOverData = {};
33986 };
33987
33988 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33989     ddGroup : "TreeDD",
33990     scroll:  true,
33991     
33992     expandDelay : 1000,
33993     
33994     expandNode : function(node){
33995         if(node.hasChildNodes() && !node.isExpanded()){
33996             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33997         }
33998     },
33999     
34000     queueExpand : function(node){
34001         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34002     },
34003     
34004     cancelExpand : function(){
34005         if(this.expandProcId){
34006             clearTimeout(this.expandProcId);
34007             this.expandProcId = false;
34008         }
34009     },
34010     
34011     isValidDropPoint : function(n, pt, dd, e, data){
34012         if(!n || !data){ return false; }
34013         var targetNode = n.node;
34014         var dropNode = data.node;
34015         // default drop rules
34016         if(!(targetNode && targetNode.isTarget && pt)){
34017             return false;
34018         }
34019         if(pt == "append" && targetNode.allowChildren === false){
34020             return false;
34021         }
34022         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34023             return false;
34024         }
34025         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34026             return false;
34027         }
34028         // reuse the object
34029         var overEvent = this.dragOverData;
34030         overEvent.tree = this.tree;
34031         overEvent.target = targetNode;
34032         overEvent.data = data;
34033         overEvent.point = pt;
34034         overEvent.source = dd;
34035         overEvent.rawEvent = e;
34036         overEvent.dropNode = dropNode;
34037         overEvent.cancel = false;  
34038         var result = this.tree.fireEvent("nodedragover", overEvent);
34039         return overEvent.cancel === false && result !== false;
34040     },
34041     
34042     getDropPoint : function(e, n, dd)
34043     {
34044         var tn = n.node;
34045         if(tn.isRoot){
34046             return tn.allowChildren !== false ? "append" : false; // always append for root
34047         }
34048         var dragEl = n.ddel;
34049         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34050         var y = Roo.lib.Event.getPageY(e);
34051         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34052         
34053         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34054         var noAppend = tn.allowChildren === false;
34055         if(this.appendOnly || tn.parentNode.allowChildren === false){
34056             return noAppend ? false : "append";
34057         }
34058         var noBelow = false;
34059         if(!this.allowParentInsert){
34060             noBelow = tn.hasChildNodes() && tn.isExpanded();
34061         }
34062         var q = (b - t) / (noAppend ? 2 : 3);
34063         if(y >= t && y < (t + q)){
34064             return "above";
34065         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34066             return "below";
34067         }else{
34068             return "append";
34069         }
34070     },
34071     
34072     onNodeEnter : function(n, dd, e, data)
34073     {
34074         this.cancelExpand();
34075     },
34076     
34077     onNodeOver : function(n, dd, e, data)
34078     {
34079        
34080         var pt = this.getDropPoint(e, n, dd);
34081         var node = n.node;
34082         
34083         // auto node expand check
34084         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34085             this.queueExpand(node);
34086         }else if(pt != "append"){
34087             this.cancelExpand();
34088         }
34089         
34090         // set the insert point style on the target node
34091         var returnCls = this.dropNotAllowed;
34092         if(this.isValidDropPoint(n, pt, dd, e, data)){
34093            if(pt){
34094                var el = n.ddel;
34095                var cls;
34096                if(pt == "above"){
34097                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34098                    cls = "x-tree-drag-insert-above";
34099                }else if(pt == "below"){
34100                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34101                    cls = "x-tree-drag-insert-below";
34102                }else{
34103                    returnCls = "x-tree-drop-ok-append";
34104                    cls = "x-tree-drag-append";
34105                }
34106                if(this.lastInsertClass != cls){
34107                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34108                    this.lastInsertClass = cls;
34109                }
34110            }
34111        }
34112        return returnCls;
34113     },
34114     
34115     onNodeOut : function(n, dd, e, data){
34116         
34117         this.cancelExpand();
34118         this.removeDropIndicators(n);
34119     },
34120     
34121     onNodeDrop : function(n, dd, e, data){
34122         var point = this.getDropPoint(e, n, dd);
34123         var targetNode = n.node;
34124         targetNode.ui.startDrop();
34125         if(!this.isValidDropPoint(n, point, dd, e, data)){
34126             targetNode.ui.endDrop();
34127             return false;
34128         }
34129         // first try to find the drop node
34130         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34131         var dropEvent = {
34132             tree : this.tree,
34133             target: targetNode,
34134             data: data,
34135             point: point,
34136             source: dd,
34137             rawEvent: e,
34138             dropNode: dropNode,
34139             cancel: !dropNode   
34140         };
34141         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34142         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34143             targetNode.ui.endDrop();
34144             return false;
34145         }
34146         // allow target changing
34147         targetNode = dropEvent.target;
34148         if(point == "append" && !targetNode.isExpanded()){
34149             targetNode.expand(false, null, function(){
34150                 this.completeDrop(dropEvent);
34151             }.createDelegate(this));
34152         }else{
34153             this.completeDrop(dropEvent);
34154         }
34155         return true;
34156     },
34157     
34158     completeDrop : function(de){
34159         var ns = de.dropNode, p = de.point, t = de.target;
34160         if(!(ns instanceof Array)){
34161             ns = [ns];
34162         }
34163         var n;
34164         for(var i = 0, len = ns.length; i < len; i++){
34165             n = ns[i];
34166             if(p == "above"){
34167                 t.parentNode.insertBefore(n, t);
34168             }else if(p == "below"){
34169                 t.parentNode.insertBefore(n, t.nextSibling);
34170             }else{
34171                 t.appendChild(n);
34172             }
34173         }
34174         n.ui.focus();
34175         if(this.tree.hlDrop){
34176             n.ui.highlight();
34177         }
34178         t.ui.endDrop();
34179         this.tree.fireEvent("nodedrop", de);
34180     },
34181     
34182     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34183         if(this.tree.hlDrop){
34184             dropNode.ui.focus();
34185             dropNode.ui.highlight();
34186         }
34187         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34188     },
34189     
34190     getTree : function(){
34191         return this.tree;
34192     },
34193     
34194     removeDropIndicators : function(n){
34195         if(n && n.ddel){
34196             var el = n.ddel;
34197             Roo.fly(el).removeClass([
34198                     "x-tree-drag-insert-above",
34199                     "x-tree-drag-insert-below",
34200                     "x-tree-drag-append"]);
34201             this.lastInsertClass = "_noclass";
34202         }
34203     },
34204     
34205     beforeDragDrop : function(target, e, id){
34206         this.cancelExpand();
34207         return true;
34208     },
34209     
34210     afterRepair : function(data){
34211         if(data && Roo.enableFx){
34212             data.node.ui.highlight();
34213         }
34214         this.hideProxy();
34215     } 
34216     
34217 });
34218
34219 }
34220 /*
34221  * Based on:
34222  * Ext JS Library 1.1.1
34223  * Copyright(c) 2006-2007, Ext JS, LLC.
34224  *
34225  * Originally Released Under LGPL - original licence link has changed is not relivant.
34226  *
34227  * Fork - LGPL
34228  * <script type="text/javascript">
34229  */
34230  
34231
34232 if(Roo.dd.DragZone){
34233 Roo.tree.TreeDragZone = function(tree, config){
34234     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34235     this.tree = tree;
34236 };
34237
34238 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34239     ddGroup : "TreeDD",
34240    
34241     onBeforeDrag : function(data, e){
34242         var n = data.node;
34243         return n && n.draggable && !n.disabled;
34244     },
34245      
34246     
34247     onInitDrag : function(e){
34248         var data = this.dragData;
34249         this.tree.getSelectionModel().select(data.node);
34250         this.proxy.update("");
34251         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34252         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34253     },
34254     
34255     getRepairXY : function(e, data){
34256         return data.node.ui.getDDRepairXY();
34257     },
34258     
34259     onEndDrag : function(data, e){
34260         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34261         
34262         
34263     },
34264     
34265     onValidDrop : function(dd, e, id){
34266         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34267         this.hideProxy();
34268     },
34269     
34270     beforeInvalidDrop : function(e, id){
34271         // this scrolls the original position back into view
34272         var sm = this.tree.getSelectionModel();
34273         sm.clearSelections();
34274         sm.select(this.dragData.node);
34275     }
34276 });
34277 }/*
34278  * Based on:
34279  * Ext JS Library 1.1.1
34280  * Copyright(c) 2006-2007, Ext JS, LLC.
34281  *
34282  * Originally Released Under LGPL - original licence link has changed is not relivant.
34283  *
34284  * Fork - LGPL
34285  * <script type="text/javascript">
34286  */
34287 /**
34288  * @class Roo.tree.TreeEditor
34289  * @extends Roo.Editor
34290  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34291  * as the editor field.
34292  * @constructor
34293  * @param {Object} config (used to be the tree panel.)
34294  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34295  * 
34296  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34297  * @cfg {Roo.form.TextField|Object} field The field configuration
34298  *
34299  * 
34300  */
34301 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34302     var tree = config;
34303     var field;
34304     if (oldconfig) { // old style..
34305         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34306     } else {
34307         // new style..
34308         tree = config.tree;
34309         config.field = config.field  || {};
34310         config.field.xtype = 'TextField';
34311         field = Roo.factory(config.field, Roo.form);
34312     }
34313     config = config || {};
34314     
34315     
34316     this.addEvents({
34317         /**
34318          * @event beforenodeedit
34319          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34320          * false from the handler of this event.
34321          * @param {Editor} this
34322          * @param {Roo.tree.Node} node 
34323          */
34324         "beforenodeedit" : true
34325     });
34326     
34327     //Roo.log(config);
34328     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34329
34330     this.tree = tree;
34331
34332     tree.on('beforeclick', this.beforeNodeClick, this);
34333     tree.getTreeEl().on('mousedown', this.hide, this);
34334     this.on('complete', this.updateNode, this);
34335     this.on('beforestartedit', this.fitToTree, this);
34336     this.on('startedit', this.bindScroll, this, {delay:10});
34337     this.on('specialkey', this.onSpecialKey, this);
34338 };
34339
34340 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34341     /**
34342      * @cfg {String} alignment
34343      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34344      */
34345     alignment: "l-l",
34346     // inherit
34347     autoSize: false,
34348     /**
34349      * @cfg {Boolean} hideEl
34350      * True to hide the bound element while the editor is displayed (defaults to false)
34351      */
34352     hideEl : false,
34353     /**
34354      * @cfg {String} cls
34355      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34356      */
34357     cls: "x-small-editor x-tree-editor",
34358     /**
34359      * @cfg {Boolean} shim
34360      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34361      */
34362     shim:false,
34363     // inherit
34364     shadow:"frame",
34365     /**
34366      * @cfg {Number} maxWidth
34367      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34368      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34369      * scroll and client offsets into account prior to each edit.
34370      */
34371     maxWidth: 250,
34372
34373     editDelay : 350,
34374
34375     // private
34376     fitToTree : function(ed, el){
34377         var td = this.tree.getTreeEl().dom, nd = el.dom;
34378         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
34379             td.scrollLeft = nd.offsetLeft;
34380         }
34381         var w = Math.min(
34382                 this.maxWidth,
34383                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
34384         this.setSize(w, '');
34385         
34386         return this.fireEvent('beforenodeedit', this, this.editNode);
34387         
34388     },
34389
34390     // private
34391     triggerEdit : function(node){
34392         this.completeEdit();
34393         this.editNode = node;
34394         this.startEdit(node.ui.textNode, node.text);
34395     },
34396
34397     // private
34398     bindScroll : function(){
34399         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
34400     },
34401
34402     // private
34403     beforeNodeClick : function(node, e){
34404         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
34405         this.lastClick = new Date();
34406         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
34407             e.stopEvent();
34408             this.triggerEdit(node);
34409             return false;
34410         }
34411         return true;
34412     },
34413
34414     // private
34415     updateNode : function(ed, value){
34416         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
34417         this.editNode.setText(value);
34418     },
34419
34420     // private
34421     onHide : function(){
34422         Roo.tree.TreeEditor.superclass.onHide.call(this);
34423         if(this.editNode){
34424             this.editNode.ui.focus();
34425         }
34426     },
34427
34428     // private
34429     onSpecialKey : function(field, e){
34430         var k = e.getKey();
34431         if(k == e.ESC){
34432             e.stopEvent();
34433             this.cancelEdit();
34434         }else if(k == e.ENTER && !e.hasModifier()){
34435             e.stopEvent();
34436             this.completeEdit();
34437         }
34438     }
34439 });//<Script type="text/javascript">
34440 /*
34441  * Based on:
34442  * Ext JS Library 1.1.1
34443  * Copyright(c) 2006-2007, Ext JS, LLC.
34444  *
34445  * Originally Released Under LGPL - original licence link has changed is not relivant.
34446  *
34447  * Fork - LGPL
34448  * <script type="text/javascript">
34449  */
34450  
34451 /**
34452  * Not documented??? - probably should be...
34453  */
34454
34455 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
34456     //focus: Roo.emptyFn, // prevent odd scrolling behavior
34457     
34458     renderElements : function(n, a, targetNode, bulkRender){
34459         //consel.log("renderElements?");
34460         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34461
34462         var t = n.getOwnerTree();
34463         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
34464         
34465         var cols = t.columns;
34466         var bw = t.borderWidth;
34467         var c = cols[0];
34468         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34469          var cb = typeof a.checked == "boolean";
34470         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34471         var colcls = 'x-t-' + tid + '-c0';
34472         var buf = [
34473             '<li class="x-tree-node">',
34474             
34475                 
34476                 '<div class="x-tree-node-el ', a.cls,'">',
34477                     // extran...
34478                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
34479                 
34480                 
34481                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
34482                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
34483                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
34484                            (a.icon ? ' x-tree-node-inline-icon' : ''),
34485                            (a.iconCls ? ' '+a.iconCls : ''),
34486                            '" unselectable="on" />',
34487                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
34488                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
34489                              
34490                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34491                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
34492                             '<span unselectable="on" qtip="' + tx + '">',
34493                              tx,
34494                              '</span></a>' ,
34495                     '</div>',
34496                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
34497                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
34498                  ];
34499         for(var i = 1, len = cols.length; i < len; i++){
34500             c = cols[i];
34501             colcls = 'x-t-' + tid + '-c' +i;
34502             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
34503             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
34504                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
34505                       "</div>");
34506          }
34507          
34508          buf.push(
34509             '</a>',
34510             '<div class="x-clear"></div></div>',
34511             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34512             "</li>");
34513         
34514         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34515             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34516                                 n.nextSibling.ui.getEl(), buf.join(""));
34517         }else{
34518             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34519         }
34520         var el = this.wrap.firstChild;
34521         this.elRow = el;
34522         this.elNode = el.firstChild;
34523         this.ranchor = el.childNodes[1];
34524         this.ctNode = this.wrap.childNodes[1];
34525         var cs = el.firstChild.childNodes;
34526         this.indentNode = cs[0];
34527         this.ecNode = cs[1];
34528         this.iconNode = cs[2];
34529         var index = 3;
34530         if(cb){
34531             this.checkbox = cs[3];
34532             index++;
34533         }
34534         this.anchor = cs[index];
34535         
34536         this.textNode = cs[index].firstChild;
34537         
34538         //el.on("click", this.onClick, this);
34539         //el.on("dblclick", this.onDblClick, this);
34540         
34541         
34542        // console.log(this);
34543     },
34544     initEvents : function(){
34545         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
34546         
34547             
34548         var a = this.ranchor;
34549
34550         var el = Roo.get(a);
34551
34552         if(Roo.isOpera){ // opera render bug ignores the CSS
34553             el.setStyle("text-decoration", "none");
34554         }
34555
34556         el.on("click", this.onClick, this);
34557         el.on("dblclick", this.onDblClick, this);
34558         el.on("contextmenu", this.onContextMenu, this);
34559         
34560     },
34561     
34562     /*onSelectedChange : function(state){
34563         if(state){
34564             this.focus();
34565             this.addClass("x-tree-selected");
34566         }else{
34567             //this.blur();
34568             this.removeClass("x-tree-selected");
34569         }
34570     },*/
34571     addClass : function(cls){
34572         if(this.elRow){
34573             Roo.fly(this.elRow).addClass(cls);
34574         }
34575         
34576     },
34577     
34578     
34579     removeClass : function(cls){
34580         if(this.elRow){
34581             Roo.fly(this.elRow).removeClass(cls);
34582         }
34583     }
34584
34585     
34586     
34587 });//<Script type="text/javascript">
34588
34589 /*
34590  * Based on:
34591  * Ext JS Library 1.1.1
34592  * Copyright(c) 2006-2007, Ext JS, LLC.
34593  *
34594  * Originally Released Under LGPL - original licence link has changed is not relivant.
34595  *
34596  * Fork - LGPL
34597  * <script type="text/javascript">
34598  */
34599  
34600
34601 /**
34602  * @class Roo.tree.ColumnTree
34603  * @extends Roo.data.TreePanel
34604  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
34605  * @cfg {int} borderWidth  compined right/left border allowance
34606  * @constructor
34607  * @param {String/HTMLElement/Element} el The container element
34608  * @param {Object} config
34609  */
34610 Roo.tree.ColumnTree =  function(el, config)
34611 {
34612    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
34613    this.addEvents({
34614         /**
34615         * @event resize
34616         * Fire this event on a container when it resizes
34617         * @param {int} w Width
34618         * @param {int} h Height
34619         */
34620        "resize" : true
34621     });
34622     this.on('resize', this.onResize, this);
34623 };
34624
34625 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
34626     //lines:false,
34627     
34628     
34629     borderWidth: Roo.isBorderBox ? 0 : 2, 
34630     headEls : false,
34631     
34632     render : function(){
34633         // add the header.....
34634        
34635         Roo.tree.ColumnTree.superclass.render.apply(this);
34636         
34637         this.el.addClass('x-column-tree');
34638         
34639         this.headers = this.el.createChild(
34640             {cls:'x-tree-headers'},this.innerCt.dom);
34641    
34642         var cols = this.columns, c;
34643         var totalWidth = 0;
34644         this.headEls = [];
34645         var  len = cols.length;
34646         for(var i = 0; i < len; i++){
34647              c = cols[i];
34648              totalWidth += c.width;
34649             this.headEls.push(this.headers.createChild({
34650                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
34651                  cn: {
34652                      cls:'x-tree-hd-text',
34653                      html: c.header
34654                  },
34655                  style:'width:'+(c.width-this.borderWidth)+'px;'
34656              }));
34657         }
34658         this.headers.createChild({cls:'x-clear'});
34659         // prevent floats from wrapping when clipped
34660         this.headers.setWidth(totalWidth);
34661         //this.innerCt.setWidth(totalWidth);
34662         this.innerCt.setStyle({ overflow: 'auto' });
34663         this.onResize(this.width, this.height);
34664              
34665         
34666     },
34667     onResize : function(w,h)
34668     {
34669         this.height = h;
34670         this.width = w;
34671         // resize cols..
34672         this.innerCt.setWidth(this.width);
34673         this.innerCt.setHeight(this.height-20);
34674         
34675         // headers...
34676         var cols = this.columns, c;
34677         var totalWidth = 0;
34678         var expEl = false;
34679         var len = cols.length;
34680         for(var i = 0; i < len; i++){
34681             c = cols[i];
34682             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34683                 // it's the expander..
34684                 expEl  = this.headEls[i];
34685                 continue;
34686             }
34687             totalWidth += c.width;
34688             
34689         }
34690         if (expEl) {
34691             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34692         }
34693         this.headers.setWidth(w-20);
34694
34695         
34696         
34697         
34698     }
34699 });
34700 /*
34701  * Based on:
34702  * Ext JS Library 1.1.1
34703  * Copyright(c) 2006-2007, Ext JS, LLC.
34704  *
34705  * Originally Released Under LGPL - original licence link has changed is not relivant.
34706  *
34707  * Fork - LGPL
34708  * <script type="text/javascript">
34709  */
34710  
34711 /**
34712  * @class Roo.menu.Menu
34713  * @extends Roo.util.Observable
34714  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34715  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34716  * @constructor
34717  * Creates a new Menu
34718  * @param {Object} config Configuration options
34719  */
34720 Roo.menu.Menu = function(config){
34721     Roo.apply(this, config);
34722     this.id = this.id || Roo.id();
34723     this.addEvents({
34724         /**
34725          * @event beforeshow
34726          * Fires before this menu is displayed
34727          * @param {Roo.menu.Menu} this
34728          */
34729         beforeshow : true,
34730         /**
34731          * @event beforehide
34732          * Fires before this menu is hidden
34733          * @param {Roo.menu.Menu} this
34734          */
34735         beforehide : true,
34736         /**
34737          * @event show
34738          * Fires after this menu is displayed
34739          * @param {Roo.menu.Menu} this
34740          */
34741         show : true,
34742         /**
34743          * @event hide
34744          * Fires after this menu is hidden
34745          * @param {Roo.menu.Menu} this
34746          */
34747         hide : true,
34748         /**
34749          * @event click
34750          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34751          * @param {Roo.menu.Menu} this
34752          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34753          * @param {Roo.EventObject} e
34754          */
34755         click : true,
34756         /**
34757          * @event mouseover
34758          * Fires when the mouse is hovering over this menu
34759          * @param {Roo.menu.Menu} this
34760          * @param {Roo.EventObject} e
34761          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34762          */
34763         mouseover : true,
34764         /**
34765          * @event mouseout
34766          * Fires when the mouse exits this menu
34767          * @param {Roo.menu.Menu} this
34768          * @param {Roo.EventObject} e
34769          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34770          */
34771         mouseout : true,
34772         /**
34773          * @event itemclick
34774          * Fires when a menu item contained in this menu is clicked
34775          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34776          * @param {Roo.EventObject} e
34777          */
34778         itemclick: true
34779     });
34780     if (this.registerMenu) {
34781         Roo.menu.MenuMgr.register(this);
34782     }
34783     
34784     var mis = this.items;
34785     this.items = new Roo.util.MixedCollection();
34786     if(mis){
34787         this.add.apply(this, mis);
34788     }
34789 };
34790
34791 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34792     /**
34793      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34794      */
34795     minWidth : 120,
34796     /**
34797      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34798      * for bottom-right shadow (defaults to "sides")
34799      */
34800     shadow : "sides",
34801     /**
34802      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34803      * this menu (defaults to "tl-tr?")
34804      */
34805     subMenuAlign : "tl-tr?",
34806     /**
34807      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34808      * relative to its element of origin (defaults to "tl-bl?")
34809      */
34810     defaultAlign : "tl-bl?",
34811     /**
34812      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34813      */
34814     allowOtherMenus : false,
34815     /**
34816      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34817      */
34818     registerMenu : true,
34819
34820     hidden:true,
34821
34822     // private
34823     render : function(){
34824         if(this.el){
34825             return;
34826         }
34827         var el = this.el = new Roo.Layer({
34828             cls: "x-menu",
34829             shadow:this.shadow,
34830             constrain: false,
34831             parentEl: this.parentEl || document.body,
34832             zindex:15000
34833         });
34834
34835         this.keyNav = new Roo.menu.MenuNav(this);
34836
34837         if(this.plain){
34838             el.addClass("x-menu-plain");
34839         }
34840         if(this.cls){
34841             el.addClass(this.cls);
34842         }
34843         // generic focus element
34844         this.focusEl = el.createChild({
34845             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34846         });
34847         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34848         ul.on("click", this.onClick, this);
34849         ul.on("mouseover", this.onMouseOver, this);
34850         ul.on("mouseout", this.onMouseOut, this);
34851         this.items.each(function(item){
34852             if (item.hidden) {
34853                 return;
34854             }
34855             
34856             var li = document.createElement("li");
34857             li.className = "x-menu-list-item";
34858             ul.dom.appendChild(li);
34859             item.render(li, this);
34860         }, this);
34861         this.ul = ul;
34862         this.autoWidth();
34863     },
34864
34865     // private
34866     autoWidth : function(){
34867         var el = this.el, ul = this.ul;
34868         if(!el){
34869             return;
34870         }
34871         var w = this.width;
34872         if(w){
34873             el.setWidth(w);
34874         }else if(Roo.isIE){
34875             el.setWidth(this.minWidth);
34876             var t = el.dom.offsetWidth; // force recalc
34877             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34878         }
34879     },
34880
34881     // private
34882     delayAutoWidth : function(){
34883         if(this.rendered){
34884             if(!this.awTask){
34885                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34886             }
34887             this.awTask.delay(20);
34888         }
34889     },
34890
34891     // private
34892     findTargetItem : function(e){
34893         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34894         if(t && t.menuItemId){
34895             return this.items.get(t.menuItemId);
34896         }
34897     },
34898
34899     // private
34900     onClick : function(e){
34901         var t;
34902         if(t = this.findTargetItem(e)){
34903             t.onClick(e);
34904             this.fireEvent("click", this, t, e);
34905         }
34906     },
34907
34908     // private
34909     setActiveItem : function(item, autoExpand){
34910         if(item != this.activeItem){
34911             if(this.activeItem){
34912                 this.activeItem.deactivate();
34913             }
34914             this.activeItem = item;
34915             item.activate(autoExpand);
34916         }else if(autoExpand){
34917             item.expandMenu();
34918         }
34919     },
34920
34921     // private
34922     tryActivate : function(start, step){
34923         var items = this.items;
34924         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34925             var item = items.get(i);
34926             if(!item.disabled && item.canActivate){
34927                 this.setActiveItem(item, false);
34928                 return item;
34929             }
34930         }
34931         return false;
34932     },
34933
34934     // private
34935     onMouseOver : function(e){
34936         var t;
34937         if(t = this.findTargetItem(e)){
34938             if(t.canActivate && !t.disabled){
34939                 this.setActiveItem(t, true);
34940             }
34941         }
34942         this.fireEvent("mouseover", this, e, t);
34943     },
34944
34945     // private
34946     onMouseOut : function(e){
34947         var t;
34948         if(t = this.findTargetItem(e)){
34949             if(t == this.activeItem && t.shouldDeactivate(e)){
34950                 this.activeItem.deactivate();
34951                 delete this.activeItem;
34952             }
34953         }
34954         this.fireEvent("mouseout", this, e, t);
34955     },
34956
34957     /**
34958      * Read-only.  Returns true if the menu is currently displayed, else false.
34959      * @type Boolean
34960      */
34961     isVisible : function(){
34962         return this.el && !this.hidden;
34963     },
34964
34965     /**
34966      * Displays this menu relative to another element
34967      * @param {String/HTMLElement/Roo.Element} element The element to align to
34968      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34969      * the element (defaults to this.defaultAlign)
34970      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34971      */
34972     show : function(el, pos, parentMenu){
34973         this.parentMenu = parentMenu;
34974         if(!this.el){
34975             this.render();
34976         }
34977         this.fireEvent("beforeshow", this);
34978         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34979     },
34980
34981     /**
34982      * Displays this menu at a specific xy position
34983      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34984      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34985      */
34986     showAt : function(xy, parentMenu, /* private: */_e){
34987         this.parentMenu = parentMenu;
34988         if(!this.el){
34989             this.render();
34990         }
34991         if(_e !== false){
34992             this.fireEvent("beforeshow", this);
34993             xy = this.el.adjustForConstraints(xy);
34994         }
34995         this.el.setXY(xy);
34996         this.el.show();
34997         this.hidden = false;
34998         this.focus();
34999         this.fireEvent("show", this);
35000     },
35001
35002     focus : function(){
35003         if(!this.hidden){
35004             this.doFocus.defer(50, this);
35005         }
35006     },
35007
35008     doFocus : function(){
35009         if(!this.hidden){
35010             this.focusEl.focus();
35011         }
35012     },
35013
35014     /**
35015      * Hides this menu and optionally all parent menus
35016      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35017      */
35018     hide : function(deep){
35019         if(this.el && this.isVisible()){
35020             this.fireEvent("beforehide", this);
35021             if(this.activeItem){
35022                 this.activeItem.deactivate();
35023                 this.activeItem = null;
35024             }
35025             this.el.hide();
35026             this.hidden = true;
35027             this.fireEvent("hide", this);
35028         }
35029         if(deep === true && this.parentMenu){
35030             this.parentMenu.hide(true);
35031         }
35032     },
35033
35034     /**
35035      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35036      * Any of the following are valid:
35037      * <ul>
35038      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35039      * <li>An HTMLElement object which will be converted to a menu item</li>
35040      * <li>A menu item config object that will be created as a new menu item</li>
35041      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35042      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35043      * </ul>
35044      * Usage:
35045      * <pre><code>
35046 // Create the menu
35047 var menu = new Roo.menu.Menu();
35048
35049 // Create a menu item to add by reference
35050 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35051
35052 // Add a bunch of items at once using different methods.
35053 // Only the last item added will be returned.
35054 var item = menu.add(
35055     menuItem,                // add existing item by ref
35056     'Dynamic Item',          // new TextItem
35057     '-',                     // new separator
35058     { text: 'Config Item' }  // new item by config
35059 );
35060 </code></pre>
35061      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35062      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35063      */
35064     add : function(){
35065         var a = arguments, l = a.length, item;
35066         for(var i = 0; i < l; i++){
35067             var el = a[i];
35068             if ((typeof(el) == "object") && el.xtype && el.xns) {
35069                 el = Roo.factory(el, Roo.menu);
35070             }
35071             
35072             if(el.render){ // some kind of Item
35073                 item = this.addItem(el);
35074             }else if(typeof el == "string"){ // string
35075                 if(el == "separator" || el == "-"){
35076                     item = this.addSeparator();
35077                 }else{
35078                     item = this.addText(el);
35079                 }
35080             }else if(el.tagName || el.el){ // element
35081                 item = this.addElement(el);
35082             }else if(typeof el == "object"){ // must be menu item config?
35083                 item = this.addMenuItem(el);
35084             }
35085         }
35086         return item;
35087     },
35088
35089     /**
35090      * Returns this menu's underlying {@link Roo.Element} object
35091      * @return {Roo.Element} The element
35092      */
35093     getEl : function(){
35094         if(!this.el){
35095             this.render();
35096         }
35097         return this.el;
35098     },
35099
35100     /**
35101      * Adds a separator bar to the menu
35102      * @return {Roo.menu.Item} The menu item that was added
35103      */
35104     addSeparator : function(){
35105         return this.addItem(new Roo.menu.Separator());
35106     },
35107
35108     /**
35109      * Adds an {@link Roo.Element} object to the menu
35110      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35111      * @return {Roo.menu.Item} The menu item that was added
35112      */
35113     addElement : function(el){
35114         return this.addItem(new Roo.menu.BaseItem(el));
35115     },
35116
35117     /**
35118      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35119      * @param {Roo.menu.Item} item The menu item to add
35120      * @return {Roo.menu.Item} The menu item that was added
35121      */
35122     addItem : function(item){
35123         this.items.add(item);
35124         if(this.ul){
35125             var li = document.createElement("li");
35126             li.className = "x-menu-list-item";
35127             this.ul.dom.appendChild(li);
35128             item.render(li, this);
35129             this.delayAutoWidth();
35130         }
35131         return item;
35132     },
35133
35134     /**
35135      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35136      * @param {Object} config A MenuItem config object
35137      * @return {Roo.menu.Item} The menu item that was added
35138      */
35139     addMenuItem : function(config){
35140         if(!(config instanceof Roo.menu.Item)){
35141             if(typeof config.checked == "boolean"){ // must be check menu item config?
35142                 config = new Roo.menu.CheckItem(config);
35143             }else{
35144                 config = new Roo.menu.Item(config);
35145             }
35146         }
35147         return this.addItem(config);
35148     },
35149
35150     /**
35151      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35152      * @param {String} text The text to display in the menu item
35153      * @return {Roo.menu.Item} The menu item that was added
35154      */
35155     addText : function(text){
35156         return this.addItem(new Roo.menu.TextItem({ text : text }));
35157     },
35158
35159     /**
35160      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35161      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35162      * @param {Roo.menu.Item} item The menu item to add
35163      * @return {Roo.menu.Item} The menu item that was added
35164      */
35165     insert : function(index, item){
35166         this.items.insert(index, item);
35167         if(this.ul){
35168             var li = document.createElement("li");
35169             li.className = "x-menu-list-item";
35170             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35171             item.render(li, this);
35172             this.delayAutoWidth();
35173         }
35174         return item;
35175     },
35176
35177     /**
35178      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35179      * @param {Roo.menu.Item} item The menu item to remove
35180      */
35181     remove : function(item){
35182         this.items.removeKey(item.id);
35183         item.destroy();
35184     },
35185
35186     /**
35187      * Removes and destroys all items in the menu
35188      */
35189     removeAll : function(){
35190         var f;
35191         while(f = this.items.first()){
35192             this.remove(f);
35193         }
35194     }
35195 });
35196
35197 // MenuNav is a private utility class used internally by the Menu
35198 Roo.menu.MenuNav = function(menu){
35199     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35200     this.scope = this.menu = menu;
35201 };
35202
35203 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35204     doRelay : function(e, h){
35205         var k = e.getKey();
35206         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35207             this.menu.tryActivate(0, 1);
35208             return false;
35209         }
35210         return h.call(this.scope || this, e, this.menu);
35211     },
35212
35213     up : function(e, m){
35214         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35215             m.tryActivate(m.items.length-1, -1);
35216         }
35217     },
35218
35219     down : function(e, m){
35220         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35221             m.tryActivate(0, 1);
35222         }
35223     },
35224
35225     right : function(e, m){
35226         if(m.activeItem){
35227             m.activeItem.expandMenu(true);
35228         }
35229     },
35230
35231     left : function(e, m){
35232         m.hide();
35233         if(m.parentMenu && m.parentMenu.activeItem){
35234             m.parentMenu.activeItem.activate();
35235         }
35236     },
35237
35238     enter : function(e, m){
35239         if(m.activeItem){
35240             e.stopPropagation();
35241             m.activeItem.onClick(e);
35242             m.fireEvent("click", this, m.activeItem);
35243             return true;
35244         }
35245     }
35246 });/*
35247  * Based on:
35248  * Ext JS Library 1.1.1
35249  * Copyright(c) 2006-2007, Ext JS, LLC.
35250  *
35251  * Originally Released Under LGPL - original licence link has changed is not relivant.
35252  *
35253  * Fork - LGPL
35254  * <script type="text/javascript">
35255  */
35256  
35257 /**
35258  * @class Roo.menu.MenuMgr
35259  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35260  * @singleton
35261  */
35262 Roo.menu.MenuMgr = function(){
35263    var menus, active, groups = {}, attached = false, lastShow = new Date();
35264
35265    // private - called when first menu is created
35266    function init(){
35267        menus = {};
35268        active = new Roo.util.MixedCollection();
35269        Roo.get(document).addKeyListener(27, function(){
35270            if(active.length > 0){
35271                hideAll();
35272            }
35273        });
35274    }
35275
35276    // private
35277    function hideAll(){
35278        if(active && active.length > 0){
35279            var c = active.clone();
35280            c.each(function(m){
35281                m.hide();
35282            });
35283        }
35284    }
35285
35286    // private
35287    function onHide(m){
35288        active.remove(m);
35289        if(active.length < 1){
35290            Roo.get(document).un("mousedown", onMouseDown);
35291            attached = false;
35292        }
35293    }
35294
35295    // private
35296    function onShow(m){
35297        var last = active.last();
35298        lastShow = new Date();
35299        active.add(m);
35300        if(!attached){
35301            Roo.get(document).on("mousedown", onMouseDown);
35302            attached = true;
35303        }
35304        if(m.parentMenu){
35305           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35306           m.parentMenu.activeChild = m;
35307        }else if(last && last.isVisible()){
35308           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35309        }
35310    }
35311
35312    // private
35313    function onBeforeHide(m){
35314        if(m.activeChild){
35315            m.activeChild.hide();
35316        }
35317        if(m.autoHideTimer){
35318            clearTimeout(m.autoHideTimer);
35319            delete m.autoHideTimer;
35320        }
35321    }
35322
35323    // private
35324    function onBeforeShow(m){
35325        var pm = m.parentMenu;
35326        if(!pm && !m.allowOtherMenus){
35327            hideAll();
35328        }else if(pm && pm.activeChild && active != m){
35329            pm.activeChild.hide();
35330        }
35331    }
35332
35333    // private
35334    function onMouseDown(e){
35335        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35336            hideAll();
35337        }
35338    }
35339
35340    // private
35341    function onBeforeCheck(mi, state){
35342        if(state){
35343            var g = groups[mi.group];
35344            for(var i = 0, l = g.length; i < l; i++){
35345                if(g[i] != mi){
35346                    g[i].setChecked(false);
35347                }
35348            }
35349        }
35350    }
35351
35352    return {
35353
35354        /**
35355         * Hides all menus that are currently visible
35356         */
35357        hideAll : function(){
35358             hideAll();  
35359        },
35360
35361        // private
35362        register : function(menu){
35363            if(!menus){
35364                init();
35365            }
35366            menus[menu.id] = menu;
35367            menu.on("beforehide", onBeforeHide);
35368            menu.on("hide", onHide);
35369            menu.on("beforeshow", onBeforeShow);
35370            menu.on("show", onShow);
35371            var g = menu.group;
35372            if(g && menu.events["checkchange"]){
35373                if(!groups[g]){
35374                    groups[g] = [];
35375                }
35376                groups[g].push(menu);
35377                menu.on("checkchange", onCheck);
35378            }
35379        },
35380
35381         /**
35382          * Returns a {@link Roo.menu.Menu} object
35383          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
35384          * be used to generate and return a new Menu instance.
35385          */
35386        get : function(menu){
35387            if(typeof menu == "string"){ // menu id
35388                return menus[menu];
35389            }else if(menu.events){  // menu instance
35390                return menu;
35391            }else if(typeof menu.length == 'number'){ // array of menu items?
35392                return new Roo.menu.Menu({items:menu});
35393            }else{ // otherwise, must be a config
35394                return new Roo.menu.Menu(menu);
35395            }
35396        },
35397
35398        // private
35399        unregister : function(menu){
35400            delete menus[menu.id];
35401            menu.un("beforehide", onBeforeHide);
35402            menu.un("hide", onHide);
35403            menu.un("beforeshow", onBeforeShow);
35404            menu.un("show", onShow);
35405            var g = menu.group;
35406            if(g && menu.events["checkchange"]){
35407                groups[g].remove(menu);
35408                menu.un("checkchange", onCheck);
35409            }
35410        },
35411
35412        // private
35413        registerCheckable : function(menuItem){
35414            var g = menuItem.group;
35415            if(g){
35416                if(!groups[g]){
35417                    groups[g] = [];
35418                }
35419                groups[g].push(menuItem);
35420                menuItem.on("beforecheckchange", onBeforeCheck);
35421            }
35422        },
35423
35424        // private
35425        unregisterCheckable : function(menuItem){
35426            var g = menuItem.group;
35427            if(g){
35428                groups[g].remove(menuItem);
35429                menuItem.un("beforecheckchange", onBeforeCheck);
35430            }
35431        }
35432    };
35433 }();/*
35434  * Based on:
35435  * Ext JS Library 1.1.1
35436  * Copyright(c) 2006-2007, Ext JS, LLC.
35437  *
35438  * Originally Released Under LGPL - original licence link has changed is not relivant.
35439  *
35440  * Fork - LGPL
35441  * <script type="text/javascript">
35442  */
35443  
35444
35445 /**
35446  * @class Roo.menu.BaseItem
35447  * @extends Roo.Component
35448  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
35449  * management and base configuration options shared by all menu components.
35450  * @constructor
35451  * Creates a new BaseItem
35452  * @param {Object} config Configuration options
35453  */
35454 Roo.menu.BaseItem = function(config){
35455     Roo.menu.BaseItem.superclass.constructor.call(this, config);
35456
35457     this.addEvents({
35458         /**
35459          * @event click
35460          * Fires when this item is clicked
35461          * @param {Roo.menu.BaseItem} this
35462          * @param {Roo.EventObject} e
35463          */
35464         click: true,
35465         /**
35466          * @event activate
35467          * Fires when this item is activated
35468          * @param {Roo.menu.BaseItem} this
35469          */
35470         activate : true,
35471         /**
35472          * @event deactivate
35473          * Fires when this item is deactivated
35474          * @param {Roo.menu.BaseItem} this
35475          */
35476         deactivate : true
35477     });
35478
35479     if(this.handler){
35480         this.on("click", this.handler, this.scope, true);
35481     }
35482 };
35483
35484 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
35485     /**
35486      * @cfg {Function} handler
35487      * A function that will handle the click event of this menu item (defaults to undefined)
35488      */
35489     /**
35490      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
35491      */
35492     canActivate : false,
35493     
35494      /**
35495      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
35496      */
35497     hidden: false,
35498     
35499     /**
35500      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
35501      */
35502     activeClass : "x-menu-item-active",
35503     /**
35504      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
35505      */
35506     hideOnClick : true,
35507     /**
35508      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
35509      */
35510     hideDelay : 100,
35511
35512     // private
35513     ctype: "Roo.menu.BaseItem",
35514
35515     // private
35516     actionMode : "container",
35517
35518     // private
35519     render : function(container, parentMenu){
35520         this.parentMenu = parentMenu;
35521         Roo.menu.BaseItem.superclass.render.call(this, container);
35522         this.container.menuItemId = this.id;
35523     },
35524
35525     // private
35526     onRender : function(container, position){
35527         this.el = Roo.get(this.el);
35528         container.dom.appendChild(this.el.dom);
35529     },
35530
35531     // private
35532     onClick : function(e){
35533         if(!this.disabled && this.fireEvent("click", this, e) !== false
35534                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
35535             this.handleClick(e);
35536         }else{
35537             e.stopEvent();
35538         }
35539     },
35540
35541     // private
35542     activate : function(){
35543         if(this.disabled){
35544             return false;
35545         }
35546         var li = this.container;
35547         li.addClass(this.activeClass);
35548         this.region = li.getRegion().adjust(2, 2, -2, -2);
35549         this.fireEvent("activate", this);
35550         return true;
35551     },
35552
35553     // private
35554     deactivate : function(){
35555         this.container.removeClass(this.activeClass);
35556         this.fireEvent("deactivate", this);
35557     },
35558
35559     // private
35560     shouldDeactivate : function(e){
35561         return !this.region || !this.region.contains(e.getPoint());
35562     },
35563
35564     // private
35565     handleClick : function(e){
35566         if(this.hideOnClick){
35567             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
35568         }
35569     },
35570
35571     // private
35572     expandMenu : function(autoActivate){
35573         // do nothing
35574     },
35575
35576     // private
35577     hideMenu : function(){
35578         // do nothing
35579     }
35580 });/*
35581  * Based on:
35582  * Ext JS Library 1.1.1
35583  * Copyright(c) 2006-2007, Ext JS, LLC.
35584  *
35585  * Originally Released Under LGPL - original licence link has changed is not relivant.
35586  *
35587  * Fork - LGPL
35588  * <script type="text/javascript">
35589  */
35590  
35591 /**
35592  * @class Roo.menu.Adapter
35593  * @extends Roo.menu.BaseItem
35594  * 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.
35595  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
35596  * @constructor
35597  * Creates a new Adapter
35598  * @param {Object} config Configuration options
35599  */
35600 Roo.menu.Adapter = function(component, config){
35601     Roo.menu.Adapter.superclass.constructor.call(this, config);
35602     this.component = component;
35603 };
35604 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
35605     // private
35606     canActivate : true,
35607
35608     // private
35609     onRender : function(container, position){
35610         this.component.render(container);
35611         this.el = this.component.getEl();
35612     },
35613
35614     // private
35615     activate : function(){
35616         if(this.disabled){
35617             return false;
35618         }
35619         this.component.focus();
35620         this.fireEvent("activate", this);
35621         return true;
35622     },
35623
35624     // private
35625     deactivate : function(){
35626         this.fireEvent("deactivate", this);
35627     },
35628
35629     // private
35630     disable : function(){
35631         this.component.disable();
35632         Roo.menu.Adapter.superclass.disable.call(this);
35633     },
35634
35635     // private
35636     enable : function(){
35637         this.component.enable();
35638         Roo.menu.Adapter.superclass.enable.call(this);
35639     }
35640 });/*
35641  * Based on:
35642  * Ext JS Library 1.1.1
35643  * Copyright(c) 2006-2007, Ext JS, LLC.
35644  *
35645  * Originally Released Under LGPL - original licence link has changed is not relivant.
35646  *
35647  * Fork - LGPL
35648  * <script type="text/javascript">
35649  */
35650
35651 /**
35652  * @class Roo.menu.TextItem
35653  * @extends Roo.menu.BaseItem
35654  * Adds a static text string to a menu, usually used as either a heading or group separator.
35655  * Note: old style constructor with text is still supported.
35656  * 
35657  * @constructor
35658  * Creates a new TextItem
35659  * @param {Object} cfg Configuration
35660  */
35661 Roo.menu.TextItem = function(cfg){
35662     if (typeof(cfg) == 'string') {
35663         this.text = cfg;
35664     } else {
35665         Roo.apply(this,cfg);
35666     }
35667     
35668     Roo.menu.TextItem.superclass.constructor.call(this);
35669 };
35670
35671 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
35672     /**
35673      * @cfg {Boolean} text Text to show on item.
35674      */
35675     text : '',
35676     
35677     /**
35678      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35679      */
35680     hideOnClick : false,
35681     /**
35682      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
35683      */
35684     itemCls : "x-menu-text",
35685
35686     // private
35687     onRender : function(){
35688         var s = document.createElement("span");
35689         s.className = this.itemCls;
35690         s.innerHTML = this.text;
35691         this.el = s;
35692         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35693     }
35694 });/*
35695  * Based on:
35696  * Ext JS Library 1.1.1
35697  * Copyright(c) 2006-2007, Ext JS, LLC.
35698  *
35699  * Originally Released Under LGPL - original licence link has changed is not relivant.
35700  *
35701  * Fork - LGPL
35702  * <script type="text/javascript">
35703  */
35704
35705 /**
35706  * @class Roo.menu.Separator
35707  * @extends Roo.menu.BaseItem
35708  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35709  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35710  * @constructor
35711  * @param {Object} config Configuration options
35712  */
35713 Roo.menu.Separator = function(config){
35714     Roo.menu.Separator.superclass.constructor.call(this, config);
35715 };
35716
35717 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35718     /**
35719      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35720      */
35721     itemCls : "x-menu-sep",
35722     /**
35723      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35724      */
35725     hideOnClick : false,
35726
35727     // private
35728     onRender : function(li){
35729         var s = document.createElement("span");
35730         s.className = this.itemCls;
35731         s.innerHTML = "&#160;";
35732         this.el = s;
35733         li.addClass("x-menu-sep-li");
35734         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35735     }
35736 });/*
35737  * Based on:
35738  * Ext JS Library 1.1.1
35739  * Copyright(c) 2006-2007, Ext JS, LLC.
35740  *
35741  * Originally Released Under LGPL - original licence link has changed is not relivant.
35742  *
35743  * Fork - LGPL
35744  * <script type="text/javascript">
35745  */
35746 /**
35747  * @class Roo.menu.Item
35748  * @extends Roo.menu.BaseItem
35749  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35750  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35751  * activation and click handling.
35752  * @constructor
35753  * Creates a new Item
35754  * @param {Object} config Configuration options
35755  */
35756 Roo.menu.Item = function(config){
35757     Roo.menu.Item.superclass.constructor.call(this, config);
35758     if(this.menu){
35759         this.menu = Roo.menu.MenuMgr.get(this.menu);
35760     }
35761 };
35762 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35763     
35764     /**
35765      * @cfg {String} text
35766      * The text to show on the menu item.
35767      */
35768     text: '',
35769      /**
35770      * @cfg {String} HTML to render in menu
35771      * The text to show on the menu item (HTML version).
35772      */
35773     html: '',
35774     /**
35775      * @cfg {String} icon
35776      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35777      */
35778     icon: undefined,
35779     /**
35780      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35781      */
35782     itemCls : "x-menu-item",
35783     /**
35784      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35785      */
35786     canActivate : true,
35787     /**
35788      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35789      */
35790     showDelay: 200,
35791     // doc'd in BaseItem
35792     hideDelay: 200,
35793
35794     // private
35795     ctype: "Roo.menu.Item",
35796     
35797     // private
35798     onRender : function(container, position){
35799         var el = document.createElement("a");
35800         el.hideFocus = true;
35801         el.unselectable = "on";
35802         el.href = this.href || "#";
35803         if(this.hrefTarget){
35804             el.target = this.hrefTarget;
35805         }
35806         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35807         
35808         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35809         
35810         el.innerHTML = String.format(
35811                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35812                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35813         this.el = el;
35814         Roo.menu.Item.superclass.onRender.call(this, container, position);
35815     },
35816
35817     /**
35818      * Sets the text to display in this menu item
35819      * @param {String} text The text to display
35820      * @param {Boolean} isHTML true to indicate text is pure html.
35821      */
35822     setText : function(text, isHTML){
35823         if (isHTML) {
35824             this.html = text;
35825         } else {
35826             this.text = text;
35827             this.html = '';
35828         }
35829         if(this.rendered){
35830             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35831      
35832             this.el.update(String.format(
35833                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35834                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35835             this.parentMenu.autoWidth();
35836         }
35837     },
35838
35839     // private
35840     handleClick : function(e){
35841         if(!this.href){ // if no link defined, stop the event automatically
35842             e.stopEvent();
35843         }
35844         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35845     },
35846
35847     // private
35848     activate : function(autoExpand){
35849         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35850             this.focus();
35851             if(autoExpand){
35852                 this.expandMenu();
35853             }
35854         }
35855         return true;
35856     },
35857
35858     // private
35859     shouldDeactivate : function(e){
35860         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35861             if(this.menu && this.menu.isVisible()){
35862                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35863             }
35864             return true;
35865         }
35866         return false;
35867     },
35868
35869     // private
35870     deactivate : function(){
35871         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35872         this.hideMenu();
35873     },
35874
35875     // private
35876     expandMenu : function(autoActivate){
35877         if(!this.disabled && this.menu){
35878             clearTimeout(this.hideTimer);
35879             delete this.hideTimer;
35880             if(!this.menu.isVisible() && !this.showTimer){
35881                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35882             }else if (this.menu.isVisible() && autoActivate){
35883                 this.menu.tryActivate(0, 1);
35884             }
35885         }
35886     },
35887
35888     // private
35889     deferExpand : function(autoActivate){
35890         delete this.showTimer;
35891         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35892         if(autoActivate){
35893             this.menu.tryActivate(0, 1);
35894         }
35895     },
35896
35897     // private
35898     hideMenu : function(){
35899         clearTimeout(this.showTimer);
35900         delete this.showTimer;
35901         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35902             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35903         }
35904     },
35905
35906     // private
35907     deferHide : function(){
35908         delete this.hideTimer;
35909         this.menu.hide();
35910     }
35911 });/*
35912  * Based on:
35913  * Ext JS Library 1.1.1
35914  * Copyright(c) 2006-2007, Ext JS, LLC.
35915  *
35916  * Originally Released Under LGPL - original licence link has changed is not relivant.
35917  *
35918  * Fork - LGPL
35919  * <script type="text/javascript">
35920  */
35921  
35922 /**
35923  * @class Roo.menu.CheckItem
35924  * @extends Roo.menu.Item
35925  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35926  * @constructor
35927  * Creates a new CheckItem
35928  * @param {Object} config Configuration options
35929  */
35930 Roo.menu.CheckItem = function(config){
35931     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35932     this.addEvents({
35933         /**
35934          * @event beforecheckchange
35935          * Fires before the checked value is set, providing an opportunity to cancel if needed
35936          * @param {Roo.menu.CheckItem} this
35937          * @param {Boolean} checked The new checked value that will be set
35938          */
35939         "beforecheckchange" : true,
35940         /**
35941          * @event checkchange
35942          * Fires after the checked value has been set
35943          * @param {Roo.menu.CheckItem} this
35944          * @param {Boolean} checked The checked value that was set
35945          */
35946         "checkchange" : true
35947     });
35948     if(this.checkHandler){
35949         this.on('checkchange', this.checkHandler, this.scope);
35950     }
35951 };
35952 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35953     /**
35954      * @cfg {String} group
35955      * All check items with the same group name will automatically be grouped into a single-select
35956      * radio button group (defaults to '')
35957      */
35958     /**
35959      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35960      */
35961     itemCls : "x-menu-item x-menu-check-item",
35962     /**
35963      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35964      */
35965     groupClass : "x-menu-group-item",
35966
35967     /**
35968      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35969      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35970      * initialized with checked = true will be rendered as checked.
35971      */
35972     checked: false,
35973
35974     // private
35975     ctype: "Roo.menu.CheckItem",
35976
35977     // private
35978     onRender : function(c){
35979         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35980         if(this.group){
35981             this.el.addClass(this.groupClass);
35982         }
35983         Roo.menu.MenuMgr.registerCheckable(this);
35984         if(this.checked){
35985             this.checked = false;
35986             this.setChecked(true, true);
35987         }
35988     },
35989
35990     // private
35991     destroy : function(){
35992         if(this.rendered){
35993             Roo.menu.MenuMgr.unregisterCheckable(this);
35994         }
35995         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35996     },
35997
35998     /**
35999      * Set the checked state of this item
36000      * @param {Boolean} checked The new checked value
36001      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36002      */
36003     setChecked : function(state, suppressEvent){
36004         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36005             if(this.container){
36006                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36007             }
36008             this.checked = state;
36009             if(suppressEvent !== true){
36010                 this.fireEvent("checkchange", this, state);
36011             }
36012         }
36013     },
36014
36015     // private
36016     handleClick : function(e){
36017        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36018            this.setChecked(!this.checked);
36019        }
36020        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36021     }
36022 });/*
36023  * Based on:
36024  * Ext JS Library 1.1.1
36025  * Copyright(c) 2006-2007, Ext JS, LLC.
36026  *
36027  * Originally Released Under LGPL - original licence link has changed is not relivant.
36028  *
36029  * Fork - LGPL
36030  * <script type="text/javascript">
36031  */
36032  
36033 /**
36034  * @class Roo.menu.DateItem
36035  * @extends Roo.menu.Adapter
36036  * A menu item that wraps the {@link Roo.DatPicker} component.
36037  * @constructor
36038  * Creates a new DateItem
36039  * @param {Object} config Configuration options
36040  */
36041 Roo.menu.DateItem = function(config){
36042     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36043     /** The Roo.DatePicker object @type Roo.DatePicker */
36044     this.picker = this.component;
36045     this.addEvents({select: true});
36046     
36047     this.picker.on("render", function(picker){
36048         picker.getEl().swallowEvent("click");
36049         picker.container.addClass("x-menu-date-item");
36050     });
36051
36052     this.picker.on("select", this.onSelect, this);
36053 };
36054
36055 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36056     // private
36057     onSelect : function(picker, date){
36058         this.fireEvent("select", this, date, picker);
36059         Roo.menu.DateItem.superclass.handleClick.call(this);
36060     }
36061 });/*
36062  * Based on:
36063  * Ext JS Library 1.1.1
36064  * Copyright(c) 2006-2007, Ext JS, LLC.
36065  *
36066  * Originally Released Under LGPL - original licence link has changed is not relivant.
36067  *
36068  * Fork - LGPL
36069  * <script type="text/javascript">
36070  */
36071  
36072 /**
36073  * @class Roo.menu.ColorItem
36074  * @extends Roo.menu.Adapter
36075  * A menu item that wraps the {@link Roo.ColorPalette} component.
36076  * @constructor
36077  * Creates a new ColorItem
36078  * @param {Object} config Configuration options
36079  */
36080 Roo.menu.ColorItem = function(config){
36081     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36082     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36083     this.palette = this.component;
36084     this.relayEvents(this.palette, ["select"]);
36085     if(this.selectHandler){
36086         this.on('select', this.selectHandler, this.scope);
36087     }
36088 };
36089 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36090  * Based on:
36091  * Ext JS Library 1.1.1
36092  * Copyright(c) 2006-2007, Ext JS, LLC.
36093  *
36094  * Originally Released Under LGPL - original licence link has changed is not relivant.
36095  *
36096  * Fork - LGPL
36097  * <script type="text/javascript">
36098  */
36099  
36100
36101 /**
36102  * @class Roo.menu.DateMenu
36103  * @extends Roo.menu.Menu
36104  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36105  * @constructor
36106  * Creates a new DateMenu
36107  * @param {Object} config Configuration options
36108  */
36109 Roo.menu.DateMenu = function(config){
36110     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36111     this.plain = true;
36112     var di = new Roo.menu.DateItem(config);
36113     this.add(di);
36114     /**
36115      * The {@link Roo.DatePicker} instance for this DateMenu
36116      * @type DatePicker
36117      */
36118     this.picker = di.picker;
36119     /**
36120      * @event select
36121      * @param {DatePicker} picker
36122      * @param {Date} date
36123      */
36124     this.relayEvents(di, ["select"]);
36125     this.on('beforeshow', function(){
36126         if(this.picker){
36127             this.picker.hideMonthPicker(false);
36128         }
36129     }, this);
36130 };
36131 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36132     cls:'x-date-menu'
36133 });/*
36134  * Based on:
36135  * Ext JS Library 1.1.1
36136  * Copyright(c) 2006-2007, Ext JS, LLC.
36137  *
36138  * Originally Released Under LGPL - original licence link has changed is not relivant.
36139  *
36140  * Fork - LGPL
36141  * <script type="text/javascript">
36142  */
36143  
36144
36145 /**
36146  * @class Roo.menu.ColorMenu
36147  * @extends Roo.menu.Menu
36148  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36149  * @constructor
36150  * Creates a new ColorMenu
36151  * @param {Object} config Configuration options
36152  */
36153 Roo.menu.ColorMenu = function(config){
36154     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36155     this.plain = true;
36156     var ci = new Roo.menu.ColorItem(config);
36157     this.add(ci);
36158     /**
36159      * The {@link Roo.ColorPalette} instance for this ColorMenu
36160      * @type ColorPalette
36161      */
36162     this.palette = ci.palette;
36163     /**
36164      * @event select
36165      * @param {ColorPalette} palette
36166      * @param {String} color
36167      */
36168     this.relayEvents(ci, ["select"]);
36169 };
36170 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36171  * Based on:
36172  * Ext JS Library 1.1.1
36173  * Copyright(c) 2006-2007, Ext JS, LLC.
36174  *
36175  * Originally Released Under LGPL - original licence link has changed is not relivant.
36176  *
36177  * Fork - LGPL
36178  * <script type="text/javascript">
36179  */
36180  
36181 /**
36182  * @class Roo.form.Field
36183  * @extends Roo.BoxComponent
36184  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36185  * @constructor
36186  * Creates a new Field
36187  * @param {Object} config Configuration options
36188  */
36189 Roo.form.Field = function(config){
36190     Roo.form.Field.superclass.constructor.call(this, config);
36191 };
36192
36193 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36194     /**
36195      * @cfg {String} fieldLabel Label to use when rendering a form.
36196      */
36197        /**
36198      * @cfg {String} qtip Mouse over tip
36199      */
36200      
36201     /**
36202      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36203      */
36204     invalidClass : "x-form-invalid",
36205     /**
36206      * @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")
36207      */
36208     invalidText : "The value in this field is invalid",
36209     /**
36210      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36211      */
36212     focusClass : "x-form-focus",
36213     /**
36214      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36215       automatic validation (defaults to "keyup").
36216      */
36217     validationEvent : "keyup",
36218     /**
36219      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36220      */
36221     validateOnBlur : true,
36222     /**
36223      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36224      */
36225     validationDelay : 250,
36226     /**
36227      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36228      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36229      */
36230     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36231     /**
36232      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36233      */
36234     fieldClass : "x-form-field",
36235     /**
36236      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36237      *<pre>
36238 Value         Description
36239 -----------   ----------------------------------------------------------------------
36240 qtip          Display a quick tip when the user hovers over the field
36241 title         Display a default browser title attribute popup
36242 under         Add a block div beneath the field containing the error text
36243 side          Add an error icon to the right of the field with a popup on hover
36244 [element id]  Add the error text directly to the innerHTML of the specified element
36245 </pre>
36246      */
36247     msgTarget : 'qtip',
36248     /**
36249      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36250      */
36251     msgFx : 'normal',
36252
36253     /**
36254      * @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.
36255      */
36256     readOnly : false,
36257
36258     /**
36259      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36260      */
36261     disabled : false,
36262
36263     /**
36264      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36265      */
36266     inputType : undefined,
36267     
36268     /**
36269      * @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).
36270          */
36271         tabIndex : undefined,
36272         
36273     // private
36274     isFormField : true,
36275
36276     // private
36277     hasFocus : false,
36278     /**
36279      * @property {Roo.Element} fieldEl
36280      * Element Containing the rendered Field (with label etc.)
36281      */
36282     /**
36283      * @cfg {Mixed} value A value to initialize this field with.
36284      */
36285     value : undefined,
36286
36287     /**
36288      * @cfg {String} name The field's HTML name attribute.
36289      */
36290     /**
36291      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36292      */
36293
36294         // private ??
36295         initComponent : function(){
36296         Roo.form.Field.superclass.initComponent.call(this);
36297         this.addEvents({
36298             /**
36299              * @event focus
36300              * Fires when this field receives input focus.
36301              * @param {Roo.form.Field} this
36302              */
36303             focus : true,
36304             /**
36305              * @event blur
36306              * Fires when this field loses input focus.
36307              * @param {Roo.form.Field} this
36308              */
36309             blur : true,
36310             /**
36311              * @event specialkey
36312              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36313              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36314              * @param {Roo.form.Field} this
36315              * @param {Roo.EventObject} e The event object
36316              */
36317             specialkey : true,
36318             /**
36319              * @event change
36320              * Fires just before the field blurs if the field value has changed.
36321              * @param {Roo.form.Field} this
36322              * @param {Mixed} newValue The new value
36323              * @param {Mixed} oldValue The original value
36324              */
36325             change : true,
36326             /**
36327              * @event invalid
36328              * Fires after the field has been marked as invalid.
36329              * @param {Roo.form.Field} this
36330              * @param {String} msg The validation message
36331              */
36332             invalid : true,
36333             /**
36334              * @event valid
36335              * Fires after the field has been validated with no errors.
36336              * @param {Roo.form.Field} this
36337              */
36338             valid : true,
36339              /**
36340              * @event keyup
36341              * Fires after the key up
36342              * @param {Roo.form.Field} this
36343              * @param {Roo.EventObject}  e The event Object
36344              */
36345             keyup : true
36346         });
36347     },
36348
36349     /**
36350      * Returns the name attribute of the field if available
36351      * @return {String} name The field name
36352      */
36353     getName: function(){
36354          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36355     },
36356
36357     // private
36358     onRender : function(ct, position){
36359         Roo.form.Field.superclass.onRender.call(this, ct, position);
36360         if(!this.el){
36361             var cfg = this.getAutoCreate();
36362             if(!cfg.name){
36363                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
36364             }
36365             if (!cfg.name.length) {
36366                 delete cfg.name;
36367             }
36368             if(this.inputType){
36369                 cfg.type = this.inputType;
36370             }
36371             this.el = ct.createChild(cfg, position);
36372         }
36373         var type = this.el.dom.type;
36374         if(type){
36375             if(type == 'password'){
36376                 type = 'text';
36377             }
36378             this.el.addClass('x-form-'+type);
36379         }
36380         if(this.readOnly){
36381             this.el.dom.readOnly = true;
36382         }
36383         if(this.tabIndex !== undefined){
36384             this.el.dom.setAttribute('tabIndex', this.tabIndex);
36385         }
36386
36387         this.el.addClass([this.fieldClass, this.cls]);
36388         this.initValue();
36389     },
36390
36391     /**
36392      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
36393      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
36394      * @return {Roo.form.Field} this
36395      */
36396     applyTo : function(target){
36397         this.allowDomMove = false;
36398         this.el = Roo.get(target);
36399         this.render(this.el.dom.parentNode);
36400         return this;
36401     },
36402
36403     // private
36404     initValue : function(){
36405         if(this.value !== undefined){
36406             this.setValue(this.value);
36407         }else if(this.el.dom.value.length > 0){
36408             this.setValue(this.el.dom.value);
36409         }
36410     },
36411
36412     /**
36413      * Returns true if this field has been changed since it was originally loaded and is not disabled.
36414      */
36415     isDirty : function() {
36416         if(this.disabled) {
36417             return false;
36418         }
36419         return String(this.getValue()) !== String(this.originalValue);
36420     },
36421
36422     // private
36423     afterRender : function(){
36424         Roo.form.Field.superclass.afterRender.call(this);
36425         this.initEvents();
36426     },
36427
36428     // private
36429     fireKey : function(e){
36430         //Roo.log('field ' + e.getKey());
36431         if(e.isNavKeyPress()){
36432             this.fireEvent("specialkey", this, e);
36433         }
36434     },
36435
36436     /**
36437      * Resets the current field value to the originally loaded value and clears any validation messages
36438      */
36439     reset : function(){
36440         this.setValue(this.originalValue);
36441         this.clearInvalid();
36442     },
36443
36444     // private
36445     initEvents : function(){
36446         // safari killled keypress - so keydown is now used..
36447         this.el.on("keydown" , this.fireKey,  this);
36448         this.el.on("focus", this.onFocus,  this);
36449         this.el.on("blur", this.onBlur,  this);
36450         this.el.relayEvent('keyup', this);
36451
36452         // reference to original value for reset
36453         this.originalValue = this.getValue();
36454     },
36455
36456     // private
36457     onFocus : function(){
36458         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36459             this.el.addClass(this.focusClass);
36460         }
36461         if(!this.hasFocus){
36462             this.hasFocus = true;
36463             this.startValue = this.getValue();
36464             this.fireEvent("focus", this);
36465         }
36466     },
36467
36468     beforeBlur : Roo.emptyFn,
36469
36470     // private
36471     onBlur : function(){
36472         this.beforeBlur();
36473         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
36474             this.el.removeClass(this.focusClass);
36475         }
36476         this.hasFocus = false;
36477         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
36478             this.validate();
36479         }
36480         var v = this.getValue();
36481         if(String(v) !== String(this.startValue)){
36482             this.fireEvent('change', this, v, this.startValue);
36483         }
36484         this.fireEvent("blur", this);
36485     },
36486
36487     /**
36488      * Returns whether or not the field value is currently valid
36489      * @param {Boolean} preventMark True to disable marking the field invalid
36490      * @return {Boolean} True if the value is valid, else false
36491      */
36492     isValid : function(preventMark){
36493         if(this.disabled){
36494             return true;
36495         }
36496         var restore = this.preventMark;
36497         this.preventMark = preventMark === true;
36498         var v = this.validateValue(this.processValue(this.getRawValue()));
36499         this.preventMark = restore;
36500         return v;
36501     },
36502
36503     /**
36504      * Validates the field value
36505      * @return {Boolean} True if the value is valid, else false
36506      */
36507     validate : function(){
36508         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
36509             this.clearInvalid();
36510             return true;
36511         }
36512         return false;
36513     },
36514
36515     processValue : function(value){
36516         return value;
36517     },
36518
36519     // private
36520     // Subclasses should provide the validation implementation by overriding this
36521     validateValue : function(value){
36522         return true;
36523     },
36524
36525     /**
36526      * Mark this field as invalid
36527      * @param {String} msg The validation message
36528      */
36529     markInvalid : function(msg){
36530         if(!this.rendered || this.preventMark){ // not rendered
36531             return;
36532         }
36533         this.el.addClass(this.invalidClass);
36534         msg = msg || this.invalidText;
36535         switch(this.msgTarget){
36536             case 'qtip':
36537                 this.el.dom.qtip = msg;
36538                 this.el.dom.qclass = 'x-form-invalid-tip';
36539                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
36540                     Roo.QuickTips.enable();
36541                 }
36542                 break;
36543             case 'title':
36544                 this.el.dom.title = msg;
36545                 break;
36546             case 'under':
36547                 if(!this.errorEl){
36548                     var elp = this.el.findParent('.x-form-element', 5, true);
36549                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
36550                     this.errorEl.setWidth(elp.getWidth(true)-20);
36551                 }
36552                 this.errorEl.update(msg);
36553                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
36554                 break;
36555             case 'side':
36556                 if(!this.errorIcon){
36557                     var elp = this.el.findParent('.x-form-element', 5, true);
36558                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
36559                 }
36560                 this.alignErrorIcon();
36561                 this.errorIcon.dom.qtip = msg;
36562                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
36563                 this.errorIcon.show();
36564                 this.on('resize', this.alignErrorIcon, this);
36565                 break;
36566             default:
36567                 var t = Roo.getDom(this.msgTarget);
36568                 t.innerHTML = msg;
36569                 t.style.display = this.msgDisplay;
36570                 break;
36571         }
36572         this.fireEvent('invalid', this, msg);
36573     },
36574
36575     // private
36576     alignErrorIcon : function(){
36577         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
36578     },
36579
36580     /**
36581      * Clear any invalid styles/messages for this field
36582      */
36583     clearInvalid : function(){
36584         if(!this.rendered || this.preventMark){ // not rendered
36585             return;
36586         }
36587         this.el.removeClass(this.invalidClass);
36588         switch(this.msgTarget){
36589             case 'qtip':
36590                 this.el.dom.qtip = '';
36591                 break;
36592             case 'title':
36593                 this.el.dom.title = '';
36594                 break;
36595             case 'under':
36596                 if(this.errorEl){
36597                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
36598                 }
36599                 break;
36600             case 'side':
36601                 if(this.errorIcon){
36602                     this.errorIcon.dom.qtip = '';
36603                     this.errorIcon.hide();
36604                     this.un('resize', this.alignErrorIcon, this);
36605                 }
36606                 break;
36607             default:
36608                 var t = Roo.getDom(this.msgTarget);
36609                 t.innerHTML = '';
36610                 t.style.display = 'none';
36611                 break;
36612         }
36613         this.fireEvent('valid', this);
36614     },
36615
36616     /**
36617      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
36618      * @return {Mixed} value The field value
36619      */
36620     getRawValue : function(){
36621         var v = this.el.getValue();
36622         
36623         return v;
36624     },
36625
36626     /**
36627      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
36628      * @return {Mixed} value The field value
36629      */
36630     getValue : function(){
36631         var v = this.el.getValue();
36632          
36633         return v;
36634     },
36635
36636     /**
36637      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
36638      * @param {Mixed} value The value to set
36639      */
36640     setRawValue : function(v){
36641         return this.el.dom.value = (v === null || v === undefined ? '' : v);
36642     },
36643
36644     /**
36645      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
36646      * @param {Mixed} value The value to set
36647      */
36648     setValue : function(v){
36649         this.value = v;
36650         if(this.rendered){
36651             this.el.dom.value = (v === null || v === undefined ? '' : v);
36652              this.validate();
36653         }
36654     },
36655
36656     adjustSize : function(w, h){
36657         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
36658         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
36659         return s;
36660     },
36661
36662     adjustWidth : function(tag, w){
36663         tag = tag.toLowerCase();
36664         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
36665             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
36666                 if(tag == 'input'){
36667                     return w + 2;
36668                 }
36669                 if(tag == 'textarea'){
36670                     return w-2;
36671                 }
36672             }else if(Roo.isOpera){
36673                 if(tag == 'input'){
36674                     return w + 2;
36675                 }
36676                 if(tag == 'textarea'){
36677                     return w-2;
36678                 }
36679             }
36680         }
36681         return w;
36682     }
36683 });
36684
36685
36686 // anything other than normal should be considered experimental
36687 Roo.form.Field.msgFx = {
36688     normal : {
36689         show: function(msgEl, f){
36690             msgEl.setDisplayed('block');
36691         },
36692
36693         hide : function(msgEl, f){
36694             msgEl.setDisplayed(false).update('');
36695         }
36696     },
36697
36698     slide : {
36699         show: function(msgEl, f){
36700             msgEl.slideIn('t', {stopFx:true});
36701         },
36702
36703         hide : function(msgEl, f){
36704             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36705         }
36706     },
36707
36708     slideRight : {
36709         show: function(msgEl, f){
36710             msgEl.fixDisplay();
36711             msgEl.alignTo(f.el, 'tl-tr');
36712             msgEl.slideIn('l', {stopFx:true});
36713         },
36714
36715         hide : function(msgEl, f){
36716             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36717         }
36718     }
36719 };/*
36720  * Based on:
36721  * Ext JS Library 1.1.1
36722  * Copyright(c) 2006-2007, Ext JS, LLC.
36723  *
36724  * Originally Released Under LGPL - original licence link has changed is not relivant.
36725  *
36726  * Fork - LGPL
36727  * <script type="text/javascript">
36728  */
36729  
36730
36731 /**
36732  * @class Roo.form.TextField
36733  * @extends Roo.form.Field
36734  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36735  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36736  * @constructor
36737  * Creates a new TextField
36738  * @param {Object} config Configuration options
36739  */
36740 Roo.form.TextField = function(config){
36741     Roo.form.TextField.superclass.constructor.call(this, config);
36742     this.addEvents({
36743         /**
36744          * @event autosize
36745          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36746          * according to the default logic, but this event provides a hook for the developer to apply additional
36747          * logic at runtime to resize the field if needed.
36748              * @param {Roo.form.Field} this This text field
36749              * @param {Number} width The new field width
36750              */
36751         autosize : true
36752     });
36753 };
36754
36755 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36756     /**
36757      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36758      */
36759     grow : false,
36760     /**
36761      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36762      */
36763     growMin : 30,
36764     /**
36765      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36766      */
36767     growMax : 800,
36768     /**
36769      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36770      */
36771     vtype : null,
36772     /**
36773      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36774      */
36775     maskRe : null,
36776     /**
36777      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36778      */
36779     disableKeyFilter : false,
36780     /**
36781      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36782      */
36783     allowBlank : true,
36784     /**
36785      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36786      */
36787     minLength : 0,
36788     /**
36789      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36790      */
36791     maxLength : Number.MAX_VALUE,
36792     /**
36793      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36794      */
36795     minLengthText : "The minimum length for this field is {0}",
36796     /**
36797      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36798      */
36799     maxLengthText : "The maximum length for this field is {0}",
36800     /**
36801      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36802      */
36803     selectOnFocus : false,
36804     /**
36805      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36806      */
36807     blankText : "This field is required",
36808     /**
36809      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36810      * If available, this function will be called only after the basic validators all return true, and will be passed the
36811      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36812      */
36813     validator : null,
36814     /**
36815      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36816      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36817      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36818      */
36819     regex : null,
36820     /**
36821      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36822      */
36823     regexText : "",
36824     /**
36825      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
36826      */
36827     emptyText : null,
36828    
36829
36830     // private
36831     initEvents : function()
36832     {
36833         if (this.emptyText) {
36834             this.el.attr('placeholder', this.emptyText);
36835         }
36836         
36837         Roo.form.TextField.superclass.initEvents.call(this);
36838         if(this.validationEvent == 'keyup'){
36839             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36840             this.el.on('keyup', this.filterValidation, this);
36841         }
36842         else if(this.validationEvent !== false){
36843             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36844         }
36845         
36846         if(this.selectOnFocus){
36847             this.on("focus", this.preFocus, this);
36848             
36849         }
36850         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36851             this.el.on("keypress", this.filterKeys, this);
36852         }
36853         if(this.grow){
36854             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36855             this.el.on("click", this.autoSize,  this);
36856         }
36857         if(this.el.is('input[type=password]') && Roo.isSafari){
36858             this.el.on('keydown', this.SafariOnKeyDown, this);
36859         }
36860     },
36861
36862     processValue : function(value){
36863         if(this.stripCharsRe){
36864             var newValue = value.replace(this.stripCharsRe, '');
36865             if(newValue !== value){
36866                 this.setRawValue(newValue);
36867                 return newValue;
36868             }
36869         }
36870         return value;
36871     },
36872
36873     filterValidation : function(e){
36874         if(!e.isNavKeyPress()){
36875             this.validationTask.delay(this.validationDelay);
36876         }
36877     },
36878
36879     // private
36880     onKeyUp : function(e){
36881         if(!e.isNavKeyPress()){
36882             this.autoSize();
36883         }
36884     },
36885
36886     /**
36887      * Resets the current field value to the originally-loaded value and clears any validation messages.
36888      *  
36889      */
36890     reset : function(){
36891         Roo.form.TextField.superclass.reset.call(this);
36892        
36893     },
36894
36895     
36896     // private
36897     preFocus : function(){
36898         
36899         if(this.selectOnFocus){
36900             this.el.dom.select();
36901         }
36902     },
36903
36904     
36905     // private
36906     filterKeys : function(e){
36907         var k = e.getKey();
36908         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36909             return;
36910         }
36911         var c = e.getCharCode(), cc = String.fromCharCode(c);
36912         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36913             return;
36914         }
36915         if(!this.maskRe.test(cc)){
36916             e.stopEvent();
36917         }
36918     },
36919
36920     setValue : function(v){
36921         
36922         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36923         
36924         this.autoSize();
36925     },
36926
36927     /**
36928      * Validates a value according to the field's validation rules and marks the field as invalid
36929      * if the validation fails
36930      * @param {Mixed} value The value to validate
36931      * @return {Boolean} True if the value is valid, else false
36932      */
36933     validateValue : function(value){
36934         if(value.length < 1)  { // if it's blank
36935              if(this.allowBlank){
36936                 this.clearInvalid();
36937                 return true;
36938              }else{
36939                 this.markInvalid(this.blankText);
36940                 return false;
36941              }
36942         }
36943         if(value.length < this.minLength){
36944             this.markInvalid(String.format(this.minLengthText, this.minLength));
36945             return false;
36946         }
36947         if(value.length > this.maxLength){
36948             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36949             return false;
36950         }
36951         if(this.vtype){
36952             var vt = Roo.form.VTypes;
36953             if(!vt[this.vtype](value, this)){
36954                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36955                 return false;
36956             }
36957         }
36958         if(typeof this.validator == "function"){
36959             var msg = this.validator(value);
36960             if(msg !== true){
36961                 this.markInvalid(msg);
36962                 return false;
36963             }
36964         }
36965         if(this.regex && !this.regex.test(value)){
36966             this.markInvalid(this.regexText);
36967             return false;
36968         }
36969         return true;
36970     },
36971
36972     /**
36973      * Selects text in this field
36974      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36975      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36976      */
36977     selectText : function(start, end){
36978         var v = this.getRawValue();
36979         if(v.length > 0){
36980             start = start === undefined ? 0 : start;
36981             end = end === undefined ? v.length : end;
36982             var d = this.el.dom;
36983             if(d.setSelectionRange){
36984                 d.setSelectionRange(start, end);
36985             }else if(d.createTextRange){
36986                 var range = d.createTextRange();
36987                 range.moveStart("character", start);
36988                 range.moveEnd("character", v.length-end);
36989                 range.select();
36990             }
36991         }
36992     },
36993
36994     /**
36995      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36996      * This only takes effect if grow = true, and fires the autosize event.
36997      */
36998     autoSize : function(){
36999         if(!this.grow || !this.rendered){
37000             return;
37001         }
37002         if(!this.metrics){
37003             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37004         }
37005         var el = this.el;
37006         var v = el.dom.value;
37007         var d = document.createElement('div');
37008         d.appendChild(document.createTextNode(v));
37009         v = d.innerHTML;
37010         d = null;
37011         v += "&#160;";
37012         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37013         this.el.setWidth(w);
37014         this.fireEvent("autosize", this, w);
37015     },
37016     
37017     // private
37018     SafariOnKeyDown : function(event)
37019     {
37020         // this is a workaround for a password hang bug on chrome/ webkit.
37021         
37022         var isSelectAll = false;
37023         
37024         if(this.el.dom.selectionEnd > 0){
37025             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37026         }
37027         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37028             event.preventDefault();
37029             this.setValue('');
37030             return;
37031         }
37032         
37033         if(isSelectAll){ // backspace and delete key
37034             
37035             event.preventDefault();
37036             // this is very hacky as keydown always get's upper case.
37037             //
37038             var cc = String.fromCharCode(event.getCharCode());
37039             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37040             
37041         }
37042         
37043         
37044     }
37045 });/*
37046  * Based on:
37047  * Ext JS Library 1.1.1
37048  * Copyright(c) 2006-2007, Ext JS, LLC.
37049  *
37050  * Originally Released Under LGPL - original licence link has changed is not relivant.
37051  *
37052  * Fork - LGPL
37053  * <script type="text/javascript">
37054  */
37055  
37056 /**
37057  * @class Roo.form.Hidden
37058  * @extends Roo.form.TextField
37059  * Simple Hidden element used on forms 
37060  * 
37061  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37062  * 
37063  * @constructor
37064  * Creates a new Hidden form element.
37065  * @param {Object} config Configuration options
37066  */
37067
37068
37069
37070 // easy hidden field...
37071 Roo.form.Hidden = function(config){
37072     Roo.form.Hidden.superclass.constructor.call(this, config);
37073 };
37074   
37075 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37076     fieldLabel:      '',
37077     inputType:      'hidden',
37078     width:          50,
37079     allowBlank:     true,
37080     labelSeparator: '',
37081     hidden:         true,
37082     itemCls :       'x-form-item-display-none'
37083
37084
37085 });
37086
37087
37088 /*
37089  * Based on:
37090  * Ext JS Library 1.1.1
37091  * Copyright(c) 2006-2007, Ext JS, LLC.
37092  *
37093  * Originally Released Under LGPL - original licence link has changed is not relivant.
37094  *
37095  * Fork - LGPL
37096  * <script type="text/javascript">
37097  */
37098  
37099 /**
37100  * @class Roo.form.TriggerField
37101  * @extends Roo.form.TextField
37102  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37103  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37104  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37105  * for which you can provide a custom implementation.  For example:
37106  * <pre><code>
37107 var trigger = new Roo.form.TriggerField();
37108 trigger.onTriggerClick = myTriggerFn;
37109 trigger.applyTo('my-field');
37110 </code></pre>
37111  *
37112  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37113  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37114  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37115  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37116  * @constructor
37117  * Create a new TriggerField.
37118  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37119  * to the base TextField)
37120  */
37121 Roo.form.TriggerField = function(config){
37122     this.mimicing = false;
37123     Roo.form.TriggerField.superclass.constructor.call(this, config);
37124 };
37125
37126 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37127     /**
37128      * @cfg {String} triggerClass A CSS class to apply to the trigger
37129      */
37130     /**
37131      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37132      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37133      */
37134     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37135     /**
37136      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37137      */
37138     hideTrigger:false,
37139
37140     /** @cfg {Boolean} grow @hide */
37141     /** @cfg {Number} growMin @hide */
37142     /** @cfg {Number} growMax @hide */
37143
37144     /**
37145      * @hide 
37146      * @method
37147      */
37148     autoSize: Roo.emptyFn,
37149     // private
37150     monitorTab : true,
37151     // private
37152     deferHeight : true,
37153
37154     
37155     actionMode : 'wrap',
37156     // private
37157     onResize : function(w, h){
37158         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37159         if(typeof w == 'number'){
37160             var x = w - this.trigger.getWidth();
37161             this.el.setWidth(this.adjustWidth('input', x));
37162             this.trigger.setStyle('left', x+'px');
37163         }
37164     },
37165
37166     // private
37167     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37168
37169     // private
37170     getResizeEl : function(){
37171         return this.wrap;
37172     },
37173
37174     // private
37175     getPositionEl : function(){
37176         return this.wrap;
37177     },
37178
37179     // private
37180     alignErrorIcon : function(){
37181         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37182     },
37183
37184     // private
37185     onRender : function(ct, position){
37186         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37187         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37188         this.trigger = this.wrap.createChild(this.triggerConfig ||
37189                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37190         if(this.hideTrigger){
37191             this.trigger.setDisplayed(false);
37192         }
37193         this.initTrigger();
37194         if(!this.width){
37195             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37196         }
37197     },
37198
37199     // private
37200     initTrigger : function(){
37201         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37202         this.trigger.addClassOnOver('x-form-trigger-over');
37203         this.trigger.addClassOnClick('x-form-trigger-click');
37204     },
37205
37206     // private
37207     onDestroy : function(){
37208         if(this.trigger){
37209             this.trigger.removeAllListeners();
37210             this.trigger.remove();
37211         }
37212         if(this.wrap){
37213             this.wrap.remove();
37214         }
37215         Roo.form.TriggerField.superclass.onDestroy.call(this);
37216     },
37217
37218     // private
37219     onFocus : function(){
37220         Roo.form.TriggerField.superclass.onFocus.call(this);
37221         if(!this.mimicing){
37222             this.wrap.addClass('x-trigger-wrap-focus');
37223             this.mimicing = true;
37224             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37225             if(this.monitorTab){
37226                 this.el.on("keydown", this.checkTab, this);
37227             }
37228         }
37229     },
37230
37231     // private
37232     checkTab : function(e){
37233         if(e.getKey() == e.TAB){
37234             this.triggerBlur();
37235         }
37236     },
37237
37238     // private
37239     onBlur : function(){
37240         // do nothing
37241     },
37242
37243     // private
37244     mimicBlur : function(e, t){
37245         if(!this.wrap.contains(t) && this.validateBlur()){
37246             this.triggerBlur();
37247         }
37248     },
37249
37250     // private
37251     triggerBlur : function(){
37252         this.mimicing = false;
37253         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37254         if(this.monitorTab){
37255             this.el.un("keydown", this.checkTab, this);
37256         }
37257         this.wrap.removeClass('x-trigger-wrap-focus');
37258         Roo.form.TriggerField.superclass.onBlur.call(this);
37259     },
37260
37261     // private
37262     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37263     validateBlur : function(e, t){
37264         return true;
37265     },
37266
37267     // private
37268     onDisable : function(){
37269         Roo.form.TriggerField.superclass.onDisable.call(this);
37270         if(this.wrap){
37271             this.wrap.addClass('x-item-disabled');
37272         }
37273     },
37274
37275     // private
37276     onEnable : function(){
37277         Roo.form.TriggerField.superclass.onEnable.call(this);
37278         if(this.wrap){
37279             this.wrap.removeClass('x-item-disabled');
37280         }
37281     },
37282
37283     // private
37284     onShow : function(){
37285         var ae = this.getActionEl();
37286         
37287         if(ae){
37288             ae.dom.style.display = '';
37289             ae.dom.style.visibility = 'visible';
37290         }
37291     },
37292
37293     // private
37294     
37295     onHide : function(){
37296         var ae = this.getActionEl();
37297         ae.dom.style.display = 'none';
37298     },
37299
37300     /**
37301      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37302      * by an implementing function.
37303      * @method
37304      * @param {EventObject} e
37305      */
37306     onTriggerClick : Roo.emptyFn
37307 });
37308
37309 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37310 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37311 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37312 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37313     initComponent : function(){
37314         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37315
37316         this.triggerConfig = {
37317             tag:'span', cls:'x-form-twin-triggers', cn:[
37318             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37319             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37320         ]};
37321     },
37322
37323     getTrigger : function(index){
37324         return this.triggers[index];
37325     },
37326
37327     initTrigger : function(){
37328         var ts = this.trigger.select('.x-form-trigger', true);
37329         this.wrap.setStyle('overflow', 'hidden');
37330         var triggerField = this;
37331         ts.each(function(t, all, index){
37332             t.hide = function(){
37333                 var w = triggerField.wrap.getWidth();
37334                 this.dom.style.display = 'none';
37335                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37336             };
37337             t.show = function(){
37338                 var w = triggerField.wrap.getWidth();
37339                 this.dom.style.display = '';
37340                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37341             };
37342             var triggerIndex = 'Trigger'+(index+1);
37343
37344             if(this['hide'+triggerIndex]){
37345                 t.dom.style.display = 'none';
37346             }
37347             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37348             t.addClassOnOver('x-form-trigger-over');
37349             t.addClassOnClick('x-form-trigger-click');
37350         }, this);
37351         this.triggers = ts.elements;
37352     },
37353
37354     onTrigger1Click : Roo.emptyFn,
37355     onTrigger2Click : Roo.emptyFn
37356 });/*
37357  * Based on:
37358  * Ext JS Library 1.1.1
37359  * Copyright(c) 2006-2007, Ext JS, LLC.
37360  *
37361  * Originally Released Under LGPL - original licence link has changed is not relivant.
37362  *
37363  * Fork - LGPL
37364  * <script type="text/javascript">
37365  */
37366  
37367 /**
37368  * @class Roo.form.TextArea
37369  * @extends Roo.form.TextField
37370  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
37371  * support for auto-sizing.
37372  * @constructor
37373  * Creates a new TextArea
37374  * @param {Object} config Configuration options
37375  */
37376 Roo.form.TextArea = function(config){
37377     Roo.form.TextArea.superclass.constructor.call(this, config);
37378     // these are provided exchanges for backwards compat
37379     // minHeight/maxHeight were replaced by growMin/growMax to be
37380     // compatible with TextField growing config values
37381     if(this.minHeight !== undefined){
37382         this.growMin = this.minHeight;
37383     }
37384     if(this.maxHeight !== undefined){
37385         this.growMax = this.maxHeight;
37386     }
37387 };
37388
37389 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
37390     /**
37391      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
37392      */
37393     growMin : 60,
37394     /**
37395      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
37396      */
37397     growMax: 1000,
37398     /**
37399      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
37400      * in the field (equivalent to setting overflow: hidden, defaults to false)
37401      */
37402     preventScrollbars: false,
37403     /**
37404      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37405      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
37406      */
37407
37408     // private
37409     onRender : function(ct, position){
37410         if(!this.el){
37411             this.defaultAutoCreate = {
37412                 tag: "textarea",
37413                 style:"width:300px;height:60px;",
37414                 autocomplete: "off"
37415             };
37416         }
37417         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
37418         if(this.grow){
37419             this.textSizeEl = Roo.DomHelper.append(document.body, {
37420                 tag: "pre", cls: "x-form-grow-sizer"
37421             });
37422             if(this.preventScrollbars){
37423                 this.el.setStyle("overflow", "hidden");
37424             }
37425             this.el.setHeight(this.growMin);
37426         }
37427     },
37428
37429     onDestroy : function(){
37430         if(this.textSizeEl){
37431             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
37432         }
37433         Roo.form.TextArea.superclass.onDestroy.call(this);
37434     },
37435
37436     // private
37437     onKeyUp : function(e){
37438         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
37439             this.autoSize();
37440         }
37441     },
37442
37443     /**
37444      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
37445      * This only takes effect if grow = true, and fires the autosize event if the height changes.
37446      */
37447     autoSize : function(){
37448         if(!this.grow || !this.textSizeEl){
37449             return;
37450         }
37451         var el = this.el;
37452         var v = el.dom.value;
37453         var ts = this.textSizeEl;
37454
37455         ts.innerHTML = '';
37456         ts.appendChild(document.createTextNode(v));
37457         v = ts.innerHTML;
37458
37459         Roo.fly(ts).setWidth(this.el.getWidth());
37460         if(v.length < 1){
37461             v = "&#160;&#160;";
37462         }else{
37463             if(Roo.isIE){
37464                 v = v.replace(/\n/g, '<p>&#160;</p>');
37465             }
37466             v += "&#160;\n&#160;";
37467         }
37468         ts.innerHTML = v;
37469         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
37470         if(h != this.lastHeight){
37471             this.lastHeight = h;
37472             this.el.setHeight(h);
37473             this.fireEvent("autosize", this, h);
37474         }
37475     }
37476 });/*
37477  * Based on:
37478  * Ext JS Library 1.1.1
37479  * Copyright(c) 2006-2007, Ext JS, LLC.
37480  *
37481  * Originally Released Under LGPL - original licence link has changed is not relivant.
37482  *
37483  * Fork - LGPL
37484  * <script type="text/javascript">
37485  */
37486  
37487
37488 /**
37489  * @class Roo.form.NumberField
37490  * @extends Roo.form.TextField
37491  * Numeric text field that provides automatic keystroke filtering and numeric validation.
37492  * @constructor
37493  * Creates a new NumberField
37494  * @param {Object} config Configuration options
37495  */
37496 Roo.form.NumberField = function(config){
37497     Roo.form.NumberField.superclass.constructor.call(this, config);
37498 };
37499
37500 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
37501     /**
37502      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
37503      */
37504     fieldClass: "x-form-field x-form-num-field",
37505     /**
37506      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
37507      */
37508     allowDecimals : true,
37509     /**
37510      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
37511      */
37512     decimalSeparator : ".",
37513     /**
37514      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
37515      */
37516     decimalPrecision : 2,
37517     /**
37518      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
37519      */
37520     allowNegative : true,
37521     /**
37522      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
37523      */
37524     minValue : Number.NEGATIVE_INFINITY,
37525     /**
37526      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
37527      */
37528     maxValue : Number.MAX_VALUE,
37529     /**
37530      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
37531      */
37532     minText : "The minimum value for this field is {0}",
37533     /**
37534      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37535      */
37536     maxText : "The maximum value for this field is {0}",
37537     /**
37538      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
37539      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37540      */
37541     nanText : "{0} is not a valid number",
37542
37543     // private
37544     initEvents : function(){
37545         Roo.form.NumberField.superclass.initEvents.call(this);
37546         var allowed = "0123456789";
37547         if(this.allowDecimals){
37548             allowed += this.decimalSeparator;
37549         }
37550         if(this.allowNegative){
37551             allowed += "-";
37552         }
37553         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37554         var keyPress = function(e){
37555             var k = e.getKey();
37556             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37557                 return;
37558             }
37559             var c = e.getCharCode();
37560             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37561                 e.stopEvent();
37562             }
37563         };
37564         this.el.on("keypress", keyPress, this);
37565     },
37566
37567     // private
37568     validateValue : function(value){
37569         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
37570             return false;
37571         }
37572         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37573              return true;
37574         }
37575         var num = this.parseValue(value);
37576         if(isNaN(num)){
37577             this.markInvalid(String.format(this.nanText, value));
37578             return false;
37579         }
37580         if(num < this.minValue){
37581             this.markInvalid(String.format(this.minText, this.minValue));
37582             return false;
37583         }
37584         if(num > this.maxValue){
37585             this.markInvalid(String.format(this.maxText, this.maxValue));
37586             return false;
37587         }
37588         return true;
37589     },
37590
37591     getValue : function(){
37592         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
37593     },
37594
37595     // private
37596     parseValue : function(value){
37597         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37598         return isNaN(value) ? '' : value;
37599     },
37600
37601     // private
37602     fixPrecision : function(value){
37603         var nan = isNaN(value);
37604         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37605             return nan ? '' : value;
37606         }
37607         return parseFloat(value).toFixed(this.decimalPrecision);
37608     },
37609
37610     setValue : function(v){
37611         v = this.fixPrecision(v);
37612         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
37613     },
37614
37615     // private
37616     decimalPrecisionFcn : function(v){
37617         return Math.floor(v);
37618     },
37619
37620     beforeBlur : function(){
37621         var v = this.parseValue(this.getRawValue());
37622         if(v){
37623             this.setValue(v);
37624         }
37625     }
37626 });/*
37627  * Based on:
37628  * Ext JS Library 1.1.1
37629  * Copyright(c) 2006-2007, Ext JS, LLC.
37630  *
37631  * Originally Released Under LGPL - original licence link has changed is not relivant.
37632  *
37633  * Fork - LGPL
37634  * <script type="text/javascript">
37635  */
37636  
37637 /**
37638  * @class Roo.form.DateField
37639  * @extends Roo.form.TriggerField
37640  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37641 * @constructor
37642 * Create a new DateField
37643 * @param {Object} config
37644  */
37645 Roo.form.DateField = function(config){
37646     Roo.form.DateField.superclass.constructor.call(this, config);
37647     
37648       this.addEvents({
37649          
37650         /**
37651          * @event select
37652          * Fires when a date is selected
37653              * @param {Roo.form.DateField} combo This combo box
37654              * @param {Date} date The date selected
37655              */
37656         'select' : true
37657          
37658     });
37659     
37660     
37661     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
37662     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
37663     this.ddMatch = null;
37664     if(this.disabledDates){
37665         var dd = this.disabledDates;
37666         var re = "(?:";
37667         for(var i = 0; i < dd.length; i++){
37668             re += dd[i];
37669             if(i != dd.length-1) re += "|";
37670         }
37671         this.ddMatch = new RegExp(re + ")");
37672     }
37673 };
37674
37675 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
37676     /**
37677      * @cfg {String} format
37678      * The default date format string which can be overriden for localization support.  The format must be
37679      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
37680      */
37681     format : "m/d/y",
37682     /**
37683      * @cfg {String} altFormats
37684      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
37685      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
37686      */
37687     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
37688     /**
37689      * @cfg {Array} disabledDays
37690      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
37691      */
37692     disabledDays : null,
37693     /**
37694      * @cfg {String} disabledDaysText
37695      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
37696      */
37697     disabledDaysText : "Disabled",
37698     /**
37699      * @cfg {Array} disabledDates
37700      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
37701      * expression so they are very powerful. Some examples:
37702      * <ul>
37703      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37704      * <li>["03/08", "09/16"] would disable those days for every year</li>
37705      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37706      * <li>["03/../2006"] would disable every day in March 2006</li>
37707      * <li>["^03"] would disable every day in every March</li>
37708      * </ul>
37709      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37710      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37711      */
37712     disabledDates : null,
37713     /**
37714      * @cfg {String} disabledDatesText
37715      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37716      */
37717     disabledDatesText : "Disabled",
37718     /**
37719      * @cfg {Date/String} minValue
37720      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37721      * valid format (defaults to null).
37722      */
37723     minValue : null,
37724     /**
37725      * @cfg {Date/String} maxValue
37726      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37727      * valid format (defaults to null).
37728      */
37729     maxValue : null,
37730     /**
37731      * @cfg {String} minText
37732      * The error text to display when the date in the cell is before minValue (defaults to
37733      * 'The date in this field must be after {minValue}').
37734      */
37735     minText : "The date in this field must be equal to or after {0}",
37736     /**
37737      * @cfg {String} maxText
37738      * The error text to display when the date in the cell is after maxValue (defaults to
37739      * 'The date in this field must be before {maxValue}').
37740      */
37741     maxText : "The date in this field must be equal to or before {0}",
37742     /**
37743      * @cfg {String} invalidText
37744      * The error text to display when the date in the field is invalid (defaults to
37745      * '{value} is not a valid date - it must be in the format {format}').
37746      */
37747     invalidText : "{0} is not a valid date - it must be in the format {1}",
37748     /**
37749      * @cfg {String} triggerClass
37750      * An additional CSS class used to style the trigger button.  The trigger will always get the
37751      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37752      * which displays a calendar icon).
37753      */
37754     triggerClass : 'x-form-date-trigger',
37755     
37756
37757     /**
37758      * @cfg {Boolean} useIso
37759      * if enabled, then the date field will use a hidden field to store the 
37760      * real value as iso formated date. default (false)
37761      */ 
37762     useIso : false,
37763     /**
37764      * @cfg {String/Object} autoCreate
37765      * A DomHelper element spec, or true for a default element spec (defaults to
37766      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37767      */ 
37768     // private
37769     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37770     
37771     // private
37772     hiddenField: false,
37773     
37774     onRender : function(ct, position)
37775     {
37776         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37777         if (this.useIso) {
37778             //this.el.dom.removeAttribute('name'); 
37779             Roo.log("Changing name?");
37780             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
37781             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37782                     'before', true);
37783             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
37784             // prevent input submission
37785             this.hiddenName = this.name;
37786         }
37787             
37788             
37789     },
37790     
37791     // private
37792     validateValue : function(value)
37793     {
37794         value = this.formatDate(value);
37795         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37796             Roo.log('super failed');
37797             return false;
37798         }
37799         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37800              return true;
37801         }
37802         var svalue = value;
37803         value = this.parseDate(value);
37804         if(!value){
37805             Roo.log('parse date failed' + svalue);
37806             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37807             return false;
37808         }
37809         var time = value.getTime();
37810         if(this.minValue && time < this.minValue.getTime()){
37811             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37812             return false;
37813         }
37814         if(this.maxValue && time > this.maxValue.getTime()){
37815             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37816             return false;
37817         }
37818         if(this.disabledDays){
37819             var day = value.getDay();
37820             for(var i = 0; i < this.disabledDays.length; i++) {
37821                 if(day === this.disabledDays[i]){
37822                     this.markInvalid(this.disabledDaysText);
37823                     return false;
37824                 }
37825             }
37826         }
37827         var fvalue = this.formatDate(value);
37828         if(this.ddMatch && this.ddMatch.test(fvalue)){
37829             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37830             return false;
37831         }
37832         return true;
37833     },
37834
37835     // private
37836     // Provides logic to override the default TriggerField.validateBlur which just returns true
37837     validateBlur : function(){
37838         return !this.menu || !this.menu.isVisible();
37839     },
37840     
37841     getName: function()
37842     {
37843         // returns hidden if it's set..
37844         if (!this.rendered) {return ''};
37845         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
37846         
37847     },
37848
37849     /**
37850      * Returns the current date value of the date field.
37851      * @return {Date} The date value
37852      */
37853     getValue : function(){
37854         
37855         return  this.hiddenField ?
37856                 this.hiddenField.value :
37857                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37858     },
37859
37860     /**
37861      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37862      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37863      * (the default format used is "m/d/y").
37864      * <br />Usage:
37865      * <pre><code>
37866 //All of these calls set the same date value (May 4, 2006)
37867
37868 //Pass a date object:
37869 var dt = new Date('5/4/06');
37870 dateField.setValue(dt);
37871
37872 //Pass a date string (default format):
37873 dateField.setValue('5/4/06');
37874
37875 //Pass a date string (custom format):
37876 dateField.format = 'Y-m-d';
37877 dateField.setValue('2006-5-4');
37878 </code></pre>
37879      * @param {String/Date} date The date or valid date string
37880      */
37881     setValue : function(date){
37882         if (this.hiddenField) {
37883             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37884         }
37885         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37886         // make sure the value field is always stored as a date..
37887         this.value = this.parseDate(date);
37888         
37889         
37890     },
37891
37892     // private
37893     parseDate : function(value){
37894         if(!value || value instanceof Date){
37895             return value;
37896         }
37897         var v = Date.parseDate(value, this.format);
37898          if (!v && this.useIso) {
37899             v = Date.parseDate(value, 'Y-m-d');
37900         }
37901         if(!v && this.altFormats){
37902             if(!this.altFormatsArray){
37903                 this.altFormatsArray = this.altFormats.split("|");
37904             }
37905             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37906                 v = Date.parseDate(value, this.altFormatsArray[i]);
37907             }
37908         }
37909         return v;
37910     },
37911
37912     // private
37913     formatDate : function(date, fmt){
37914         return (!date || !(date instanceof Date)) ?
37915                date : date.dateFormat(fmt || this.format);
37916     },
37917
37918     // private
37919     menuListeners : {
37920         select: function(m, d){
37921             
37922             this.setValue(d);
37923             this.fireEvent('select', this, d);
37924         },
37925         show : function(){ // retain focus styling
37926             this.onFocus();
37927         },
37928         hide : function(){
37929             this.focus.defer(10, this);
37930             var ml = this.menuListeners;
37931             this.menu.un("select", ml.select,  this);
37932             this.menu.un("show", ml.show,  this);
37933             this.menu.un("hide", ml.hide,  this);
37934         }
37935     },
37936
37937     // private
37938     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37939     onTriggerClick : function(){
37940         if(this.disabled){
37941             return;
37942         }
37943         if(this.menu == null){
37944             this.menu = new Roo.menu.DateMenu();
37945         }
37946         Roo.apply(this.menu.picker,  {
37947             showClear: this.allowBlank,
37948             minDate : this.minValue,
37949             maxDate : this.maxValue,
37950             disabledDatesRE : this.ddMatch,
37951             disabledDatesText : this.disabledDatesText,
37952             disabledDays : this.disabledDays,
37953             disabledDaysText : this.disabledDaysText,
37954             format : this.useIso ? 'Y-m-d' : this.format,
37955             minText : String.format(this.minText, this.formatDate(this.minValue)),
37956             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37957         });
37958         this.menu.on(Roo.apply({}, this.menuListeners, {
37959             scope:this
37960         }));
37961         this.menu.picker.setValue(this.getValue() || new Date());
37962         this.menu.show(this.el, "tl-bl?");
37963     },
37964
37965     beforeBlur : function(){
37966         var v = this.parseDate(this.getRawValue());
37967         if(v){
37968             this.setValue(v);
37969         }
37970     }
37971
37972     /** @cfg {Boolean} grow @hide */
37973     /** @cfg {Number} growMin @hide */
37974     /** @cfg {Number} growMax @hide */
37975     /**
37976      * @hide
37977      * @method autoSize
37978      */
37979 });/*
37980  * Based on:
37981  * Ext JS Library 1.1.1
37982  * Copyright(c) 2006-2007, Ext JS, LLC.
37983  *
37984  * Originally Released Under LGPL - original licence link has changed is not relivant.
37985  *
37986  * Fork - LGPL
37987  * <script type="text/javascript">
37988  */
37989  
37990 /**
37991  * @class Roo.form.MonthField
37992  * @extends Roo.form.TriggerField
37993  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
37994 * @constructor
37995 * Create a new MonthField
37996 * @param {Object} config
37997  */
37998 Roo.form.MonthField = function(config){
37999     
38000     Roo.form.MonthField.superclass.constructor.call(this, config);
38001     
38002       this.addEvents({
38003          
38004         /**
38005          * @event select
38006          * Fires when a date is selected
38007              * @param {Roo.form.MonthFieeld} combo This combo box
38008              * @param {Date} date The date selected
38009              */
38010         'select' : true
38011          
38012     });
38013     
38014     
38015     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38016     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38017     this.ddMatch = null;
38018     if(this.disabledDates){
38019         var dd = this.disabledDates;
38020         var re = "(?:";
38021         for(var i = 0; i < dd.length; i++){
38022             re += dd[i];
38023             if(i != dd.length-1) re += "|";
38024         }
38025         this.ddMatch = new RegExp(re + ")");
38026     }
38027 };
38028
38029 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38030     /**
38031      * @cfg {String} format
38032      * The default date format string which can be overriden for localization support.  The format must be
38033      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38034      */
38035     format : "M Y",
38036     /**
38037      * @cfg {String} altFormats
38038      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38039      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38040      */
38041     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38042     /**
38043      * @cfg {Array} disabledDays
38044      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38045      */
38046     disabledDays : [0,1,2,3,4,5,6],
38047     /**
38048      * @cfg {String} disabledDaysText
38049      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38050      */
38051     disabledDaysText : "Disabled",
38052     /**
38053      * @cfg {Array} disabledDates
38054      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38055      * expression so they are very powerful. Some examples:
38056      * <ul>
38057      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38058      * <li>["03/08", "09/16"] would disable those days for every year</li>
38059      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38060      * <li>["03/../2006"] would disable every day in March 2006</li>
38061      * <li>["^03"] would disable every day in every March</li>
38062      * </ul>
38063      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38064      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38065      */
38066     disabledDates : null,
38067     /**
38068      * @cfg {String} disabledDatesText
38069      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38070      */
38071     disabledDatesText : "Disabled",
38072     /**
38073      * @cfg {Date/String} minValue
38074      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38075      * valid format (defaults to null).
38076      */
38077     minValue : null,
38078     /**
38079      * @cfg {Date/String} maxValue
38080      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38081      * valid format (defaults to null).
38082      */
38083     maxValue : null,
38084     /**
38085      * @cfg {String} minText
38086      * The error text to display when the date in the cell is before minValue (defaults to
38087      * 'The date in this field must be after {minValue}').
38088      */
38089     minText : "The date in this field must be equal to or after {0}",
38090     /**
38091      * @cfg {String} maxTextf
38092      * The error text to display when the date in the cell is after maxValue (defaults to
38093      * 'The date in this field must be before {maxValue}').
38094      */
38095     maxText : "The date in this field must be equal to or before {0}",
38096     /**
38097      * @cfg {String} invalidText
38098      * The error text to display when the date in the field is invalid (defaults to
38099      * '{value} is not a valid date - it must be in the format {format}').
38100      */
38101     invalidText : "{0} is not a valid date - it must be in the format {1}",
38102     /**
38103      * @cfg {String} triggerClass
38104      * An additional CSS class used to style the trigger button.  The trigger will always get the
38105      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38106      * which displays a calendar icon).
38107      */
38108     triggerClass : 'x-form-date-trigger',
38109     
38110
38111     /**
38112      * @cfg {Boolean} useIso
38113      * if enabled, then the date field will use a hidden field to store the 
38114      * real value as iso formated date. default (true)
38115      */ 
38116     useIso : true,
38117     /**
38118      * @cfg {String/Object} autoCreate
38119      * A DomHelper element spec, or true for a default element spec (defaults to
38120      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38121      */ 
38122     // private
38123     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38124     
38125     // private
38126     hiddenField: false,
38127     
38128     hideMonthPicker : false,
38129     
38130     onRender : function(ct, position)
38131     {
38132         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38133         if (this.useIso) {
38134             this.el.dom.removeAttribute('name'); 
38135             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38136                     'before', true);
38137             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38138             // prevent input submission
38139             this.hiddenName = this.name;
38140         }
38141             
38142             
38143     },
38144     
38145     // private
38146     validateValue : function(value)
38147     {
38148         value = this.formatDate(value);
38149         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38150             return false;
38151         }
38152         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38153              return true;
38154         }
38155         var svalue = value;
38156         value = this.parseDate(value);
38157         if(!value){
38158             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38159             return false;
38160         }
38161         var time = value.getTime();
38162         if(this.minValue && time < this.minValue.getTime()){
38163             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38164             return false;
38165         }
38166         if(this.maxValue && time > this.maxValue.getTime()){
38167             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38168             return false;
38169         }
38170         /*if(this.disabledDays){
38171             var day = value.getDay();
38172             for(var i = 0; i < this.disabledDays.length; i++) {
38173                 if(day === this.disabledDays[i]){
38174                     this.markInvalid(this.disabledDaysText);
38175                     return false;
38176                 }
38177             }
38178         }
38179         */
38180         var fvalue = this.formatDate(value);
38181         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38182             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38183             return false;
38184         }
38185         */
38186         return true;
38187     },
38188
38189     // private
38190     // Provides logic to override the default TriggerField.validateBlur which just returns true
38191     validateBlur : function(){
38192         return !this.menu || !this.menu.isVisible();
38193     },
38194
38195     /**
38196      * Returns the current date value of the date field.
38197      * @return {Date} The date value
38198      */
38199     getValue : function(){
38200         
38201         
38202         
38203         return  this.hiddenField ?
38204                 this.hiddenField.value :
38205                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38206     },
38207
38208     /**
38209      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38210      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38211      * (the default format used is "m/d/y").
38212      * <br />Usage:
38213      * <pre><code>
38214 //All of these calls set the same date value (May 4, 2006)
38215
38216 //Pass a date object:
38217 var dt = new Date('5/4/06');
38218 monthField.setValue(dt);
38219
38220 //Pass a date string (default format):
38221 monthField.setValue('5/4/06');
38222
38223 //Pass a date string (custom format):
38224 monthField.format = 'Y-m-d';
38225 monthField.setValue('2006-5-4');
38226 </code></pre>
38227      * @param {String/Date} date The date or valid date string
38228      */
38229     setValue : function(date){
38230         Roo.log('month setValue' + date);
38231         // can only be first of month..
38232         
38233         var val = this.parseDate(date);
38234         
38235         if (this.hiddenField) {
38236             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38237         }
38238         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38239         this.value = this.parseDate(date);
38240     },
38241
38242     // private
38243     parseDate : function(value){
38244         if(!value || value instanceof Date){
38245             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38246             return value;
38247         }
38248         var v = Date.parseDate(value, this.format);
38249         if (!v && this.useIso) {
38250             v = Date.parseDate(value, 'Y-m-d');
38251         }
38252         if (v) {
38253             // 
38254             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38255         }
38256         
38257         
38258         if(!v && this.altFormats){
38259             if(!this.altFormatsArray){
38260                 this.altFormatsArray = this.altFormats.split("|");
38261             }
38262             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38263                 v = Date.parseDate(value, this.altFormatsArray[i]);
38264             }
38265         }
38266         return v;
38267     },
38268
38269     // private
38270     formatDate : function(date, fmt){
38271         return (!date || !(date instanceof Date)) ?
38272                date : date.dateFormat(fmt || this.format);
38273     },
38274
38275     // private
38276     menuListeners : {
38277         select: function(m, d){
38278             this.setValue(d);
38279             this.fireEvent('select', this, d);
38280         },
38281         show : function(){ // retain focus styling
38282             this.onFocus();
38283         },
38284         hide : function(){
38285             this.focus.defer(10, this);
38286             var ml = this.menuListeners;
38287             this.menu.un("select", ml.select,  this);
38288             this.menu.un("show", ml.show,  this);
38289             this.menu.un("hide", ml.hide,  this);
38290         }
38291     },
38292     // private
38293     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38294     onTriggerClick : function(){
38295         if(this.disabled){
38296             return;
38297         }
38298         if(this.menu == null){
38299             this.menu = new Roo.menu.DateMenu();
38300            
38301         }
38302         
38303         Roo.apply(this.menu.picker,  {
38304             
38305             showClear: this.allowBlank,
38306             minDate : this.minValue,
38307             maxDate : this.maxValue,
38308             disabledDatesRE : this.ddMatch,
38309             disabledDatesText : this.disabledDatesText,
38310             
38311             format : this.useIso ? 'Y-m-d' : this.format,
38312             minText : String.format(this.minText, this.formatDate(this.minValue)),
38313             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38314             
38315         });
38316          this.menu.on(Roo.apply({}, this.menuListeners, {
38317             scope:this
38318         }));
38319        
38320         
38321         var m = this.menu;
38322         var p = m.picker;
38323         
38324         // hide month picker get's called when we called by 'before hide';
38325         
38326         var ignorehide = true;
38327         p.hideMonthPicker  = function(disableAnim){
38328             if (ignorehide) {
38329                 return;
38330             }
38331              if(this.monthPicker){
38332                 Roo.log("hideMonthPicker called");
38333                 if(disableAnim === true){
38334                     this.monthPicker.hide();
38335                 }else{
38336                     this.monthPicker.slideOut('t', {duration:.2});
38337                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38338                     p.fireEvent("select", this, this.value);
38339                     m.hide();
38340                 }
38341             }
38342         }
38343         
38344         Roo.log('picker set value');
38345         Roo.log(this.getValue());
38346         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
38347         m.show(this.el, 'tl-bl?');
38348         ignorehide  = false;
38349         // this will trigger hideMonthPicker..
38350         
38351         
38352         // hidden the day picker
38353         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
38354         
38355         
38356         
38357       
38358         
38359         p.showMonthPicker.defer(100, p);
38360     
38361         
38362        
38363     },
38364
38365     beforeBlur : function(){
38366         var v = this.parseDate(this.getRawValue());
38367         if(v){
38368             this.setValue(v);
38369         }
38370     }
38371
38372     /** @cfg {Boolean} grow @hide */
38373     /** @cfg {Number} growMin @hide */
38374     /** @cfg {Number} growMax @hide */
38375     /**
38376      * @hide
38377      * @method autoSize
38378      */
38379 });/*
38380  * Based on:
38381  * Ext JS Library 1.1.1
38382  * Copyright(c) 2006-2007, Ext JS, LLC.
38383  *
38384  * Originally Released Under LGPL - original licence link has changed is not relivant.
38385  *
38386  * Fork - LGPL
38387  * <script type="text/javascript">
38388  */
38389  
38390
38391 /**
38392  * @class Roo.form.ComboBox
38393  * @extends Roo.form.TriggerField
38394  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
38395  * @constructor
38396  * Create a new ComboBox.
38397  * @param {Object} config Configuration options
38398  */
38399 Roo.form.ComboBox = function(config){
38400     Roo.form.ComboBox.superclass.constructor.call(this, config);
38401     this.addEvents({
38402         /**
38403          * @event expand
38404          * Fires when the dropdown list is expanded
38405              * @param {Roo.form.ComboBox} combo This combo box
38406              */
38407         'expand' : true,
38408         /**
38409          * @event collapse
38410          * Fires when the dropdown list is collapsed
38411              * @param {Roo.form.ComboBox} combo This combo box
38412              */
38413         'collapse' : true,
38414         /**
38415          * @event beforeselect
38416          * Fires before a list item is selected. Return false to cancel the selection.
38417              * @param {Roo.form.ComboBox} combo This combo box
38418              * @param {Roo.data.Record} record The data record returned from the underlying store
38419              * @param {Number} index The index of the selected item in the dropdown list
38420              */
38421         'beforeselect' : true,
38422         /**
38423          * @event select
38424          * Fires when a list item is selected
38425              * @param {Roo.form.ComboBox} combo This combo box
38426              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
38427              * @param {Number} index The index of the selected item in the dropdown list
38428              */
38429         'select' : true,
38430         /**
38431          * @event beforequery
38432          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
38433          * The event object passed has these properties:
38434              * @param {Roo.form.ComboBox} combo This combo box
38435              * @param {String} query The query
38436              * @param {Boolean} forceAll true to force "all" query
38437              * @param {Boolean} cancel true to cancel the query
38438              * @param {Object} e The query event object
38439              */
38440         'beforequery': true,
38441          /**
38442          * @event add
38443          * Fires when the 'add' icon is pressed (add a listener to enable add button)
38444              * @param {Roo.form.ComboBox} combo This combo box
38445              */
38446         'add' : true,
38447         /**
38448          * @event edit
38449          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
38450              * @param {Roo.form.ComboBox} combo This combo box
38451              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
38452              */
38453         'edit' : true
38454         
38455         
38456     });
38457     if(this.transform){
38458         this.allowDomMove = false;
38459         var s = Roo.getDom(this.transform);
38460         if(!this.hiddenName){
38461             this.hiddenName = s.name;
38462         }
38463         if(!this.store){
38464             this.mode = 'local';
38465             var d = [], opts = s.options;
38466             for(var i = 0, len = opts.length;i < len; i++){
38467                 var o = opts[i];
38468                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
38469                 if(o.selected) {
38470                     this.value = value;
38471                 }
38472                 d.push([value, o.text]);
38473             }
38474             this.store = new Roo.data.SimpleStore({
38475                 'id': 0,
38476                 fields: ['value', 'text'],
38477                 data : d
38478             });
38479             this.valueField = 'value';
38480             this.displayField = 'text';
38481         }
38482         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
38483         if(!this.lazyRender){
38484             this.target = true;
38485             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
38486             s.parentNode.removeChild(s); // remove it
38487             this.render(this.el.parentNode);
38488         }else{
38489             s.parentNode.removeChild(s); // remove it
38490         }
38491
38492     }
38493     if (this.store) {
38494         this.store = Roo.factory(this.store, Roo.data);
38495     }
38496     
38497     this.selectedIndex = -1;
38498     if(this.mode == 'local'){
38499         if(config.queryDelay === undefined){
38500             this.queryDelay = 10;
38501         }
38502         if(config.minChars === undefined){
38503             this.minChars = 0;
38504         }
38505     }
38506 };
38507
38508 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
38509     /**
38510      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
38511      */
38512     /**
38513      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
38514      * rendering into an Roo.Editor, defaults to false)
38515      */
38516     /**
38517      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
38518      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
38519      */
38520     /**
38521      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
38522      */
38523     /**
38524      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
38525      * the dropdown list (defaults to undefined, with no header element)
38526      */
38527
38528      /**
38529      * @cfg {String/Roo.Template} tpl The template to use to render the output
38530      */
38531      
38532     // private
38533     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
38534     /**
38535      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
38536      */
38537     listWidth: undefined,
38538     /**
38539      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
38540      * mode = 'remote' or 'text' if mode = 'local')
38541      */
38542     displayField: undefined,
38543     /**
38544      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
38545      * mode = 'remote' or 'value' if mode = 'local'). 
38546      * Note: use of a valueField requires the user make a selection
38547      * in order for a value to be mapped.
38548      */
38549     valueField: undefined,
38550     
38551     
38552     /**
38553      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
38554      * field's data value (defaults to the underlying DOM element's name)
38555      */
38556     hiddenName: undefined,
38557     /**
38558      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
38559      */
38560     listClass: '',
38561     /**
38562      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
38563      */
38564     selectedClass: 'x-combo-selected',
38565     /**
38566      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
38567      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
38568      * which displays a downward arrow icon).
38569      */
38570     triggerClass : 'x-form-arrow-trigger',
38571     /**
38572      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
38573      */
38574     shadow:'sides',
38575     /**
38576      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
38577      * anchor positions (defaults to 'tl-bl')
38578      */
38579     listAlign: 'tl-bl?',
38580     /**
38581      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
38582      */
38583     maxHeight: 300,
38584     /**
38585      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
38586      * query specified by the allQuery config option (defaults to 'query')
38587      */
38588     triggerAction: 'query',
38589     /**
38590      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
38591      * (defaults to 4, does not apply if editable = false)
38592      */
38593     minChars : 4,
38594     /**
38595      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
38596      * delay (typeAheadDelay) if it matches a known value (defaults to false)
38597      */
38598     typeAhead: false,
38599     /**
38600      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
38601      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
38602      */
38603     queryDelay: 500,
38604     /**
38605      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
38606      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
38607      */
38608     pageSize: 0,
38609     /**
38610      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
38611      * when editable = true (defaults to false)
38612      */
38613     selectOnFocus:false,
38614     /**
38615      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
38616      */
38617     queryParam: 'query',
38618     /**
38619      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
38620      * when mode = 'remote' (defaults to 'Loading...')
38621      */
38622     loadingText: 'Loading...',
38623     /**
38624      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
38625      */
38626     resizable: false,
38627     /**
38628      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
38629      */
38630     handleHeight : 8,
38631     /**
38632      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
38633      * traditional select (defaults to true)
38634      */
38635     editable: true,
38636     /**
38637      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
38638      */
38639     allQuery: '',
38640     /**
38641      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
38642      */
38643     mode: 'remote',
38644     /**
38645      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
38646      * listWidth has a higher value)
38647      */
38648     minListWidth : 70,
38649     /**
38650      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
38651      * allow the user to set arbitrary text into the field (defaults to false)
38652      */
38653     forceSelection:false,
38654     /**
38655      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
38656      * if typeAhead = true (defaults to 250)
38657      */
38658     typeAheadDelay : 250,
38659     /**
38660      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
38661      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
38662      */
38663     valueNotFoundText : undefined,
38664     /**
38665      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
38666      */
38667     blockFocus : false,
38668     
38669     /**
38670      * @cfg {Boolean} disableClear Disable showing of clear button.
38671      */
38672     disableClear : false,
38673     /**
38674      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
38675      */
38676     alwaysQuery : false,
38677     
38678     //private
38679     addicon : false,
38680     editicon: false,
38681     
38682     // element that contains real text value.. (when hidden is used..)
38683      
38684     // private
38685     onRender : function(ct, position){
38686         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
38687         if(this.hiddenName){
38688             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
38689                     'before', true);
38690             this.hiddenField.value =
38691                 this.hiddenValue !== undefined ? this.hiddenValue :
38692                 this.value !== undefined ? this.value : '';
38693
38694             // prevent input submission
38695             this.el.dom.removeAttribute('name');
38696              
38697              
38698         }
38699         if(Roo.isGecko){
38700             this.el.dom.setAttribute('autocomplete', 'off');
38701         }
38702
38703         var cls = 'x-combo-list';
38704
38705         this.list = new Roo.Layer({
38706             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
38707         });
38708
38709         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
38710         this.list.setWidth(lw);
38711         this.list.swallowEvent('mousewheel');
38712         this.assetHeight = 0;
38713
38714         if(this.title){
38715             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
38716             this.assetHeight += this.header.getHeight();
38717         }
38718
38719         this.innerList = this.list.createChild({cls:cls+'-inner'});
38720         this.innerList.on('mouseover', this.onViewOver, this);
38721         this.innerList.on('mousemove', this.onViewMove, this);
38722         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38723         
38724         if(this.allowBlank && !this.pageSize && !this.disableClear){
38725             this.footer = this.list.createChild({cls:cls+'-ft'});
38726             this.pageTb = new Roo.Toolbar(this.footer);
38727            
38728         }
38729         if(this.pageSize){
38730             this.footer = this.list.createChild({cls:cls+'-ft'});
38731             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
38732                     {pageSize: this.pageSize});
38733             
38734         }
38735         
38736         if (this.pageTb && this.allowBlank && !this.disableClear) {
38737             var _this = this;
38738             this.pageTb.add(new Roo.Toolbar.Fill(), {
38739                 cls: 'x-btn-icon x-btn-clear',
38740                 text: '&#160;',
38741                 handler: function()
38742                 {
38743                     _this.collapse();
38744                     _this.clearValue();
38745                     _this.onSelect(false, -1);
38746                 }
38747             });
38748         }
38749         if (this.footer) {
38750             this.assetHeight += this.footer.getHeight();
38751         }
38752         
38753
38754         if(!this.tpl){
38755             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
38756         }
38757
38758         this.view = new Roo.View(this.innerList, this.tpl, {
38759             singleSelect:true, store: this.store, selectedClass: this.selectedClass
38760         });
38761
38762         this.view.on('click', this.onViewClick, this);
38763
38764         this.store.on('beforeload', this.onBeforeLoad, this);
38765         this.store.on('load', this.onLoad, this);
38766         this.store.on('loadexception', this.onLoadException, this);
38767
38768         if(this.resizable){
38769             this.resizer = new Roo.Resizable(this.list,  {
38770                pinned:true, handles:'se'
38771             });
38772             this.resizer.on('resize', function(r, w, h){
38773                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
38774                 this.listWidth = w;
38775                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
38776                 this.restrictHeight();
38777             }, this);
38778             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
38779         }
38780         if(!this.editable){
38781             this.editable = true;
38782             this.setEditable(false);
38783         }  
38784         
38785         
38786         if (typeof(this.events.add.listeners) != 'undefined') {
38787             
38788             this.addicon = this.wrap.createChild(
38789                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
38790        
38791             this.addicon.on('click', function(e) {
38792                 this.fireEvent('add', this);
38793             }, this);
38794         }
38795         if (typeof(this.events.edit.listeners) != 'undefined') {
38796             
38797             this.editicon = this.wrap.createChild(
38798                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
38799             if (this.addicon) {
38800                 this.editicon.setStyle('margin-left', '40px');
38801             }
38802             this.editicon.on('click', function(e) {
38803                 
38804                 // we fire even  if inothing is selected..
38805                 this.fireEvent('edit', this, this.lastData );
38806                 
38807             }, this);
38808         }
38809         
38810         
38811         
38812     },
38813
38814     // private
38815     initEvents : function(){
38816         Roo.form.ComboBox.superclass.initEvents.call(this);
38817
38818         this.keyNav = new Roo.KeyNav(this.el, {
38819             "up" : function(e){
38820                 this.inKeyMode = true;
38821                 this.selectPrev();
38822             },
38823
38824             "down" : function(e){
38825                 if(!this.isExpanded()){
38826                     this.onTriggerClick();
38827                 }else{
38828                     this.inKeyMode = true;
38829                     this.selectNext();
38830                 }
38831             },
38832
38833             "enter" : function(e){
38834                 this.onViewClick();
38835                 //return true;
38836             },
38837
38838             "esc" : function(e){
38839                 this.collapse();
38840             },
38841
38842             "tab" : function(e){
38843                 this.onViewClick(false);
38844                 this.fireEvent("specialkey", this, e);
38845                 return true;
38846             },
38847
38848             scope : this,
38849
38850             doRelay : function(foo, bar, hname){
38851                 if(hname == 'down' || this.scope.isExpanded()){
38852                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38853                 }
38854                 return true;
38855             },
38856
38857             forceKeyDown: true
38858         });
38859         this.queryDelay = Math.max(this.queryDelay || 10,
38860                 this.mode == 'local' ? 10 : 250);
38861         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
38862         if(this.typeAhead){
38863             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
38864         }
38865         if(this.editable !== false){
38866             this.el.on("keyup", this.onKeyUp, this);
38867         }
38868         if(this.forceSelection){
38869             this.on('blur', this.doForce, this);
38870         }
38871     },
38872
38873     onDestroy : function(){
38874         if(this.view){
38875             this.view.setStore(null);
38876             this.view.el.removeAllListeners();
38877             this.view.el.remove();
38878             this.view.purgeListeners();
38879         }
38880         if(this.list){
38881             this.list.destroy();
38882         }
38883         if(this.store){
38884             this.store.un('beforeload', this.onBeforeLoad, this);
38885             this.store.un('load', this.onLoad, this);
38886             this.store.un('loadexception', this.onLoadException, this);
38887         }
38888         Roo.form.ComboBox.superclass.onDestroy.call(this);
38889     },
38890
38891     // private
38892     fireKey : function(e){
38893         if(e.isNavKeyPress() && !this.list.isVisible()){
38894             this.fireEvent("specialkey", this, e);
38895         }
38896     },
38897
38898     // private
38899     onResize: function(w, h){
38900         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
38901         
38902         if(typeof w != 'number'){
38903             // we do not handle it!?!?
38904             return;
38905         }
38906         var tw = this.trigger.getWidth();
38907         tw += this.addicon ? this.addicon.getWidth() : 0;
38908         tw += this.editicon ? this.editicon.getWidth() : 0;
38909         var x = w - tw;
38910         this.el.setWidth( this.adjustWidth('input', x));
38911             
38912         this.trigger.setStyle('left', x+'px');
38913         
38914         if(this.list && this.listWidth === undefined){
38915             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
38916             this.list.setWidth(lw);
38917             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38918         }
38919         
38920     
38921         
38922     },
38923
38924     /**
38925      * Allow or prevent the user from directly editing the field text.  If false is passed,
38926      * the user will only be able to select from the items defined in the dropdown list.  This method
38927      * is the runtime equivalent of setting the 'editable' config option at config time.
38928      * @param {Boolean} value True to allow the user to directly edit the field text
38929      */
38930     setEditable : function(value){
38931         if(value == this.editable){
38932             return;
38933         }
38934         this.editable = value;
38935         if(!value){
38936             this.el.dom.setAttribute('readOnly', true);
38937             this.el.on('mousedown', this.onTriggerClick,  this);
38938             this.el.addClass('x-combo-noedit');
38939         }else{
38940             this.el.dom.setAttribute('readOnly', false);
38941             this.el.un('mousedown', this.onTriggerClick,  this);
38942             this.el.removeClass('x-combo-noedit');
38943         }
38944     },
38945
38946     // private
38947     onBeforeLoad : function(){
38948         if(!this.hasFocus){
38949             return;
38950         }
38951         this.innerList.update(this.loadingText ?
38952                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
38953         this.restrictHeight();
38954         this.selectedIndex = -1;
38955     },
38956
38957     // private
38958     onLoad : function(){
38959         if(!this.hasFocus){
38960             return;
38961         }
38962         if(this.store.getCount() > 0){
38963             this.expand();
38964             this.restrictHeight();
38965             if(this.lastQuery == this.allQuery){
38966                 if(this.editable){
38967                     this.el.dom.select();
38968                 }
38969                 if(!this.selectByValue(this.value, true)){
38970                     this.select(0, true);
38971                 }
38972             }else{
38973                 this.selectNext();
38974                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
38975                     this.taTask.delay(this.typeAheadDelay);
38976                 }
38977             }
38978         }else{
38979             this.onEmptyResults();
38980         }
38981         //this.el.focus();
38982     },
38983     // private
38984     onLoadException : function()
38985     {
38986         this.collapse();
38987         Roo.log(this.store.reader.jsonData);
38988         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38989             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38990         }
38991         
38992         
38993     },
38994     // private
38995     onTypeAhead : function(){
38996         if(this.store.getCount() > 0){
38997             var r = this.store.getAt(0);
38998             var newValue = r.data[this.displayField];
38999             var len = newValue.length;
39000             var selStart = this.getRawValue().length;
39001             if(selStart != len){
39002                 this.setRawValue(newValue);
39003                 this.selectText(selStart, newValue.length);
39004             }
39005         }
39006     },
39007
39008     // private
39009     onSelect : function(record, index){
39010         if(this.fireEvent('beforeselect', this, record, index) !== false){
39011             this.setFromData(index > -1 ? record.data : false);
39012             this.collapse();
39013             this.fireEvent('select', this, record, index);
39014         }
39015     },
39016
39017     /**
39018      * Returns the currently selected field value or empty string if no value is set.
39019      * @return {String} value The selected value
39020      */
39021     getValue : function(){
39022         if(this.valueField){
39023             return typeof this.value != 'undefined' ? this.value : '';
39024         }else{
39025             return Roo.form.ComboBox.superclass.getValue.call(this);
39026         }
39027     },
39028
39029     /**
39030      * Clears any text/value currently set in the field
39031      */
39032     clearValue : function(){
39033         if(this.hiddenField){
39034             this.hiddenField.value = '';
39035         }
39036         this.value = '';
39037         this.setRawValue('');
39038         this.lastSelectionText = '';
39039         
39040     },
39041
39042     /**
39043      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39044      * will be displayed in the field.  If the value does not match the data value of an existing item,
39045      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39046      * Otherwise the field will be blank (although the value will still be set).
39047      * @param {String} value The value to match
39048      */
39049     setValue : function(v){
39050         var text = v;
39051         if(this.valueField){
39052             var r = this.findRecord(this.valueField, v);
39053             if(r){
39054                 text = r.data[this.displayField];
39055             }else if(this.valueNotFoundText !== undefined){
39056                 text = this.valueNotFoundText;
39057             }
39058         }
39059         this.lastSelectionText = text;
39060         if(this.hiddenField){
39061             this.hiddenField.value = v;
39062         }
39063         Roo.form.ComboBox.superclass.setValue.call(this, text);
39064         this.value = v;
39065     },
39066     /**
39067      * @property {Object} the last set data for the element
39068      */
39069     
39070     lastData : false,
39071     /**
39072      * Sets the value of the field based on a object which is related to the record format for the store.
39073      * @param {Object} value the value to set as. or false on reset?
39074      */
39075     setFromData : function(o){
39076         var dv = ''; // display value
39077         var vv = ''; // value value..
39078         this.lastData = o;
39079         if (this.displayField) {
39080             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39081         } else {
39082             // this is an error condition!!!
39083             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39084         }
39085         
39086         if(this.valueField){
39087             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39088         }
39089         if(this.hiddenField){
39090             this.hiddenField.value = vv;
39091             
39092             this.lastSelectionText = dv;
39093             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39094             this.value = vv;
39095             return;
39096         }
39097         // no hidden field.. - we store the value in 'value', but still display
39098         // display field!!!!
39099         this.lastSelectionText = dv;
39100         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39101         this.value = vv;
39102         
39103         
39104     },
39105     // private
39106     reset : function(){
39107         // overridden so that last data is reset..
39108         this.setValue(this.originalValue);
39109         this.clearInvalid();
39110         this.lastData = false;
39111         if (this.view) {
39112             this.view.clearSelections();
39113         }
39114     },
39115     // private
39116     findRecord : function(prop, value){
39117         var record;
39118         if(this.store.getCount() > 0){
39119             this.store.each(function(r){
39120                 if(r.data[prop] == value){
39121                     record = r;
39122                     return false;
39123                 }
39124                 return true;
39125             });
39126         }
39127         return record;
39128     },
39129     
39130     getName: function()
39131     {
39132         // returns hidden if it's set..
39133         if (!this.rendered) {return ''};
39134         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39135         
39136     },
39137     // private
39138     onViewMove : function(e, t){
39139         this.inKeyMode = false;
39140     },
39141
39142     // private
39143     onViewOver : function(e, t){
39144         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39145             return;
39146         }
39147         var item = this.view.findItemFromChild(t);
39148         if(item){
39149             var index = this.view.indexOf(item);
39150             this.select(index, false);
39151         }
39152     },
39153
39154     // private
39155     onViewClick : function(doFocus)
39156     {
39157         var index = this.view.getSelectedIndexes()[0];
39158         var r = this.store.getAt(index);
39159         if(r){
39160             this.onSelect(r, index);
39161         }
39162         if(doFocus !== false && !this.blockFocus){
39163             this.el.focus();
39164         }
39165     },
39166
39167     // private
39168     restrictHeight : function(){
39169         this.innerList.dom.style.height = '';
39170         var inner = this.innerList.dom;
39171         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39172         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39173         this.list.beginUpdate();
39174         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39175         this.list.alignTo(this.el, this.listAlign);
39176         this.list.endUpdate();
39177     },
39178
39179     // private
39180     onEmptyResults : function(){
39181         this.collapse();
39182     },
39183
39184     /**
39185      * Returns true if the dropdown list is expanded, else false.
39186      */
39187     isExpanded : function(){
39188         return this.list.isVisible();
39189     },
39190
39191     /**
39192      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39193      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39194      * @param {String} value The data value of the item to select
39195      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39196      * selected item if it is not currently in view (defaults to true)
39197      * @return {Boolean} True if the value matched an item in the list, else false
39198      */
39199     selectByValue : function(v, scrollIntoView){
39200         if(v !== undefined && v !== null){
39201             var r = this.findRecord(this.valueField || this.displayField, v);
39202             if(r){
39203                 this.select(this.store.indexOf(r), scrollIntoView);
39204                 return true;
39205             }
39206         }
39207         return false;
39208     },
39209
39210     /**
39211      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39212      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39213      * @param {Number} index The zero-based index of the list item to select
39214      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39215      * selected item if it is not currently in view (defaults to true)
39216      */
39217     select : function(index, scrollIntoView){
39218         this.selectedIndex = index;
39219         this.view.select(index);
39220         if(scrollIntoView !== false){
39221             var el = this.view.getNode(index);
39222             if(el){
39223                 this.innerList.scrollChildIntoView(el, false);
39224             }
39225         }
39226     },
39227
39228     // private
39229     selectNext : function(){
39230         var ct = this.store.getCount();
39231         if(ct > 0){
39232             if(this.selectedIndex == -1){
39233                 this.select(0);
39234             }else if(this.selectedIndex < ct-1){
39235                 this.select(this.selectedIndex+1);
39236             }
39237         }
39238     },
39239
39240     // private
39241     selectPrev : function(){
39242         var ct = this.store.getCount();
39243         if(ct > 0){
39244             if(this.selectedIndex == -1){
39245                 this.select(0);
39246             }else if(this.selectedIndex != 0){
39247                 this.select(this.selectedIndex-1);
39248             }
39249         }
39250     },
39251
39252     // private
39253     onKeyUp : function(e){
39254         if(this.editable !== false && !e.isSpecialKey()){
39255             this.lastKey = e.getKey();
39256             this.dqTask.delay(this.queryDelay);
39257         }
39258     },
39259
39260     // private
39261     validateBlur : function(){
39262         return !this.list || !this.list.isVisible();   
39263     },
39264
39265     // private
39266     initQuery : function(){
39267         this.doQuery(this.getRawValue());
39268     },
39269
39270     // private
39271     doForce : function(){
39272         if(this.el.dom.value.length > 0){
39273             this.el.dom.value =
39274                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39275              
39276         }
39277     },
39278
39279     /**
39280      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39281      * query allowing the query action to be canceled if needed.
39282      * @param {String} query The SQL query to execute
39283      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39284      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39285      * saved in the current store (defaults to false)
39286      */
39287     doQuery : function(q, forceAll){
39288         if(q === undefined || q === null){
39289             q = '';
39290         }
39291         var qe = {
39292             query: q,
39293             forceAll: forceAll,
39294             combo: this,
39295             cancel:false
39296         };
39297         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39298             return false;
39299         }
39300         q = qe.query;
39301         forceAll = qe.forceAll;
39302         if(forceAll === true || (q.length >= this.minChars)){
39303             if(this.lastQuery != q || this.alwaysQuery){
39304                 this.lastQuery = q;
39305                 if(this.mode == 'local'){
39306                     this.selectedIndex = -1;
39307                     if(forceAll){
39308                         this.store.clearFilter();
39309                     }else{
39310                         this.store.filter(this.displayField, q);
39311                     }
39312                     this.onLoad();
39313                 }else{
39314                     this.store.baseParams[this.queryParam] = q;
39315                     this.store.load({
39316                         params: this.getParams(q)
39317                     });
39318                     this.expand();
39319                 }
39320             }else{
39321                 this.selectedIndex = -1;
39322                 this.onLoad();   
39323             }
39324         }
39325     },
39326
39327     // private
39328     getParams : function(q){
39329         var p = {};
39330         //p[this.queryParam] = q;
39331         if(this.pageSize){
39332             p.start = 0;
39333             p.limit = this.pageSize;
39334         }
39335         return p;
39336     },
39337
39338     /**
39339      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39340      */
39341     collapse : function(){
39342         if(!this.isExpanded()){
39343             return;
39344         }
39345         this.list.hide();
39346         Roo.get(document).un('mousedown', this.collapseIf, this);
39347         Roo.get(document).un('mousewheel', this.collapseIf, this);
39348         if (!this.editable) {
39349             Roo.get(document).un('keydown', this.listKeyPress, this);
39350         }
39351         this.fireEvent('collapse', this);
39352     },
39353
39354     // private
39355     collapseIf : function(e){
39356         if(!e.within(this.wrap) && !e.within(this.list)){
39357             this.collapse();
39358         }
39359     },
39360
39361     /**
39362      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39363      */
39364     expand : function(){
39365         if(this.isExpanded() || !this.hasFocus){
39366             return;
39367         }
39368         this.list.alignTo(this.el, this.listAlign);
39369         this.list.show();
39370         Roo.get(document).on('mousedown', this.collapseIf, this);
39371         Roo.get(document).on('mousewheel', this.collapseIf, this);
39372         if (!this.editable) {
39373             Roo.get(document).on('keydown', this.listKeyPress, this);
39374         }
39375         
39376         this.fireEvent('expand', this);
39377     },
39378
39379     // private
39380     // Implements the default empty TriggerField.onTriggerClick function
39381     onTriggerClick : function(){
39382         if(this.disabled){
39383             return;
39384         }
39385         if(this.isExpanded()){
39386             this.collapse();
39387             if (!this.blockFocus) {
39388                 this.el.focus();
39389             }
39390             
39391         }else {
39392             this.hasFocus = true;
39393             if(this.triggerAction == 'all') {
39394                 this.doQuery(this.allQuery, true);
39395             } else {
39396                 this.doQuery(this.getRawValue());
39397             }
39398             if (!this.blockFocus) {
39399                 this.el.focus();
39400             }
39401         }
39402     },
39403     listKeyPress : function(e)
39404     {
39405         //Roo.log('listkeypress');
39406         // scroll to first matching element based on key pres..
39407         if (e.isSpecialKey()) {
39408             return false;
39409         }
39410         var k = String.fromCharCode(e.getKey()).toUpperCase();
39411         //Roo.log(k);
39412         var match  = false;
39413         var csel = this.view.getSelectedNodes();
39414         var cselitem = false;
39415         if (csel.length) {
39416             var ix = this.view.indexOf(csel[0]);
39417             cselitem  = this.store.getAt(ix);
39418             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
39419                 cselitem = false;
39420             }
39421             
39422         }
39423         
39424         this.store.each(function(v) { 
39425             if (cselitem) {
39426                 // start at existing selection.
39427                 if (cselitem.id == v.id) {
39428                     cselitem = false;
39429                 }
39430                 return;
39431             }
39432                 
39433             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
39434                 match = this.store.indexOf(v);
39435                 return false;
39436             }
39437         }, this);
39438         
39439         if (match === false) {
39440             return true; // no more action?
39441         }
39442         // scroll to?
39443         this.view.select(match);
39444         var sn = Roo.get(this.view.getSelectedNodes()[0])
39445         sn.scrollIntoView(sn.dom.parentNode, false);
39446     }
39447
39448     /** 
39449     * @cfg {Boolean} grow 
39450     * @hide 
39451     */
39452     /** 
39453     * @cfg {Number} growMin 
39454     * @hide 
39455     */
39456     /** 
39457     * @cfg {Number} growMax 
39458     * @hide 
39459     */
39460     /**
39461      * @hide
39462      * @method autoSize
39463      */
39464 });/*
39465  * Copyright(c) 2010-2012, Roo J Solutions Limited
39466  *
39467  * Licence LGPL
39468  *
39469  */
39470
39471 /**
39472  * @class Roo.form.ComboBoxArray
39473  * @extends Roo.form.TextField
39474  * A facebook style adder... for lists of email / people / countries  etc...
39475  * pick multiple items from a combo box, and shows each one.
39476  *
39477  *  Fred [x]  Brian [x]  [Pick another |v]
39478  *
39479  *
39480  *  For this to work: it needs various extra information
39481  *    - normal combo problay has
39482  *      name, hiddenName
39483  *    + displayField, valueField
39484  *
39485  *    For our purpose...
39486  *
39487  *
39488  *   If we change from 'extends' to wrapping...
39489  *   
39490  *  
39491  *
39492  
39493  
39494  * @constructor
39495  * Create a new ComboBoxArray.
39496  * @param {Object} config Configuration options
39497  */
39498  
39499
39500 Roo.form.ComboBoxArray = function(config)
39501 {
39502     
39503     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
39504     
39505     this.items = new Roo.util.MixedCollection(false);
39506     
39507     // construct the child combo...
39508     
39509     
39510     
39511     
39512    
39513     
39514 }
39515
39516  
39517 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
39518
39519     /**
39520      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
39521      */
39522     
39523     lastData : false,
39524     
39525     // behavies liek a hiddne field
39526     inputType:      'hidden',
39527     /**
39528      * @cfg {Number} width The width of the box that displays the selected element
39529      */ 
39530     width:          300,
39531
39532     
39533     
39534     /**
39535      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
39536      */
39537     name : false,
39538     /**
39539      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
39540      */
39541     hiddenName : false,
39542     
39543     
39544     // private the array of items that are displayed..
39545     items  : false,
39546     // private - the hidden field el.
39547     hiddenEl : false,
39548     // private - the filed el..
39549     el : false,
39550     
39551     //validateValue : function() { return true; }, // all values are ok!
39552     //onAddClick: function() { },
39553     
39554     onRender : function(ct, position) 
39555     {
39556         
39557         // create the standard hidden element
39558         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
39559         
39560         
39561         // give fake names to child combo;
39562         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
39563         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
39564         
39565         this.combo = Roo.factory(this.combo, Roo.form);
39566         this.combo.onRender(ct, position);
39567         if (typeof(this.combo.width) != 'undefined') {
39568             this.combo.onResize(this.combo.width,0);
39569         }
39570         
39571         this.combo.initEvents();
39572         
39573         // assigned so form know we need to do this..
39574         this.store          = this.combo.store;
39575         this.valueField     = this.combo.valueField;
39576         this.displayField   = this.combo.displayField ;
39577         
39578         
39579         this.combo.wrap.addClass('x-cbarray-grp');
39580         
39581         var cbwrap = this.combo.wrap.createChild(
39582             {tag: 'div', cls: 'x-cbarray-cb'},
39583             this.combo.el.dom
39584         );
39585         
39586              
39587         this.hiddenEl = this.combo.wrap.createChild({
39588             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
39589         });
39590         this.el = this.combo.wrap.createChild({
39591             tag: 'input',  type:'hidden' , name: this.name, value : ''
39592         });
39593          //   this.el.dom.removeAttribute("name");
39594         
39595         
39596         this.outerWrap = this.combo.wrap;
39597         this.wrap = cbwrap;
39598         
39599         this.outerWrap.setWidth(this.width);
39600         this.outerWrap.dom.removeChild(this.el.dom);
39601         
39602         this.wrap.dom.appendChild(this.el.dom);
39603         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
39604         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
39605         
39606         this.combo.trigger.setStyle('position','relative');
39607         this.combo.trigger.setStyle('left', '0px');
39608         this.combo.trigger.setStyle('top', '2px');
39609         
39610         this.combo.el.setStyle('vertical-align', 'text-bottom');
39611         
39612         //this.trigger.setStyle('vertical-align', 'top');
39613         
39614         // this should use the code from combo really... on('add' ....)
39615         if (this.adder) {
39616             
39617         
39618             this.adder = this.outerWrap.createChild(
39619                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
39620             var _t = this;
39621             this.adder.on('click', function(e) {
39622                 _t.fireEvent('adderclick', this, e);
39623             }, _t);
39624         }
39625         //var _t = this;
39626         //this.adder.on('click', this.onAddClick, _t);
39627         
39628         
39629         this.combo.on('select', function(cb, rec, ix) {
39630             this.addItem(rec.data);
39631             
39632             cb.setValue('');
39633             cb.el.dom.value = '';
39634             //cb.lastData = rec.data;
39635             // add to list
39636             
39637         }, this);
39638         
39639         
39640     },
39641     
39642     
39643     getName: function()
39644     {
39645         // returns hidden if it's set..
39646         if (!this.rendered) {return ''};
39647         return  this.hiddenName ? this.hiddenName : this.name;
39648         
39649     },
39650     
39651     
39652     onResize: function(w, h){
39653         
39654         return;
39655         // not sure if this is needed..
39656         //this.combo.onResize(w,h);
39657         
39658         if(typeof w != 'number'){
39659             // we do not handle it!?!?
39660             return;
39661         }
39662         var tw = this.combo.trigger.getWidth();
39663         tw += this.addicon ? this.addicon.getWidth() : 0;
39664         tw += this.editicon ? this.editicon.getWidth() : 0;
39665         var x = w - tw;
39666         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
39667             
39668         this.combo.trigger.setStyle('left', '0px');
39669         
39670         if(this.list && this.listWidth === undefined){
39671             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
39672             this.list.setWidth(lw);
39673             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39674         }
39675         
39676     
39677         
39678     },
39679     
39680     addItem: function(rec)
39681     {
39682         var valueField = this.combo.valueField;
39683         var displayField = this.combo.displayField;
39684         if (this.items.indexOfKey(rec[valueField]) > -1) {
39685             //console.log("GOT " + rec.data.id);
39686             return;
39687         }
39688         
39689         var x = new Roo.form.ComboBoxArray.Item({
39690             //id : rec[this.idField],
39691             data : rec,
39692             displayField : displayField ,
39693             tipField : displayField ,
39694             cb : this
39695         });
39696         // use the 
39697         this.items.add(rec[valueField],x);
39698         // add it before the element..
39699         this.updateHiddenEl();
39700         x.render(this.outerWrap, this.wrap.dom);
39701         // add the image handler..
39702     },
39703     
39704     updateHiddenEl : function()
39705     {
39706         this.validate();
39707         if (!this.hiddenEl) {
39708             return;
39709         }
39710         var ar = [];
39711         var idField = this.combo.valueField;
39712         
39713         this.items.each(function(f) {
39714             ar.push(f.data[idField]);
39715            
39716         });
39717         this.hiddenEl.dom.value = ar.join(',');
39718         this.validate();
39719     },
39720     
39721     reset : function()
39722     {
39723         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
39724         this.items.each(function(f) {
39725            f.remove(); 
39726         });
39727         this.el.dom.value = '';
39728         if (this.hiddenEl) {
39729             this.hiddenEl.dom.value = '';
39730         }
39731         
39732     },
39733     getValue: function()
39734     {
39735         return this.hiddenEl ? this.hiddenEl.dom.value : '';
39736     },
39737     setValue: function(v) // not a valid action - must use addItems..
39738     {
39739          
39740         this.reset();
39741         
39742         
39743         
39744         if (this.store.isLocal && (typeof(v) == 'string')) {
39745             // then we can use the store to find the values..
39746             // comma seperated at present.. this needs to allow JSON based encoding..
39747             this.hiddenEl.value  = v;
39748             var v_ar = [];
39749             Roo.each(v.split(','), function(k) {
39750                 Roo.log("CHECK " + this.valueField + ',' + k);
39751                 var li = this.store.query(this.valueField, k);
39752                 if (!li.length) {
39753                     return;
39754                 }
39755                 var add = {};
39756                 add[this.valueField] = k;
39757                 add[this.displayField] = li.item(0).data[this.displayField];
39758                 
39759                 this.addItem(add);
39760             }, this) 
39761              
39762         }
39763         if (typeof(v) == 'object') {
39764             // then let's assume it's an array of objects..
39765             Roo.each(v, function(l) {
39766                 this.addItem(l);
39767             }, this);
39768              
39769         }
39770         
39771         
39772     },
39773     setFromData: function(v)
39774     {
39775         // this recieves an object, if setValues is called.
39776         this.reset();
39777         this.el.dom.value = v[this.displayField];
39778         this.hiddenEl.dom.value = v[this.valueField];
39779         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
39780             return;
39781         }
39782         var kv = v[this.valueField];
39783         var dv = v[this.displayField];
39784         kv = typeof(kv) != 'string' ? '' : kv;
39785         dv = typeof(dv) != 'string' ? '' : dv;
39786         
39787         
39788         var keys = kv.split(',');
39789         var display = dv.split(',');
39790         for (var i = 0 ; i < keys.length; i++) {
39791             
39792             add = {};
39793             add[this.valueField] = keys[i];
39794             add[this.displayField] = display[i];
39795             this.addItem(add);
39796         }
39797       
39798         
39799     },
39800     
39801     
39802     validateValue : function(value){
39803         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
39804         
39805     }
39806     
39807 });
39808
39809
39810
39811 /**
39812  * @class Roo.form.ComboBoxArray.Item
39813  * @extends Roo.BoxComponent
39814  * A selected item in the list
39815  *  Fred [x]  Brian [x]  [Pick another |v]
39816  * 
39817  * @constructor
39818  * Create a new item.
39819  * @param {Object} config Configuration options
39820  */
39821  
39822 Roo.form.ComboBoxArray.Item = function(config) {
39823     config.id = Roo.id();
39824     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
39825 }
39826
39827 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
39828     data : {},
39829     cb: false,
39830     displayField : false,
39831     tipField : false,
39832     
39833     
39834     defaultAutoCreate : {
39835         tag: 'div',
39836         cls: 'x-cbarray-item',
39837         cn : [ 
39838             { tag: 'div' },
39839             {
39840                 tag: 'img',
39841                 width:16,
39842                 height : 16,
39843                 src : Roo.BLANK_IMAGE_URL ,
39844                 align: 'center'
39845             }
39846         ]
39847         
39848     },
39849     
39850  
39851     onRender : function(ct, position)
39852     {
39853         Roo.form.Field.superclass.onRender.call(this, ct, position);
39854         
39855         if(!this.el){
39856             var cfg = this.getAutoCreate();
39857             this.el = ct.createChild(cfg, position);
39858         }
39859         
39860         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
39861         
39862         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
39863             this.cb.renderer(this.data) :
39864             String.format('{0}',this.data[this.displayField]);
39865         
39866             
39867         this.el.child('div').dom.setAttribute('qtip',
39868                         String.format('{0}',this.data[this.tipField])
39869         );
39870         
39871         this.el.child('img').on('click', this.remove, this);
39872         
39873     },
39874    
39875     remove : function()
39876     {
39877         
39878         this.cb.items.remove(this);
39879         this.el.child('img').un('click', this.remove, this);
39880         this.el.remove();
39881         this.cb.updateHiddenEl();
39882     }
39883     
39884     
39885 });/*
39886  * Based on:
39887  * Ext JS Library 1.1.1
39888  * Copyright(c) 2006-2007, Ext JS, LLC.
39889  *
39890  * Originally Released Under LGPL - original licence link has changed is not relivant.
39891  *
39892  * Fork - LGPL
39893  * <script type="text/javascript">
39894  */
39895 /**
39896  * @class Roo.form.Checkbox
39897  * @extends Roo.form.Field
39898  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
39899  * @constructor
39900  * Creates a new Checkbox
39901  * @param {Object} config Configuration options
39902  */
39903 Roo.form.Checkbox = function(config){
39904     Roo.form.Checkbox.superclass.constructor.call(this, config);
39905     this.addEvents({
39906         /**
39907          * @event check
39908          * Fires when the checkbox is checked or unchecked.
39909              * @param {Roo.form.Checkbox} this This checkbox
39910              * @param {Boolean} checked The new checked value
39911              */
39912         check : true
39913     });
39914 };
39915
39916 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
39917     /**
39918      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
39919      */
39920     focusClass : undefined,
39921     /**
39922      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
39923      */
39924     fieldClass: "x-form-field",
39925     /**
39926      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
39927      */
39928     checked: false,
39929     /**
39930      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39931      * {tag: "input", type: "checkbox", autocomplete: "off"})
39932      */
39933     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
39934     /**
39935      * @cfg {String} boxLabel The text that appears beside the checkbox
39936      */
39937     boxLabel : "",
39938     /**
39939      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
39940      */  
39941     inputValue : '1',
39942     /**
39943      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
39944      */
39945      valueOff: '0', // value when not checked..
39946
39947     actionMode : 'viewEl', 
39948     //
39949     // private
39950     itemCls : 'x-menu-check-item x-form-item',
39951     groupClass : 'x-menu-group-item',
39952     inputType : 'hidden',
39953     
39954     
39955     inSetChecked: false, // check that we are not calling self...
39956     
39957     inputElement: false, // real input element?
39958     basedOn: false, // ????
39959     
39960     isFormField: true, // not sure where this is needed!!!!
39961
39962     onResize : function(){
39963         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
39964         if(!this.boxLabel){
39965             this.el.alignTo(this.wrap, 'c-c');
39966         }
39967     },
39968
39969     initEvents : function(){
39970         Roo.form.Checkbox.superclass.initEvents.call(this);
39971         this.el.on("click", this.onClick,  this);
39972         this.el.on("change", this.onClick,  this);
39973     },
39974
39975
39976     getResizeEl : function(){
39977         return this.wrap;
39978     },
39979
39980     getPositionEl : function(){
39981         return this.wrap;
39982     },
39983
39984     // private
39985     onRender : function(ct, position){
39986         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
39987         /*
39988         if(this.inputValue !== undefined){
39989             this.el.dom.value = this.inputValue;
39990         }
39991         */
39992         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
39993         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
39994         var viewEl = this.wrap.createChild({ 
39995             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
39996         this.viewEl = viewEl;   
39997         this.wrap.on('click', this.onClick,  this); 
39998         
39999         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40000         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40001         
40002         
40003         
40004         if(this.boxLabel){
40005             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40006         //    viewEl.on('click', this.onClick,  this); 
40007         }
40008         //if(this.checked){
40009             this.setChecked(this.checked);
40010         //}else{
40011             //this.checked = this.el.dom;
40012         //}
40013
40014     },
40015
40016     // private
40017     initValue : Roo.emptyFn,
40018
40019     /**
40020      * Returns the checked state of the checkbox.
40021      * @return {Boolean} True if checked, else false
40022      */
40023     getValue : function(){
40024         if(this.el){
40025             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40026         }
40027         return this.valueOff;
40028         
40029     },
40030
40031         // private
40032     onClick : function(){ 
40033         this.setChecked(!this.checked);
40034
40035         //if(this.el.dom.checked != this.checked){
40036         //    this.setValue(this.el.dom.checked);
40037        // }
40038     },
40039
40040     /**
40041      * Sets the checked state of the checkbox.
40042      * On is always based on a string comparison between inputValue and the param.
40043      * @param {Boolean/String} value - the value to set 
40044      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40045      */
40046     setValue : function(v,suppressEvent){
40047         
40048         
40049         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40050         //if(this.el && this.el.dom){
40051         //    this.el.dom.checked = this.checked;
40052         //    this.el.dom.defaultChecked = this.checked;
40053         //}
40054         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40055         //this.fireEvent("check", this, this.checked);
40056     },
40057     // private..
40058     setChecked : function(state,suppressEvent)
40059     {
40060         if (this.inSetChecked) {
40061             this.checked = state;
40062             return;
40063         }
40064         
40065     
40066         if(this.wrap){
40067             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40068         }
40069         this.checked = state;
40070         if(suppressEvent !== true){
40071             this.fireEvent('check', this, state);
40072         }
40073         this.inSetChecked = true;
40074         this.el.dom.value = state ? this.inputValue : this.valueOff;
40075         this.inSetChecked = false;
40076         
40077     },
40078     // handle setting of hidden value by some other method!!?!?
40079     setFromHidden: function()
40080     {
40081         if(!this.el){
40082             return;
40083         }
40084         //console.log("SET FROM HIDDEN");
40085         //alert('setFrom hidden');
40086         this.setValue(this.el.dom.value);
40087     },
40088     
40089     onDestroy : function()
40090     {
40091         if(this.viewEl){
40092             Roo.get(this.viewEl).remove();
40093         }
40094          
40095         Roo.form.Checkbox.superclass.onDestroy.call(this);
40096     }
40097
40098 });/*
40099  * Based on:
40100  * Ext JS Library 1.1.1
40101  * Copyright(c) 2006-2007, Ext JS, LLC.
40102  *
40103  * Originally Released Under LGPL - original licence link has changed is not relivant.
40104  *
40105  * Fork - LGPL
40106  * <script type="text/javascript">
40107  */
40108  
40109 /**
40110  * @class Roo.form.Radio
40111  * @extends Roo.form.Checkbox
40112  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40113  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40114  * @constructor
40115  * Creates a new Radio
40116  * @param {Object} config Configuration options
40117  */
40118 Roo.form.Radio = function(){
40119     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40120 };
40121 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40122     inputType: 'radio',
40123
40124     /**
40125      * If this radio is part of a group, it will return the selected value
40126      * @return {String}
40127      */
40128     getGroupValue : function(){
40129         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40130     },
40131     
40132     
40133     onRender : function(ct, position){
40134         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40135         
40136         if(this.inputValue !== undefined){
40137             this.el.dom.value = this.inputValue;
40138         }
40139          
40140         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40141         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40142         //var viewEl = this.wrap.createChild({ 
40143         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40144         //this.viewEl = viewEl;   
40145         //this.wrap.on('click', this.onClick,  this); 
40146         
40147         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40148         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40149         
40150         
40151         
40152         if(this.boxLabel){
40153             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40154         //    viewEl.on('click', this.onClick,  this); 
40155         }
40156          if(this.checked){
40157             this.el.dom.checked =   'checked' ;
40158         }
40159          
40160     } 
40161     
40162     
40163 });//<script type="text/javascript">
40164
40165 /*
40166  * Ext JS Library 1.1.1
40167  * Copyright(c) 2006-2007, Ext JS, LLC.
40168  * licensing@extjs.com
40169  * 
40170  * http://www.extjs.com/license
40171  */
40172  
40173  /*
40174   * 
40175   * Known bugs:
40176   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
40177   * - IE ? - no idea how much works there.
40178   * 
40179   * 
40180   * 
40181   */
40182  
40183
40184 /**
40185  * @class Ext.form.HtmlEditor
40186  * @extends Ext.form.Field
40187  * Provides a lightweight HTML Editor component.
40188  *
40189  * This has been tested on Fireforx / Chrome.. IE may not be so great..
40190  * 
40191  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
40192  * supported by this editor.</b><br/><br/>
40193  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
40194  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40195  */
40196 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
40197       /**
40198      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
40199      */
40200     toolbars : false,
40201     /**
40202      * @cfg {String} createLinkText The default text for the create link prompt
40203      */
40204     createLinkText : 'Please enter the URL for the link:',
40205     /**
40206      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
40207      */
40208     defaultLinkValue : 'http:/'+'/',
40209    
40210      /**
40211      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40212      *                        Roo.resizable.
40213      */
40214     resizable : false,
40215      /**
40216      * @cfg {Number} height (in pixels)
40217      */   
40218     height: 300,
40219    /**
40220      * @cfg {Number} width (in pixels)
40221      */   
40222     width: 500,
40223     
40224     /**
40225      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40226      * 
40227      */
40228     stylesheets: false,
40229     
40230     // id of frame..
40231     frameId: false,
40232     
40233     // private properties
40234     validationEvent : false,
40235     deferHeight: true,
40236     initialized : false,
40237     activated : false,
40238     sourceEditMode : false,
40239     onFocus : Roo.emptyFn,
40240     iframePad:3,
40241     hideMode:'offsets',
40242     
40243     defaultAutoCreate : { // modified by initCompnoent..
40244         tag: "textarea",
40245         style:"width:500px;height:300px;",
40246         autocomplete: "off"
40247     },
40248
40249     // private
40250     initComponent : function(){
40251         this.addEvents({
40252             /**
40253              * @event initialize
40254              * Fires when the editor is fully initialized (including the iframe)
40255              * @param {HtmlEditor} this
40256              */
40257             initialize: true,
40258             /**
40259              * @event activate
40260              * Fires when the editor is first receives the focus. Any insertion must wait
40261              * until after this event.
40262              * @param {HtmlEditor} this
40263              */
40264             activate: true,
40265              /**
40266              * @event beforesync
40267              * Fires before the textarea is updated with content from the editor iframe. Return false
40268              * to cancel the sync.
40269              * @param {HtmlEditor} this
40270              * @param {String} html
40271              */
40272             beforesync: true,
40273              /**
40274              * @event beforepush
40275              * Fires before the iframe editor is updated with content from the textarea. Return false
40276              * to cancel the push.
40277              * @param {HtmlEditor} this
40278              * @param {String} html
40279              */
40280             beforepush: true,
40281              /**
40282              * @event sync
40283              * Fires when the textarea is updated with content from the editor iframe.
40284              * @param {HtmlEditor} this
40285              * @param {String} html
40286              */
40287             sync: true,
40288              /**
40289              * @event push
40290              * Fires when the iframe editor is updated with content from the textarea.
40291              * @param {HtmlEditor} this
40292              * @param {String} html
40293              */
40294             push: true,
40295              /**
40296              * @event editmodechange
40297              * Fires when the editor switches edit modes
40298              * @param {HtmlEditor} this
40299              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
40300              */
40301             editmodechange: true,
40302             /**
40303              * @event editorevent
40304              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40305              * @param {HtmlEditor} this
40306              */
40307             editorevent: true
40308         });
40309         this.defaultAutoCreate =  {
40310             tag: "textarea",
40311             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
40312             autocomplete: "off"
40313         };
40314     },
40315
40316     /**
40317      * Protected method that will not generally be called directly. It
40318      * is called when the editor creates its toolbar. Override this method if you need to
40319      * add custom toolbar buttons.
40320      * @param {HtmlEditor} editor
40321      */
40322     createToolbar : function(editor){
40323         if (!editor.toolbars || !editor.toolbars.length) {
40324             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
40325         }
40326         
40327         for (var i =0 ; i < editor.toolbars.length;i++) {
40328             editor.toolbars[i] = Roo.factory(
40329                     typeof(editor.toolbars[i]) == 'string' ?
40330                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
40331                 Roo.form.HtmlEditor);
40332             editor.toolbars[i].init(editor);
40333         }
40334          
40335         
40336     },
40337
40338     /**
40339      * Protected method that will not generally be called directly. It
40340      * is called when the editor initializes the iframe with HTML contents. Override this method if you
40341      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
40342      */
40343     getDocMarkup : function(){
40344         // body styles..
40345         var st = '';
40346         if (this.stylesheets === false) {
40347             
40348             Roo.get(document.head).select('style').each(function(node) {
40349                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40350             });
40351             
40352             Roo.get(document.head).select('link').each(function(node) { 
40353                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40354             });
40355             
40356         } else if (!this.stylesheets.length) {
40357                 // simple..
40358                 st = '<style type="text/css">' +
40359                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40360                    '</style>';
40361         } else {
40362             Roo.each(this.stylesheets, function(s) {
40363                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
40364             });
40365             
40366         }
40367         
40368         st +=  '<style type="text/css">' +
40369             'IMG { cursor: pointer } ' +
40370         '</style>';
40371
40372         
40373         return '<html><head>' + st  +
40374             //<style type="text/css">' +
40375             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
40376             //'</style>' +
40377             ' </head><body class="roo-htmleditor-body"></body></html>';
40378     },
40379
40380     // private
40381     onRender : function(ct, position)
40382     {
40383         var _t = this;
40384         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
40385         this.el.dom.style.border = '0 none';
40386         this.el.dom.setAttribute('tabIndex', -1);
40387         this.el.addClass('x-hidden');
40388         if(Roo.isIE){ // fix IE 1px bogus margin
40389             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
40390         }
40391         this.wrap = this.el.wrap({
40392             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
40393         });
40394         
40395         if (this.resizable) {
40396             this.resizeEl = new Roo.Resizable(this.wrap, {
40397                 pinned : true,
40398                 wrap: true,
40399                 dynamic : true,
40400                 minHeight : this.height,
40401                 height: this.height,
40402                 handles : this.resizable,
40403                 width: this.width,
40404                 listeners : {
40405                     resize : function(r, w, h) {
40406                         _t.onResize(w,h); // -something
40407                     }
40408                 }
40409             });
40410             
40411         }
40412
40413         this.frameId = Roo.id();
40414         
40415         this.createToolbar(this);
40416         
40417       
40418         
40419         var iframe = this.wrap.createChild({
40420             tag: 'iframe',
40421             id: this.frameId,
40422             name: this.frameId,
40423             frameBorder : 'no',
40424             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
40425         }, this.el
40426         );
40427         
40428        // console.log(iframe);
40429         //this.wrap.dom.appendChild(iframe);
40430
40431         this.iframe = iframe.dom;
40432
40433          this.assignDocWin();
40434         
40435         this.doc.designMode = 'on';
40436        
40437         this.doc.open();
40438         this.doc.write(this.getDocMarkup());
40439         this.doc.close();
40440
40441         
40442         var task = { // must defer to wait for browser to be ready
40443             run : function(){
40444                 //console.log("run task?" + this.doc.readyState);
40445                 this.assignDocWin();
40446                 if(this.doc.body || this.doc.readyState == 'complete'){
40447                     try {
40448                         this.doc.designMode="on";
40449                     } catch (e) {
40450                         return;
40451                     }
40452                     Roo.TaskMgr.stop(task);
40453                     this.initEditor.defer(10, this);
40454                 }
40455             },
40456             interval : 10,
40457             duration:10000,
40458             scope: this
40459         };
40460         Roo.TaskMgr.start(task);
40461
40462         if(!this.width){
40463             this.setSize(this.wrap.getSize());
40464         }
40465         if (this.resizeEl) {
40466             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
40467             // should trigger onReize..
40468         }
40469     },
40470
40471     // private
40472     onResize : function(w, h)
40473     {
40474         //Roo.log('resize: ' +w + ',' + h );
40475         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
40476         if(this.el && this.iframe){
40477             if(typeof w == 'number'){
40478                 var aw = w - this.wrap.getFrameWidth('lr');
40479                 this.el.setWidth(this.adjustWidth('textarea', aw));
40480                 this.iframe.style.width = aw + 'px';
40481             }
40482             if(typeof h == 'number'){
40483                 var tbh = 0;
40484                 for (var i =0; i < this.toolbars.length;i++) {
40485                     // fixme - ask toolbars for heights?
40486                     tbh += this.toolbars[i].tb.el.getHeight();
40487                     if (this.toolbars[i].footer) {
40488                         tbh += this.toolbars[i].footer.el.getHeight();
40489                     }
40490                 }
40491                 
40492                 
40493                 
40494                 
40495                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
40496                 ah -= 5; // knock a few pixes off for look..
40497                 this.el.setHeight(this.adjustWidth('textarea', ah));
40498                 this.iframe.style.height = ah + 'px';
40499                 if(this.doc){
40500                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
40501                 }
40502             }
40503         }
40504     },
40505
40506     /**
40507      * Toggles the editor between standard and source edit mode.
40508      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
40509      */
40510     toggleSourceEdit : function(sourceEditMode){
40511         
40512         this.sourceEditMode = sourceEditMode === true;
40513         
40514         if(this.sourceEditMode){
40515 //            Roo.log('in');
40516 //            Roo.log(this.syncValue());
40517             this.syncValue();
40518             this.iframe.className = 'x-hidden';
40519             this.el.removeClass('x-hidden');
40520             this.el.dom.removeAttribute('tabIndex');
40521             this.el.focus();
40522         }else{
40523 //            Roo.log('out')
40524 //            Roo.log(this.pushValue()); 
40525             this.pushValue();
40526             this.iframe.className = '';
40527             this.el.addClass('x-hidden');
40528             this.el.dom.setAttribute('tabIndex', -1);
40529             this.deferFocus();
40530         }
40531         this.setSize(this.wrap.getSize());
40532         this.fireEvent('editmodechange', this, this.sourceEditMode);
40533     },
40534
40535     // private used internally
40536     createLink : function(){
40537         var url = prompt(this.createLinkText, this.defaultLinkValue);
40538         if(url && url != 'http:/'+'/'){
40539             this.relayCmd('createlink', url);
40540         }
40541     },
40542
40543     // private (for BoxComponent)
40544     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40545
40546     // private (for BoxComponent)
40547     getResizeEl : function(){
40548         return this.wrap;
40549     },
40550
40551     // private (for BoxComponent)
40552     getPositionEl : function(){
40553         return this.wrap;
40554     },
40555
40556     // private
40557     initEvents : function(){
40558         this.originalValue = this.getValue();
40559     },
40560
40561     /**
40562      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40563      * @method
40564      */
40565     markInvalid : Roo.emptyFn,
40566     /**
40567      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
40568      * @method
40569      */
40570     clearInvalid : Roo.emptyFn,
40571
40572     setValue : function(v){
40573         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
40574         this.pushValue();
40575     },
40576
40577     /**
40578      * Protected method that will not generally be called directly. If you need/want
40579      * custom HTML cleanup, this is the method you should override.
40580      * @param {String} html The HTML to be cleaned
40581      * return {String} The cleaned HTML
40582      */
40583     cleanHtml : function(html){
40584         html = String(html);
40585         if(html.length > 5){
40586             if(Roo.isSafari){ // strip safari nonsense
40587                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
40588             }
40589         }
40590         if(html == '&nbsp;'){
40591             html = '';
40592         }
40593         return html;
40594     },
40595
40596     /**
40597      * Protected method that will not generally be called directly. Syncs the contents
40598      * of the editor iframe with the textarea.
40599      */
40600     syncValue : function(){
40601         if(this.initialized){
40602             var bd = (this.doc.body || this.doc.documentElement);
40603             //this.cleanUpPaste(); -- this is done else where and causes havoc..
40604             var html = bd.innerHTML;
40605             if(Roo.isSafari){
40606                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
40607                 var m = bs.match(/text-align:(.*?);/i);
40608                 if(m && m[1]){
40609                     html = '<div style="'+m[0]+'">' + html + '</div>';
40610                 }
40611             }
40612             html = this.cleanHtml(html);
40613             // fix up the special chars.. normaly like back quotes in word...
40614             // however we do not want to do this with chinese..
40615             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
40616                 var cc = b.charCodeAt();
40617                 if (
40618                     (cc >= 0x4E00 && cc < 0xA000 ) ||
40619                     (cc >= 0x3400 && cc < 0x4E00 ) ||
40620                     (cc >= 0xf900 && cc < 0xfb00 )
40621                 ) {
40622                         return b;
40623                 }
40624                 return "&#"+cc+";" 
40625             });
40626             if(this.fireEvent('beforesync', this, html) !== false){
40627                 this.el.dom.value = html;
40628                 this.fireEvent('sync', this, html);
40629             }
40630         }
40631     },
40632
40633     /**
40634      * Protected method that will not generally be called directly. Pushes the value of the textarea
40635      * into the iframe editor.
40636      */
40637     pushValue : function(){
40638         if(this.initialized){
40639             var v = this.el.dom.value;
40640             
40641             if(v.length < 1){
40642                 v = '&#160;';
40643             }
40644             
40645             if(this.fireEvent('beforepush', this, v) !== false){
40646                 var d = (this.doc.body || this.doc.documentElement);
40647                 d.innerHTML = v;
40648                 this.cleanUpPaste();
40649                 this.el.dom.value = d.innerHTML;
40650                 this.fireEvent('push', this, v);
40651             }
40652         }
40653     },
40654
40655     // private
40656     deferFocus : function(){
40657         this.focus.defer(10, this);
40658     },
40659
40660     // doc'ed in Field
40661     focus : function(){
40662         if(this.win && !this.sourceEditMode){
40663             this.win.focus();
40664         }else{
40665             this.el.focus();
40666         }
40667     },
40668     
40669     assignDocWin: function()
40670     {
40671         var iframe = this.iframe;
40672         
40673          if(Roo.isIE){
40674             this.doc = iframe.contentWindow.document;
40675             this.win = iframe.contentWindow;
40676         } else {
40677             if (!Roo.get(this.frameId)) {
40678                 return;
40679             }
40680             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
40681             this.win = Roo.get(this.frameId).dom.contentWindow;
40682         }
40683     },
40684     
40685     // private
40686     initEditor : function(){
40687         //console.log("INIT EDITOR");
40688         this.assignDocWin();
40689         
40690         
40691         
40692         this.doc.designMode="on";
40693         this.doc.open();
40694         this.doc.write(this.getDocMarkup());
40695         this.doc.close();
40696         
40697         var dbody = (this.doc.body || this.doc.documentElement);
40698         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
40699         // this copies styles from the containing element into thsi one..
40700         // not sure why we need all of this..
40701         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
40702         ss['background-attachment'] = 'fixed'; // w3c
40703         dbody.bgProperties = 'fixed'; // ie
40704         Roo.DomHelper.applyStyles(dbody, ss);
40705         Roo.EventManager.on(this.doc, {
40706             //'mousedown': this.onEditorEvent,
40707             'mouseup': this.onEditorEvent,
40708             'dblclick': this.onEditorEvent,
40709             'click': this.onEditorEvent,
40710             'keyup': this.onEditorEvent,
40711             buffer:100,
40712             scope: this
40713         });
40714         if(Roo.isGecko){
40715             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
40716         }
40717         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
40718             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
40719         }
40720         this.initialized = true;
40721
40722         this.fireEvent('initialize', this);
40723         this.pushValue();
40724     },
40725
40726     // private
40727     onDestroy : function(){
40728         
40729         
40730         
40731         if(this.rendered){
40732             
40733             for (var i =0; i < this.toolbars.length;i++) {
40734                 // fixme - ask toolbars for heights?
40735                 this.toolbars[i].onDestroy();
40736             }
40737             
40738             this.wrap.dom.innerHTML = '';
40739             this.wrap.remove();
40740         }
40741     },
40742
40743     // private
40744     onFirstFocus : function(){
40745         
40746         this.assignDocWin();
40747         
40748         
40749         this.activated = true;
40750         for (var i =0; i < this.toolbars.length;i++) {
40751             this.toolbars[i].onFirstFocus();
40752         }
40753        
40754         if(Roo.isGecko){ // prevent silly gecko errors
40755             this.win.focus();
40756             var s = this.win.getSelection();
40757             if(!s.focusNode || s.focusNode.nodeType != 3){
40758                 var r = s.getRangeAt(0);
40759                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
40760                 r.collapse(true);
40761                 this.deferFocus();
40762             }
40763             try{
40764                 this.execCmd('useCSS', true);
40765                 this.execCmd('styleWithCSS', false);
40766             }catch(e){}
40767         }
40768         this.fireEvent('activate', this);
40769     },
40770
40771     // private
40772     adjustFont: function(btn){
40773         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
40774         //if(Roo.isSafari){ // safari
40775         //    adjust *= 2;
40776        // }
40777         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
40778         if(Roo.isSafari){ // safari
40779             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
40780             v =  (v < 10) ? 10 : v;
40781             v =  (v > 48) ? 48 : v;
40782             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
40783             
40784         }
40785         
40786         
40787         v = Math.max(1, v+adjust);
40788         
40789         this.execCmd('FontSize', v  );
40790     },
40791
40792     onEditorEvent : function(e){
40793         this.fireEvent('editorevent', this, e);
40794       //  this.updateToolbar();
40795         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
40796     },
40797
40798     insertTag : function(tg)
40799     {
40800         // could be a bit smarter... -> wrap the current selected tRoo..
40801         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
40802             
40803             range = this.createRange(this.getSelection());
40804             var wrappingNode = this.doc.createElement(tg.toLowerCase());
40805             wrappingNode.appendChild(range.extractContents());
40806             range.insertNode(wrappingNode);
40807
40808             return;
40809             
40810             
40811             
40812         }
40813         this.execCmd("formatblock",   tg);
40814         
40815     },
40816     
40817     insertText : function(txt)
40818     {
40819         
40820         
40821         var range = this.createRange();
40822         range.deleteContents();
40823                //alert(Sender.getAttribute('label'));
40824                
40825         range.insertNode(this.doc.createTextNode(txt));
40826     } ,
40827     
40828     // private
40829     relayBtnCmd : function(btn){
40830         this.relayCmd(btn.cmd);
40831     },
40832
40833     /**
40834      * Executes a Midas editor command on the editor document and performs necessary focus and
40835      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
40836      * @param {String} cmd The Midas command
40837      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40838      */
40839     relayCmd : function(cmd, value){
40840         this.win.focus();
40841         this.execCmd(cmd, value);
40842         this.fireEvent('editorevent', this);
40843         //this.updateToolbar();
40844         this.deferFocus();
40845     },
40846
40847     /**
40848      * Executes a Midas editor command directly on the editor document.
40849      * For visual commands, you should use {@link #relayCmd} instead.
40850      * <b>This should only be called after the editor is initialized.</b>
40851      * @param {String} cmd The Midas command
40852      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
40853      */
40854     execCmd : function(cmd, value){
40855         this.doc.execCommand(cmd, false, value === undefined ? null : value);
40856         this.syncValue();
40857     },
40858  
40859  
40860    
40861     /**
40862      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
40863      * to insert tRoo.
40864      * @param {String} text | dom node.. 
40865      */
40866     insertAtCursor : function(text)
40867     {
40868         
40869         
40870         
40871         if(!this.activated){
40872             return;
40873         }
40874         /*
40875         if(Roo.isIE){
40876             this.win.focus();
40877             var r = this.doc.selection.createRange();
40878             if(r){
40879                 r.collapse(true);
40880                 r.pasteHTML(text);
40881                 this.syncValue();
40882                 this.deferFocus();
40883             
40884             }
40885             return;
40886         }
40887         */
40888         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
40889             this.win.focus();
40890             
40891             
40892             // from jquery ui (MIT licenced)
40893             var range, node;
40894             var win = this.win;
40895             
40896             if (win.getSelection && win.getSelection().getRangeAt) {
40897                 range = win.getSelection().getRangeAt(0);
40898                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
40899                 range.insertNode(node);
40900             } else if (win.document.selection && win.document.selection.createRange) {
40901                 // no firefox support
40902                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
40903                 win.document.selection.createRange().pasteHTML(txt);
40904             } else {
40905                 // no firefox support
40906                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
40907                 this.execCmd('InsertHTML', txt);
40908             } 
40909             
40910             this.syncValue();
40911             
40912             this.deferFocus();
40913         }
40914     },
40915  // private
40916     mozKeyPress : function(e){
40917         if(e.ctrlKey){
40918             var c = e.getCharCode(), cmd;
40919           
40920             if(c > 0){
40921                 c = String.fromCharCode(c).toLowerCase();
40922                 switch(c){
40923                     case 'b':
40924                         cmd = 'bold';
40925                         break;
40926                     case 'i':
40927                         cmd = 'italic';
40928                         break;
40929                     
40930                     case 'u':
40931                         cmd = 'underline';
40932                         break;
40933                     
40934                     case 'v':
40935                         this.cleanUpPaste.defer(100, this);
40936                         return;
40937                         
40938                 }
40939                 if(cmd){
40940                     this.win.focus();
40941                     this.execCmd(cmd);
40942                     this.deferFocus();
40943                     e.preventDefault();
40944                 }
40945                 
40946             }
40947         }
40948     },
40949
40950     // private
40951     fixKeys : function(){ // load time branching for fastest keydown performance
40952         if(Roo.isIE){
40953             return function(e){
40954                 var k = e.getKey(), r;
40955                 if(k == e.TAB){
40956                     e.stopEvent();
40957                     r = this.doc.selection.createRange();
40958                     if(r){
40959                         r.collapse(true);
40960                         r.pasteHTML('&#160;&#160;&#160;&#160;');
40961                         this.deferFocus();
40962                     }
40963                     return;
40964                 }
40965                 
40966                 if(k == e.ENTER){
40967                     r = this.doc.selection.createRange();
40968                     if(r){
40969                         var target = r.parentElement();
40970                         if(!target || target.tagName.toLowerCase() != 'li'){
40971                             e.stopEvent();
40972                             r.pasteHTML('<br />');
40973                             r.collapse(false);
40974                             r.select();
40975                         }
40976                     }
40977                 }
40978                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40979                     this.cleanUpPaste.defer(100, this);
40980                     return;
40981                 }
40982                 
40983                 
40984             };
40985         }else if(Roo.isOpera){
40986             return function(e){
40987                 var k = e.getKey();
40988                 if(k == e.TAB){
40989                     e.stopEvent();
40990                     this.win.focus();
40991                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
40992                     this.deferFocus();
40993                 }
40994                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
40995                     this.cleanUpPaste.defer(100, this);
40996                     return;
40997                 }
40998                 
40999             };
41000         }else if(Roo.isSafari){
41001             return function(e){
41002                 var k = e.getKey();
41003                 
41004                 if(k == e.TAB){
41005                     e.stopEvent();
41006                     this.execCmd('InsertText','\t');
41007                     this.deferFocus();
41008                     return;
41009                 }
41010                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41011                     this.cleanUpPaste.defer(100, this);
41012                     return;
41013                 }
41014                 
41015              };
41016         }
41017     }(),
41018     
41019     getAllAncestors: function()
41020     {
41021         var p = this.getSelectedNode();
41022         var a = [];
41023         if (!p) {
41024             a.push(p); // push blank onto stack..
41025             p = this.getParentElement();
41026         }
41027         
41028         
41029         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41030             a.push(p);
41031             p = p.parentNode;
41032         }
41033         a.push(this.doc.body);
41034         return a;
41035     },
41036     lastSel : false,
41037     lastSelNode : false,
41038     
41039     
41040     getSelection : function() 
41041     {
41042         this.assignDocWin();
41043         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41044     },
41045     
41046     getSelectedNode: function() 
41047     {
41048         // this may only work on Gecko!!!
41049         
41050         // should we cache this!!!!
41051         
41052         
41053         
41054          
41055         var range = this.createRange(this.getSelection()).cloneRange();
41056         
41057         if (Roo.isIE) {
41058             var parent = range.parentElement();
41059             while (true) {
41060                 var testRange = range.duplicate();
41061                 testRange.moveToElementText(parent);
41062                 if (testRange.inRange(range)) {
41063                     break;
41064                 }
41065                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41066                     break;
41067                 }
41068                 parent = parent.parentElement;
41069             }
41070             return parent;
41071         }
41072         
41073         // is ancestor a text element.
41074         var ac =  range.commonAncestorContainer;
41075         if (ac.nodeType == 3) {
41076             ac = ac.parentNode;
41077         }
41078         
41079         var ar = ac.childNodes;
41080          
41081         var nodes = [];
41082         var other_nodes = [];
41083         var has_other_nodes = false;
41084         for (var i=0;i<ar.length;i++) {
41085             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41086                 continue;
41087             }
41088             // fullly contained node.
41089             
41090             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41091                 nodes.push(ar[i]);
41092                 continue;
41093             }
41094             
41095             // probably selected..
41096             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41097                 other_nodes.push(ar[i]);
41098                 continue;
41099             }
41100             // outer..
41101             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41102                 continue;
41103             }
41104             
41105             
41106             has_other_nodes = true;
41107         }
41108         if (!nodes.length && other_nodes.length) {
41109             nodes= other_nodes;
41110         }
41111         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41112             return false;
41113         }
41114         
41115         return nodes[0];
41116     },
41117     createRange: function(sel)
41118     {
41119         // this has strange effects when using with 
41120         // top toolbar - not sure if it's a great idea.
41121         //this.editor.contentWindow.focus();
41122         if (typeof sel != "undefined") {
41123             try {
41124                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41125             } catch(e) {
41126                 return this.doc.createRange();
41127             }
41128         } else {
41129             return this.doc.createRange();
41130         }
41131     },
41132     getParentElement: function()
41133     {
41134         
41135         this.assignDocWin();
41136         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41137         
41138         var range = this.createRange(sel);
41139          
41140         try {
41141             var p = range.commonAncestorContainer;
41142             while (p.nodeType == 3) { // text node
41143                 p = p.parentNode;
41144             }
41145             return p;
41146         } catch (e) {
41147             return null;
41148         }
41149     
41150     },
41151     /***
41152      *
41153      * Range intersection.. the hard stuff...
41154      *  '-1' = before
41155      *  '0' = hits..
41156      *  '1' = after.
41157      *         [ -- selected range --- ]
41158      *   [fail]                        [fail]
41159      *
41160      *    basically..
41161      *      if end is before start or  hits it. fail.
41162      *      if start is after end or hits it fail.
41163      *
41164      *   if either hits (but other is outside. - then it's not 
41165      *   
41166      *    
41167      **/
41168     
41169     
41170     // @see http://www.thismuchiknow.co.uk/?p=64.
41171     rangeIntersectsNode : function(range, node)
41172     {
41173         var nodeRange = node.ownerDocument.createRange();
41174         try {
41175             nodeRange.selectNode(node);
41176         } catch (e) {
41177             nodeRange.selectNodeContents(node);
41178         }
41179     
41180         var rangeStartRange = range.cloneRange();
41181         rangeStartRange.collapse(true);
41182     
41183         var rangeEndRange = range.cloneRange();
41184         rangeEndRange.collapse(false);
41185     
41186         var nodeStartRange = nodeRange.cloneRange();
41187         nodeStartRange.collapse(true);
41188     
41189         var nodeEndRange = nodeRange.cloneRange();
41190         nodeEndRange.collapse(false);
41191     
41192         return rangeStartRange.compareBoundaryPoints(
41193                  Range.START_TO_START, nodeEndRange) == -1 &&
41194                rangeEndRange.compareBoundaryPoints(
41195                  Range.START_TO_START, nodeStartRange) == 1;
41196         
41197          
41198     },
41199     rangeCompareNode : function(range, node)
41200     {
41201         var nodeRange = node.ownerDocument.createRange();
41202         try {
41203             nodeRange.selectNode(node);
41204         } catch (e) {
41205             nodeRange.selectNodeContents(node);
41206         }
41207         
41208         
41209         range.collapse(true);
41210     
41211         nodeRange.collapse(true);
41212      
41213         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41214         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41215          
41216         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41217         
41218         var nodeIsBefore   =  ss == 1;
41219         var nodeIsAfter    = ee == -1;
41220         
41221         if (nodeIsBefore && nodeIsAfter)
41222             return 0; // outer
41223         if (!nodeIsBefore && nodeIsAfter)
41224             return 1; //right trailed.
41225         
41226         if (nodeIsBefore && !nodeIsAfter)
41227             return 2;  // left trailed.
41228         // fully contined.
41229         return 3;
41230     },
41231
41232     // private? - in a new class?
41233     cleanUpPaste :  function()
41234     {
41235         // cleans up the whole document..
41236          Roo.log('cleanuppaste');
41237         this.cleanUpChildren(this.doc.body);
41238         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41239         if (clean != this.doc.body.innerHTML) {
41240             this.doc.body.innerHTML = clean;
41241         }
41242         
41243     },
41244     
41245     cleanWordChars : function(input) {// change the chars to hex code
41246         var he = Roo.form.HtmlEditor;
41247         
41248         var output = input;
41249         Roo.each(he.swapCodes, function(sw) { 
41250             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41251             
41252             output = output.replace(swapper, sw[1]);
41253         });
41254         
41255         return output;
41256     },
41257     
41258     
41259     cleanUpChildren : function (n)
41260     {
41261         if (!n.childNodes.length) {
41262             return;
41263         }
41264         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41265            this.cleanUpChild(n.childNodes[i]);
41266         }
41267     },
41268     
41269     
41270         
41271     
41272     cleanUpChild : function (node)
41273     {
41274         var ed = this;
41275         //console.log(node);
41276         if (node.nodeName == "#text") {
41277             // clean up silly Windows -- stuff?
41278             return; 
41279         }
41280         if (node.nodeName == "#comment") {
41281             node.parentNode.removeChild(node);
41282             // clean up silly Windows -- stuff?
41283             return; 
41284         }
41285         
41286         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
41287             // remove node.
41288             node.parentNode.removeChild(node);
41289             return;
41290             
41291         }
41292         
41293         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
41294         
41295         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41296         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41297         
41298         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41299         //    remove_keep_children = true;
41300         //}
41301         
41302         if (remove_keep_children) {
41303             this.cleanUpChildren(node);
41304             // inserts everything just before this node...
41305             while (node.childNodes.length) {
41306                 var cn = node.childNodes[0];
41307                 node.removeChild(cn);
41308                 node.parentNode.insertBefore(cn, node);
41309             }
41310             node.parentNode.removeChild(node);
41311             return;
41312         }
41313         
41314         if (!node.attributes || !node.attributes.length) {
41315             this.cleanUpChildren(node);
41316             return;
41317         }
41318         
41319         function cleanAttr(n,v)
41320         {
41321             
41322             if (v.match(/^\./) || v.match(/^\//)) {
41323                 return;
41324             }
41325             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41326                 return;
41327             }
41328             if (v.match(/^#/)) {
41329                 return;
41330             }
41331 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41332             node.removeAttribute(n);
41333             
41334         }
41335         
41336         function cleanStyle(n,v)
41337         {
41338             if (v.match(/expression/)) { //XSS?? should we even bother..
41339                 node.removeAttribute(n);
41340                 return;
41341             }
41342             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
41343             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
41344             
41345             
41346             var parts = v.split(/;/);
41347             var clean = [];
41348             
41349             Roo.each(parts, function(p) {
41350                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
41351                 if (!p.length) {
41352                     return true;
41353                 }
41354                 var l = p.split(':').shift().replace(/\s+/g,'');
41355                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
41356                 
41357                 
41358                 if ( cblack.indexOf(l) > -1) {
41359 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41360                     //node.removeAttribute(n);
41361                     return true;
41362                 }
41363                 //Roo.log()
41364                 // only allow 'c whitelisted system attributes'
41365                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
41366 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41367                     //node.removeAttribute(n);
41368                     return true;
41369                 }
41370                 
41371                 
41372                  
41373                 
41374                 clean.push(p);
41375                 return true;
41376             });
41377             if (clean.length) { 
41378                 node.setAttribute(n, clean.join(';'));
41379             } else {
41380                 node.removeAttribute(n);
41381             }
41382             
41383         }
41384         
41385         
41386         for (var i = node.attributes.length-1; i > -1 ; i--) {
41387             var a = node.attributes[i];
41388             //console.log(a);
41389             
41390             if (a.name.toLowerCase().substr(0,2)=='on')  {
41391                 node.removeAttribute(a.name);
41392                 continue;
41393             }
41394             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
41395                 node.removeAttribute(a.name);
41396                 continue;
41397             }
41398             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
41399                 cleanAttr(a.name,a.value); // fixme..
41400                 continue;
41401             }
41402             if (a.name == 'style') {
41403                 cleanStyle(a.name,a.value);
41404                 continue;
41405             }
41406             /// clean up MS crap..
41407             // tecnically this should be a list of valid class'es..
41408             
41409             
41410             if (a.name == 'class') {
41411                 if (a.value.match(/^Mso/)) {
41412                     node.className = '';
41413                 }
41414                 
41415                 if (a.value.match(/body/)) {
41416                     node.className = '';
41417                 }
41418                 continue;
41419             }
41420             
41421             // style cleanup!?
41422             // class cleanup?
41423             
41424         }
41425         
41426         
41427         this.cleanUpChildren(node);
41428         
41429         
41430     }
41431     
41432     
41433     // hide stuff that is not compatible
41434     /**
41435      * @event blur
41436      * @hide
41437      */
41438     /**
41439      * @event change
41440      * @hide
41441      */
41442     /**
41443      * @event focus
41444      * @hide
41445      */
41446     /**
41447      * @event specialkey
41448      * @hide
41449      */
41450     /**
41451      * @cfg {String} fieldClass @hide
41452      */
41453     /**
41454      * @cfg {String} focusClass @hide
41455      */
41456     /**
41457      * @cfg {String} autoCreate @hide
41458      */
41459     /**
41460      * @cfg {String} inputType @hide
41461      */
41462     /**
41463      * @cfg {String} invalidClass @hide
41464      */
41465     /**
41466      * @cfg {String} invalidText @hide
41467      */
41468     /**
41469      * @cfg {String} msgFx @hide
41470      */
41471     /**
41472      * @cfg {String} validateOnBlur @hide
41473      */
41474 });
41475
41476 Roo.form.HtmlEditor.white = [
41477         'area', 'br', 'img', 'input', 'hr', 'wbr',
41478         
41479        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
41480        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
41481        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
41482        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
41483        'table',   'ul',         'xmp', 
41484        
41485        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
41486       'thead',   'tr', 
41487      
41488       'dir', 'menu', 'ol', 'ul', 'dl',
41489        
41490       'embed',  'object'
41491 ];
41492
41493
41494 Roo.form.HtmlEditor.black = [
41495     //    'embed',  'object', // enable - backend responsiblity to clean thiese
41496         'applet', // 
41497         'base',   'basefont', 'bgsound', 'blink',  'body', 
41498         'frame',  'frameset', 'head',    'html',   'ilayer', 
41499         'iframe', 'layer',  'link',     'meta',    'object',   
41500         'script', 'style' ,'title',  'xml' // clean later..
41501 ];
41502 Roo.form.HtmlEditor.clean = [
41503     'script', 'style', 'title', 'xml'
41504 ];
41505 Roo.form.HtmlEditor.remove = [
41506     'font'
41507 ];
41508 // attributes..
41509
41510 Roo.form.HtmlEditor.ablack = [
41511     'on'
41512 ];
41513     
41514 Roo.form.HtmlEditor.aclean = [ 
41515     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
41516 ];
41517
41518 // protocols..
41519 Roo.form.HtmlEditor.pwhite= [
41520         'http',  'https',  'mailto'
41521 ];
41522
41523 // white listed style attributes.
41524 Roo.form.HtmlEditor.cwhite= [
41525       //  'text-align', /// default is to allow most things..
41526       
41527          
41528 //        'font-size'//??
41529 ];
41530
41531 // black listed style attributes.
41532 Roo.form.HtmlEditor.cblack= [
41533       //  'font-size' -- this can be set by the project 
41534 ];
41535
41536
41537 Roo.form.HtmlEditor.swapCodes   =[ 
41538     [    8211, "--" ], 
41539     [    8212, "--" ], 
41540     [    8216,  "'" ],  
41541     [    8217, "'" ],  
41542     [    8220, '"' ],  
41543     [    8221, '"' ],  
41544     [    8226, "*" ],  
41545     [    8230, "..." ]
41546 ]; 
41547
41548     // <script type="text/javascript">
41549 /*
41550  * Based on
41551  * Ext JS Library 1.1.1
41552  * Copyright(c) 2006-2007, Ext JS, LLC.
41553  *  
41554  
41555  */
41556
41557 /**
41558  * @class Roo.form.HtmlEditorToolbar1
41559  * Basic Toolbar
41560  * 
41561  * Usage:
41562  *
41563  new Roo.form.HtmlEditor({
41564     ....
41565     toolbars : [
41566         new Roo.form.HtmlEditorToolbar1({
41567             disable : { fonts: 1 , format: 1, ..., ... , ...],
41568             btns : [ .... ]
41569         })
41570     }
41571      
41572  * 
41573  * @cfg {Object} disable List of elements to disable..
41574  * @cfg {Array} btns List of additional buttons.
41575  * 
41576  * 
41577  * NEEDS Extra CSS? 
41578  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
41579  */
41580  
41581 Roo.form.HtmlEditor.ToolbarStandard = function(config)
41582 {
41583     
41584     Roo.apply(this, config);
41585     
41586     // default disabled, based on 'good practice'..
41587     this.disable = this.disable || {};
41588     Roo.applyIf(this.disable, {
41589         fontSize : true,
41590         colors : true,
41591         specialElements : true
41592     });
41593     
41594     
41595     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
41596     // dont call parent... till later.
41597 }
41598
41599 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
41600     
41601     tb: false,
41602     
41603     rendered: false,
41604     
41605     editor : false,
41606     /**
41607      * @cfg {Object} disable  List of toolbar elements to disable
41608          
41609      */
41610     disable : false,
41611       /**
41612      * @cfg {Array} fontFamilies An array of available font families
41613      */
41614     fontFamilies : [
41615         'Arial',
41616         'Courier New',
41617         'Tahoma',
41618         'Times New Roman',
41619         'Verdana'
41620     ],
41621     
41622     specialChars : [
41623            "&#169;",
41624           "&#174;",     
41625           "&#8482;",    
41626           "&#163;" ,    
41627          // "&#8212;",    
41628           "&#8230;",    
41629           "&#247;" ,    
41630         //  "&#225;" ,     ?? a acute?
41631            "&#8364;"    , //Euro
41632        //   "&#8220;"    ,
41633         //  "&#8221;"    ,
41634         //  "&#8226;"    ,
41635           "&#176;"  //   , // degrees
41636
41637          // "&#233;"     , // e ecute
41638          // "&#250;"     , // u ecute?
41639     ],
41640     
41641     specialElements : [
41642         {
41643             text: "Insert Table",
41644             xtype: 'MenuItem',
41645             xns : Roo.Menu,
41646             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
41647                 
41648         },
41649         {    
41650             text: "Insert Image",
41651             xtype: 'MenuItem',
41652             xns : Roo.Menu,
41653             ihtml : '<img src="about:blank"/>'
41654             
41655         }
41656         
41657          
41658     ],
41659     
41660     
41661     inputElements : [ 
41662             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
41663             "input:submit", "input:button", "select", "textarea", "label" ],
41664     formats : [
41665         ["p"] ,  
41666         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
41667         ["pre"],[ "code"], 
41668         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
41669         ['div'],['span']
41670     ],
41671      /**
41672      * @cfg {String} defaultFont default font to use.
41673      */
41674     defaultFont: 'tahoma',
41675    
41676     fontSelect : false,
41677     
41678     
41679     formatCombo : false,
41680     
41681     init : function(editor)
41682     {
41683         this.editor = editor;
41684         
41685         
41686         var fid = editor.frameId;
41687         var etb = this;
41688         function btn(id, toggle, handler){
41689             var xid = fid + '-'+ id ;
41690             return {
41691                 id : xid,
41692                 cmd : id,
41693                 cls : 'x-btn-icon x-edit-'+id,
41694                 enableToggle:toggle !== false,
41695                 scope: editor, // was editor...
41696                 handler:handler||editor.relayBtnCmd,
41697                 clickEvent:'mousedown',
41698                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
41699                 tabIndex:-1
41700             };
41701         }
41702         
41703         
41704         
41705         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
41706         this.tb = tb;
41707          // stop form submits
41708         tb.el.on('click', function(e){
41709             e.preventDefault(); // what does this do?
41710         });
41711
41712         if(!this.disable.font) { // && !Roo.isSafari){
41713             /* why no safari for fonts 
41714             editor.fontSelect = tb.el.createChild({
41715                 tag:'select',
41716                 tabIndex: -1,
41717                 cls:'x-font-select',
41718                 html: this.createFontOptions()
41719             });
41720             
41721             editor.fontSelect.on('change', function(){
41722                 var font = editor.fontSelect.dom.value;
41723                 editor.relayCmd('fontname', font);
41724                 editor.deferFocus();
41725             }, editor);
41726             
41727             tb.add(
41728                 editor.fontSelect.dom,
41729                 '-'
41730             );
41731             */
41732             
41733         };
41734         if(!this.disable.formats){
41735             this.formatCombo = new Roo.form.ComboBox({
41736                 store: new Roo.data.SimpleStore({
41737                     id : 'tag',
41738                     fields: ['tag'],
41739                     data : this.formats // from states.js
41740                 }),
41741                 blockFocus : true,
41742                 name : '',
41743                 //autoCreate : {tag: "div",  size: "20"},
41744                 displayField:'tag',
41745                 typeAhead: false,
41746                 mode: 'local',
41747                 editable : false,
41748                 triggerAction: 'all',
41749                 emptyText:'Add tag',
41750                 selectOnFocus:true,
41751                 width:135,
41752                 listeners : {
41753                     'select': function(c, r, i) {
41754                         editor.insertTag(r.get('tag'));
41755                         editor.focus();
41756                     }
41757                 }
41758
41759             });
41760             tb.addField(this.formatCombo);
41761             
41762         }
41763         
41764         if(!this.disable.format){
41765             tb.add(
41766                 btn('bold'),
41767                 btn('italic'),
41768                 btn('underline')
41769             );
41770         };
41771         if(!this.disable.fontSize){
41772             tb.add(
41773                 '-',
41774                 
41775                 
41776                 btn('increasefontsize', false, editor.adjustFont),
41777                 btn('decreasefontsize', false, editor.adjustFont)
41778             );
41779         };
41780         
41781         
41782         if(!this.disable.colors){
41783             tb.add(
41784                 '-', {
41785                     id:editor.frameId +'-forecolor',
41786                     cls:'x-btn-icon x-edit-forecolor',
41787                     clickEvent:'mousedown',
41788                     tooltip: this.buttonTips['forecolor'] || undefined,
41789                     tabIndex:-1,
41790                     menu : new Roo.menu.ColorMenu({
41791                         allowReselect: true,
41792                         focus: Roo.emptyFn,
41793                         value:'000000',
41794                         plain:true,
41795                         selectHandler: function(cp, color){
41796                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
41797                             editor.deferFocus();
41798                         },
41799                         scope: editor,
41800                         clickEvent:'mousedown'
41801                     })
41802                 }, {
41803                     id:editor.frameId +'backcolor',
41804                     cls:'x-btn-icon x-edit-backcolor',
41805                     clickEvent:'mousedown',
41806                     tooltip: this.buttonTips['backcolor'] || undefined,
41807                     tabIndex:-1,
41808                     menu : new Roo.menu.ColorMenu({
41809                         focus: Roo.emptyFn,
41810                         value:'FFFFFF',
41811                         plain:true,
41812                         allowReselect: true,
41813                         selectHandler: function(cp, color){
41814                             if(Roo.isGecko){
41815                                 editor.execCmd('useCSS', false);
41816                                 editor.execCmd('hilitecolor', color);
41817                                 editor.execCmd('useCSS', true);
41818                                 editor.deferFocus();
41819                             }else{
41820                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
41821                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
41822                                 editor.deferFocus();
41823                             }
41824                         },
41825                         scope:editor,
41826                         clickEvent:'mousedown'
41827                     })
41828                 }
41829             );
41830         };
41831         // now add all the items...
41832         
41833
41834         if(!this.disable.alignments){
41835             tb.add(
41836                 '-',
41837                 btn('justifyleft'),
41838                 btn('justifycenter'),
41839                 btn('justifyright')
41840             );
41841         };
41842
41843         //if(!Roo.isSafari){
41844             if(!this.disable.links){
41845                 tb.add(
41846                     '-',
41847                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
41848                 );
41849             };
41850
41851             if(!this.disable.lists){
41852                 tb.add(
41853                     '-',
41854                     btn('insertorderedlist'),
41855                     btn('insertunorderedlist')
41856                 );
41857             }
41858             if(!this.disable.sourceEdit){
41859                 tb.add(
41860                     '-',
41861                     btn('sourceedit', true, function(btn){
41862                         this.toggleSourceEdit(btn.pressed);
41863                     })
41864                 );
41865             }
41866         //}
41867         
41868         var smenu = { };
41869         // special menu.. - needs to be tidied up..
41870         if (!this.disable.special) {
41871             smenu = {
41872                 text: "&#169;",
41873                 cls: 'x-edit-none',
41874                 
41875                 menu : {
41876                     items : []
41877                 }
41878             };
41879             for (var i =0; i < this.specialChars.length; i++) {
41880                 smenu.menu.items.push({
41881                     
41882                     html: this.specialChars[i],
41883                     handler: function(a,b) {
41884                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
41885                         //editor.insertAtCursor(a.html);
41886                         
41887                     },
41888                     tabIndex:-1
41889                 });
41890             }
41891             
41892             
41893             tb.add(smenu);
41894             
41895             
41896         }
41897          
41898         if (!this.disable.specialElements) {
41899             var semenu = {
41900                 text: "Other;",
41901                 cls: 'x-edit-none',
41902                 menu : {
41903                     items : []
41904                 }
41905             };
41906             for (var i =0; i < this.specialElements.length; i++) {
41907                 semenu.menu.items.push(
41908                     Roo.apply({ 
41909                         handler: function(a,b) {
41910                             editor.insertAtCursor(this.ihtml);
41911                         }
41912                     }, this.specialElements[i])
41913                 );
41914                     
41915             }
41916             
41917             tb.add(semenu);
41918             
41919             
41920         }
41921          
41922         
41923         if (this.btns) {
41924             for(var i =0; i< this.btns.length;i++) {
41925                 var b = Roo.factory(this.btns[i],Roo.form);
41926                 b.cls =  'x-edit-none';
41927                 b.scope = editor;
41928                 tb.add(b);
41929             }
41930         
41931         }
41932         
41933         
41934         
41935         // disable everything...
41936         
41937         this.tb.items.each(function(item){
41938            if(item.id != editor.frameId+ '-sourceedit'){
41939                 item.disable();
41940             }
41941         });
41942         this.rendered = true;
41943         
41944         // the all the btns;
41945         editor.on('editorevent', this.updateToolbar, this);
41946         // other toolbars need to implement this..
41947         //editor.on('editmodechange', this.updateToolbar, this);
41948     },
41949     
41950     
41951     
41952     /**
41953      * Protected method that will not generally be called directly. It triggers
41954      * a toolbar update by reading the markup state of the current selection in the editor.
41955      */
41956     updateToolbar: function(){
41957
41958         if(!this.editor.activated){
41959             this.editor.onFirstFocus();
41960             return;
41961         }
41962
41963         var btns = this.tb.items.map, 
41964             doc = this.editor.doc,
41965             frameId = this.editor.frameId;
41966
41967         if(!this.disable.font && !Roo.isSafari){
41968             /*
41969             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
41970             if(name != this.fontSelect.dom.value){
41971                 this.fontSelect.dom.value = name;
41972             }
41973             */
41974         }
41975         if(!this.disable.format){
41976             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
41977             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
41978             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
41979         }
41980         if(!this.disable.alignments){
41981             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
41982             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
41983             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
41984         }
41985         if(!Roo.isSafari && !this.disable.lists){
41986             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
41987             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
41988         }
41989         
41990         var ans = this.editor.getAllAncestors();
41991         if (this.formatCombo) {
41992             
41993             
41994             var store = this.formatCombo.store;
41995             this.formatCombo.setValue("");
41996             for (var i =0; i < ans.length;i++) {
41997                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
41998                     // select it..
41999                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
42000                     break;
42001                 }
42002             }
42003         }
42004         
42005         
42006         
42007         // hides menus... - so this cant be on a menu...
42008         Roo.menu.MenuMgr.hideAll();
42009
42010         //this.editorsyncValue();
42011     },
42012    
42013     
42014     createFontOptions : function(){
42015         var buf = [], fs = this.fontFamilies, ff, lc;
42016         
42017         
42018         
42019         for(var i = 0, len = fs.length; i< len; i++){
42020             ff = fs[i];
42021             lc = ff.toLowerCase();
42022             buf.push(
42023                 '<option value="',lc,'" style="font-family:',ff,';"',
42024                     (this.defaultFont == lc ? ' selected="true">' : '>'),
42025                     ff,
42026                 '</option>'
42027             );
42028         }
42029         return buf.join('');
42030     },
42031     
42032     toggleSourceEdit : function(sourceEditMode){
42033         if(sourceEditMode === undefined){
42034             sourceEditMode = !this.sourceEditMode;
42035         }
42036         this.sourceEditMode = sourceEditMode === true;
42037         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
42038         // just toggle the button?
42039         if(btn.pressed !== this.editor.sourceEditMode){
42040             btn.toggle(this.editor.sourceEditMode);
42041             return;
42042         }
42043         
42044         if(this.sourceEditMode){
42045             this.tb.items.each(function(item){
42046                 if(item.cmd != 'sourceedit'){
42047                     item.disable();
42048                 }
42049             });
42050           
42051         }else{
42052             if(this.initialized){
42053                 this.tb.items.each(function(item){
42054                     item.enable();
42055                 });
42056             }
42057             
42058         }
42059         // tell the editor that it's been pressed..
42060         this.editor.toggleSourceEdit(sourceEditMode);
42061        
42062     },
42063      /**
42064      * Object collection of toolbar tooltips for the buttons in the editor. The key
42065      * is the command id associated with that button and the value is a valid QuickTips object.
42066      * For example:
42067 <pre><code>
42068 {
42069     bold : {
42070         title: 'Bold (Ctrl+B)',
42071         text: 'Make the selected text bold.',
42072         cls: 'x-html-editor-tip'
42073     },
42074     italic : {
42075         title: 'Italic (Ctrl+I)',
42076         text: 'Make the selected text italic.',
42077         cls: 'x-html-editor-tip'
42078     },
42079     ...
42080 </code></pre>
42081     * @type Object
42082      */
42083     buttonTips : {
42084         bold : {
42085             title: 'Bold (Ctrl+B)',
42086             text: 'Make the selected text bold.',
42087             cls: 'x-html-editor-tip'
42088         },
42089         italic : {
42090             title: 'Italic (Ctrl+I)',
42091             text: 'Make the selected text italic.',
42092             cls: 'x-html-editor-tip'
42093         },
42094         underline : {
42095             title: 'Underline (Ctrl+U)',
42096             text: 'Underline the selected text.',
42097             cls: 'x-html-editor-tip'
42098         },
42099         increasefontsize : {
42100             title: 'Grow Text',
42101             text: 'Increase the font size.',
42102             cls: 'x-html-editor-tip'
42103         },
42104         decreasefontsize : {
42105             title: 'Shrink Text',
42106             text: 'Decrease the font size.',
42107             cls: 'x-html-editor-tip'
42108         },
42109         backcolor : {
42110             title: 'Text Highlight Color',
42111             text: 'Change the background color of the selected text.',
42112             cls: 'x-html-editor-tip'
42113         },
42114         forecolor : {
42115             title: 'Font Color',
42116             text: 'Change the color of the selected text.',
42117             cls: 'x-html-editor-tip'
42118         },
42119         justifyleft : {
42120             title: 'Align Text Left',
42121             text: 'Align text to the left.',
42122             cls: 'x-html-editor-tip'
42123         },
42124         justifycenter : {
42125             title: 'Center Text',
42126             text: 'Center text in the editor.',
42127             cls: 'x-html-editor-tip'
42128         },
42129         justifyright : {
42130             title: 'Align Text Right',
42131             text: 'Align text to the right.',
42132             cls: 'x-html-editor-tip'
42133         },
42134         insertunorderedlist : {
42135             title: 'Bullet List',
42136             text: 'Start a bulleted list.',
42137             cls: 'x-html-editor-tip'
42138         },
42139         insertorderedlist : {
42140             title: 'Numbered List',
42141             text: 'Start a numbered list.',
42142             cls: 'x-html-editor-tip'
42143         },
42144         createlink : {
42145             title: 'Hyperlink',
42146             text: 'Make the selected text a hyperlink.',
42147             cls: 'x-html-editor-tip'
42148         },
42149         sourceedit : {
42150             title: 'Source Edit',
42151             text: 'Switch to source editing mode.',
42152             cls: 'x-html-editor-tip'
42153         }
42154     },
42155     // private
42156     onDestroy : function(){
42157         if(this.rendered){
42158             
42159             this.tb.items.each(function(item){
42160                 if(item.menu){
42161                     item.menu.removeAll();
42162                     if(item.menu.el){
42163                         item.menu.el.destroy();
42164                     }
42165                 }
42166                 item.destroy();
42167             });
42168              
42169         }
42170     },
42171     onFirstFocus: function() {
42172         this.tb.items.each(function(item){
42173            item.enable();
42174         });
42175     }
42176 });
42177
42178
42179
42180
42181 // <script type="text/javascript">
42182 /*
42183  * Based on
42184  * Ext JS Library 1.1.1
42185  * Copyright(c) 2006-2007, Ext JS, LLC.
42186  *  
42187  
42188  */
42189
42190  
42191 /**
42192  * @class Roo.form.HtmlEditor.ToolbarContext
42193  * Context Toolbar
42194  * 
42195  * Usage:
42196  *
42197  new Roo.form.HtmlEditor({
42198     ....
42199     toolbars : [
42200         { xtype: 'ToolbarStandard', styles : {} }
42201         { xtype: 'ToolbarContext', disable : {} }
42202     ]
42203 })
42204
42205      
42206  * 
42207  * @config : {Object} disable List of elements to disable.. (not done yet.)
42208  * @config : {Object} styles  Map of styles available.
42209  * 
42210  */
42211
42212 Roo.form.HtmlEditor.ToolbarContext = function(config)
42213 {
42214     
42215     Roo.apply(this, config);
42216     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42217     // dont call parent... till later.
42218     this.styles = this.styles || {};
42219 }
42220
42221  
42222
42223 Roo.form.HtmlEditor.ToolbarContext.types = {
42224     'IMG' : {
42225         width : {
42226             title: "Width",
42227             width: 40
42228         },
42229         height:  {
42230             title: "Height",
42231             width: 40
42232         },
42233         align: {
42234             title: "Align",
42235             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
42236             width : 80
42237             
42238         },
42239         border: {
42240             title: "Border",
42241             width: 40
42242         },
42243         alt: {
42244             title: "Alt",
42245             width: 120
42246         },
42247         src : {
42248             title: "Src",
42249             width: 220
42250         }
42251         
42252     },
42253     'A' : {
42254         name : {
42255             title: "Name",
42256             width: 50
42257         },
42258         href:  {
42259             title: "Href",
42260             width: 220
42261         } // border?
42262         
42263     },
42264     'TABLE' : {
42265         rows : {
42266             title: "Rows",
42267             width: 20
42268         },
42269         cols : {
42270             title: "Cols",
42271             width: 20
42272         },
42273         width : {
42274             title: "Width",
42275             width: 40
42276         },
42277         height : {
42278             title: "Height",
42279             width: 40
42280         },
42281         border : {
42282             title: "Border",
42283             width: 20
42284         }
42285     },
42286     'TD' : {
42287         width : {
42288             title: "Width",
42289             width: 40
42290         },
42291         height : {
42292             title: "Height",
42293             width: 40
42294         },   
42295         align: {
42296             title: "Align",
42297             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
42298             width: 80
42299         },
42300         valign: {
42301             title: "Valign",
42302             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
42303             width: 80
42304         },
42305         colspan: {
42306             title: "Colspan",
42307             width: 20
42308             
42309         },
42310          'font-family'  : {
42311             title : "Font",
42312             style : 'fontFamily',
42313             displayField: 'display',
42314             optname : 'font-family',
42315             width: 140
42316         }
42317     },
42318     'INPUT' : {
42319         name : {
42320             title: "name",
42321             width: 120
42322         },
42323         value : {
42324             title: "Value",
42325             width: 120
42326         },
42327         width : {
42328             title: "Width",
42329             width: 40
42330         }
42331     },
42332     'LABEL' : {
42333         'for' : {
42334             title: "For",
42335             width: 120
42336         }
42337     },
42338     'TEXTAREA' : {
42339           name : {
42340             title: "name",
42341             width: 120
42342         },
42343         rows : {
42344             title: "Rows",
42345             width: 20
42346         },
42347         cols : {
42348             title: "Cols",
42349             width: 20
42350         }
42351     },
42352     'SELECT' : {
42353         name : {
42354             title: "name",
42355             width: 120
42356         },
42357         selectoptions : {
42358             title: "Options",
42359             width: 200
42360         }
42361     },
42362     
42363     // should we really allow this??
42364     // should this just be 
42365     'BODY' : {
42366         title : {
42367             title: "Title",
42368             width: 200,
42369             disabled : true
42370         }
42371     },
42372     'SPAN' : {
42373         'font-family'  : {
42374             title : "Font",
42375             style : 'fontFamily',
42376             displayField: 'display',
42377             optname : 'font-family',
42378             width: 140
42379         }
42380     },
42381     'DIV' : {
42382         'font-family'  : {
42383             title : "Font",
42384             style : 'fontFamily',
42385             displayField: 'display',
42386             optname : 'font-family',
42387             width: 140
42388         }
42389     },
42390      'P' : {
42391         'font-family'  : {
42392             title : "Font",
42393             style : 'fontFamily',
42394             displayField: 'display',
42395             optname : 'font-family',
42396             width: 140
42397         }
42398     },
42399     
42400     '*' : {
42401         // empty..
42402     }
42403
42404 };
42405
42406 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
42407 Roo.form.HtmlEditor.ToolbarContext.stores = false;
42408
42409 Roo.form.HtmlEditor.ToolbarContext.options = {
42410         'font-family'  : [ 
42411                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
42412                 [ 'Courier New', 'Courier New'],
42413                 [ 'Tahoma', 'Tahoma'],
42414                 [ 'Times New Roman,serif', 'Times'],
42415                 [ 'Verdana','Verdana' ]
42416         ]
42417 };
42418
42419 // fixme - these need to be configurable..
42420  
42421
42422 Roo.form.HtmlEditor.ToolbarContext.types
42423
42424
42425 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
42426     
42427     tb: false,
42428     
42429     rendered: false,
42430     
42431     editor : false,
42432     /**
42433      * @cfg {Object} disable  List of toolbar elements to disable
42434          
42435      */
42436     disable : false,
42437     /**
42438      * @cfg {Object} styles List of styles 
42439      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
42440      *
42441      * These must be defined in the page, so they get rendered correctly..
42442      * .headline { }
42443      * TD.underline { }
42444      * 
42445      */
42446     styles : false,
42447     
42448     options: false,
42449     
42450     toolbars : false,
42451     
42452     init : function(editor)
42453     {
42454         this.editor = editor;
42455         
42456         
42457         var fid = editor.frameId;
42458         var etb = this;
42459         function btn(id, toggle, handler){
42460             var xid = fid + '-'+ id ;
42461             return {
42462                 id : xid,
42463                 cmd : id,
42464                 cls : 'x-btn-icon x-edit-'+id,
42465                 enableToggle:toggle !== false,
42466                 scope: editor, // was editor...
42467                 handler:handler||editor.relayBtnCmd,
42468                 clickEvent:'mousedown',
42469                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42470                 tabIndex:-1
42471             };
42472         }
42473         // create a new element.
42474         var wdiv = editor.wrap.createChild({
42475                 tag: 'div'
42476             }, editor.wrap.dom.firstChild.nextSibling, true);
42477         
42478         // can we do this more than once??
42479         
42480          // stop form submits
42481       
42482  
42483         // disable everything...
42484         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
42485         this.toolbars = {};
42486            
42487         for (var i in  ty) {
42488           
42489             this.toolbars[i] = this.buildToolbar(ty[i],i);
42490         }
42491         this.tb = this.toolbars.BODY;
42492         this.tb.el.show();
42493         this.buildFooter();
42494         this.footer.show();
42495         editor.on('hide', function( ) { this.footer.hide() }, this);
42496         editor.on('show', function( ) { this.footer.show() }, this);
42497         
42498          
42499         this.rendered = true;
42500         
42501         // the all the btns;
42502         editor.on('editorevent', this.updateToolbar, this);
42503         // other toolbars need to implement this..
42504         //editor.on('editmodechange', this.updateToolbar, this);
42505     },
42506     
42507     
42508     
42509     /**
42510      * Protected method that will not generally be called directly. It triggers
42511      * a toolbar update by reading the markup state of the current selection in the editor.
42512      */
42513     updateToolbar: function(editor,ev,sel){
42514
42515         //Roo.log(ev);
42516         // capture mouse up - this is handy for selecting images..
42517         // perhaps should go somewhere else...
42518         if(!this.editor.activated){
42519              this.editor.onFirstFocus();
42520             return;
42521         }
42522         
42523         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
42524         // selectNode - might want to handle IE?
42525         if (ev &&
42526             (ev.type == 'mouseup' || ev.type == 'click' ) &&
42527             ev.target && ev.target.tagName == 'IMG') {
42528             // they have click on an image...
42529             // let's see if we can change the selection...
42530             sel = ev.target;
42531          
42532               var nodeRange = sel.ownerDocument.createRange();
42533             try {
42534                 nodeRange.selectNode(sel);
42535             } catch (e) {
42536                 nodeRange.selectNodeContents(sel);
42537             }
42538             //nodeRange.collapse(true);
42539             var s = editor.win.getSelection();
42540             s.removeAllRanges();
42541             s.addRange(nodeRange);
42542         }  
42543         
42544       
42545         var updateFooter = sel ? false : true;
42546         
42547         
42548         var ans = this.editor.getAllAncestors();
42549         
42550         // pick
42551         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
42552         
42553         if (!sel) { 
42554             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
42555             sel = sel ? sel : this.editor.doc.body;
42556             sel = sel.tagName.length ? sel : this.editor.doc.body;
42557             
42558         }
42559         // pick a menu that exists..
42560         var tn = sel.tagName.toUpperCase();
42561         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
42562         
42563         tn = sel.tagName.toUpperCase();
42564         
42565         var lastSel = this.tb.selectedNode
42566         
42567         this.tb.selectedNode = sel;
42568         
42569         // if current menu does not match..
42570         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
42571                 
42572             this.tb.el.hide();
42573             ///console.log("show: " + tn);
42574             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
42575             this.tb.el.show();
42576             // update name
42577             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
42578             
42579             
42580             // update attributes
42581             if (this.tb.fields) {
42582                 this.tb.fields.each(function(e) {
42583                     if (e.stylename) {
42584                         e.setValue(sel.style[e.stylename]);
42585                         return;
42586                     } 
42587                    e.setValue(sel.getAttribute(e.attrname));
42588                 });
42589             }
42590             
42591             var hasStyles = false;
42592             for(var i in this.styles) {
42593                 hasStyles = true;
42594                 break;
42595             }
42596             
42597             // update styles
42598             if (hasStyles) { 
42599                 var st = this.tb.fields.item(0);
42600                 
42601                 st.store.removeAll();
42602                
42603                 
42604                 var cn = sel.className.split(/\s+/);
42605                 
42606                 var avs = [];
42607                 if (this.styles['*']) {
42608                     
42609                     Roo.each(this.styles['*'], function(v) {
42610                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
42611                     });
42612                 }
42613                 if (this.styles[tn]) { 
42614                     Roo.each(this.styles[tn], function(v) {
42615                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
42616                     });
42617                 }
42618                 
42619                 st.store.loadData(avs);
42620                 st.collapse();
42621                 st.setValue(cn);
42622             }
42623             // flag our selected Node.
42624             this.tb.selectedNode = sel;
42625            
42626            
42627             Roo.menu.MenuMgr.hideAll();
42628
42629         }
42630         
42631         if (!updateFooter) {
42632             //this.footDisp.dom.innerHTML = ''; 
42633             return;
42634         }
42635         // update the footer
42636         //
42637         var html = '';
42638         
42639         this.footerEls = ans.reverse();
42640         Roo.each(this.footerEls, function(a,i) {
42641             if (!a) { return; }
42642             html += html.length ? ' &gt; '  :  '';
42643             
42644             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
42645             
42646         });
42647        
42648         // 
42649         var sz = this.footDisp.up('td').getSize();
42650         this.footDisp.dom.style.width = (sz.width -10) + 'px';
42651         this.footDisp.dom.style.marginLeft = '5px';
42652         
42653         this.footDisp.dom.style.overflow = 'hidden';
42654         
42655         this.footDisp.dom.innerHTML = html;
42656             
42657         //this.editorsyncValue();
42658     },
42659      
42660     
42661    
42662        
42663     // private
42664     onDestroy : function(){
42665         if(this.rendered){
42666             
42667             this.tb.items.each(function(item){
42668                 if(item.menu){
42669                     item.menu.removeAll();
42670                     if(item.menu.el){
42671                         item.menu.el.destroy();
42672                     }
42673                 }
42674                 item.destroy();
42675             });
42676              
42677         }
42678     },
42679     onFirstFocus: function() {
42680         // need to do this for all the toolbars..
42681         this.tb.items.each(function(item){
42682            item.enable();
42683         });
42684     },
42685     buildToolbar: function(tlist, nm)
42686     {
42687         var editor = this.editor;
42688          // create a new element.
42689         var wdiv = editor.wrap.createChild({
42690                 tag: 'div'
42691             }, editor.wrap.dom.firstChild.nextSibling, true);
42692         
42693        
42694         var tb = new Roo.Toolbar(wdiv);
42695         // add the name..
42696         
42697         tb.add(nm+ ":&nbsp;");
42698         
42699         var styles = [];
42700         for(var i in this.styles) {
42701             styles.push(i);
42702         }
42703         
42704         // styles...
42705         if (styles && styles.length) {
42706             
42707             // this needs a multi-select checkbox...
42708             tb.addField( new Roo.form.ComboBox({
42709                 store: new Roo.data.SimpleStore({
42710                     id : 'val',
42711                     fields: ['val', 'selected'],
42712                     data : [] 
42713                 }),
42714                 name : '-roo-edit-className',
42715                 attrname : 'className',
42716                 displayField: 'val',
42717                 typeAhead: false,
42718                 mode: 'local',
42719                 editable : false,
42720                 triggerAction: 'all',
42721                 emptyText:'Select Style',
42722                 selectOnFocus:true,
42723                 width: 130,
42724                 listeners : {
42725                     'select': function(c, r, i) {
42726                         // initial support only for on class per el..
42727                         tb.selectedNode.className =  r ? r.get('val') : '';
42728                         editor.syncValue();
42729                     }
42730                 }
42731     
42732             }));
42733         }
42734         
42735         var tbc = Roo.form.HtmlEditor.ToolbarContext;
42736         var tbops = tbc.options;
42737         
42738         for (var i in tlist) {
42739             
42740             var item = tlist[i];
42741             tb.add(item.title + ":&nbsp;");
42742             
42743             
42744             //optname == used so you can configure the options available..
42745             var opts = item.opts ? item.opts : false;
42746             if (item.optname) {
42747                 opts = tbops[item.optname];
42748            
42749             }
42750             
42751             if (opts) {
42752                 // opts == pulldown..
42753                 tb.addField( new Roo.form.ComboBox({
42754                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
42755                         id : 'val',
42756                         fields: ['val', 'display'],
42757                         data : opts  
42758                     }),
42759                     name : '-roo-edit-' + i,
42760                     attrname : i,
42761                     stylename : item.style ? item.style : false,
42762                     displayField: item.displayField ? item.displayField : 'val',
42763                     valueField :  'val',
42764                     typeAhead: false,
42765                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
42766                     editable : false,
42767                     triggerAction: 'all',
42768                     emptyText:'Select',
42769                     selectOnFocus:true,
42770                     width: item.width ? item.width  : 130,
42771                     listeners : {
42772                         'select': function(c, r, i) {
42773                             if (c.stylename) {
42774                                 tb.selectedNode.style[c.stylename] =  r.get('val');
42775                                 return;
42776                             }
42777                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
42778                         }
42779                     }
42780
42781                 }));
42782                 continue;
42783                     
42784                  
42785                 
42786                 tb.addField( new Roo.form.TextField({
42787                     name: i,
42788                     width: 100,
42789                     //allowBlank:false,
42790                     value: ''
42791                 }));
42792                 continue;
42793             }
42794             tb.addField( new Roo.form.TextField({
42795                 name: '-roo-edit-' + i,
42796                 attrname : i,
42797                 
42798                 width: item.width,
42799                 //allowBlank:true,
42800                 value: '',
42801                 listeners: {
42802                     'change' : function(f, nv, ov) {
42803                         tb.selectedNode.setAttribute(f.attrname, nv);
42804                     }
42805                 }
42806             }));
42807              
42808         }
42809         tb.addFill();
42810         var _this = this;
42811         tb.addButton( {
42812             text: 'Remove Tag',
42813     
42814             listeners : {
42815                 click : function ()
42816                 {
42817                     // remove
42818                     // undo does not work.
42819                      
42820                     var sn = tb.selectedNode;
42821                     
42822                     var pn = sn.parentNode;
42823                     
42824                     var stn =  sn.childNodes[0];
42825                     var en = sn.childNodes[sn.childNodes.length - 1 ];
42826                     while (sn.childNodes.length) {
42827                         var node = sn.childNodes[0];
42828                         sn.removeChild(node);
42829                         //Roo.log(node);
42830                         pn.insertBefore(node, sn);
42831                         
42832                     }
42833                     pn.removeChild(sn);
42834                     var range = editor.createRange();
42835         
42836                     range.setStart(stn,0);
42837                     range.setEnd(en,0); //????
42838                     //range.selectNode(sel);
42839                     
42840                     
42841                     var selection = editor.getSelection();
42842                     selection.removeAllRanges();
42843                     selection.addRange(range);
42844                     
42845                     
42846                     
42847                     //_this.updateToolbar(null, null, pn);
42848                     _this.updateToolbar(null, null, null);
42849                     _this.footDisp.dom.innerHTML = ''; 
42850                 }
42851             }
42852             
42853                     
42854                 
42855             
42856         });
42857         
42858         
42859         tb.el.on('click', function(e){
42860             e.preventDefault(); // what does this do?
42861         });
42862         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
42863         tb.el.hide();
42864         tb.name = nm;
42865         // dont need to disable them... as they will get hidden
42866         return tb;
42867          
42868         
42869     },
42870     buildFooter : function()
42871     {
42872         
42873         var fel = this.editor.wrap.createChild();
42874         this.footer = new Roo.Toolbar(fel);
42875         // toolbar has scrolly on left / right?
42876         var footDisp= new Roo.Toolbar.Fill();
42877         var _t = this;
42878         this.footer.add(
42879             {
42880                 text : '&lt;',
42881                 xtype: 'Button',
42882                 handler : function() {
42883                     _t.footDisp.scrollTo('left',0,true)
42884                 }
42885             }
42886         );
42887         this.footer.add( footDisp );
42888         this.footer.add( 
42889             {
42890                 text : '&gt;',
42891                 xtype: 'Button',
42892                 handler : function() {
42893                     // no animation..
42894                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
42895                 }
42896             }
42897         );
42898         var fel = Roo.get(footDisp.el);
42899         fel.addClass('x-editor-context');
42900         this.footDispWrap = fel; 
42901         this.footDispWrap.overflow  = 'hidden';
42902         
42903         this.footDisp = fel.createChild();
42904         this.footDispWrap.on('click', this.onContextClick, this)
42905         
42906         
42907     },
42908     onContextClick : function (ev,dom)
42909     {
42910         ev.preventDefault();
42911         var  cn = dom.className;
42912         //Roo.log(cn);
42913         if (!cn.match(/x-ed-loc-/)) {
42914             return;
42915         }
42916         var n = cn.split('-').pop();
42917         var ans = this.footerEls;
42918         var sel = ans[n];
42919         
42920          // pick
42921         var range = this.editor.createRange();
42922         
42923         range.selectNodeContents(sel);
42924         //range.selectNode(sel);
42925         
42926         
42927         var selection = this.editor.getSelection();
42928         selection.removeAllRanges();
42929         selection.addRange(range);
42930         
42931         
42932         
42933         this.updateToolbar(null, null, sel);
42934         
42935         
42936     }
42937     
42938     
42939     
42940     
42941     
42942 });
42943
42944
42945
42946
42947
42948 /*
42949  * Based on:
42950  * Ext JS Library 1.1.1
42951  * Copyright(c) 2006-2007, Ext JS, LLC.
42952  *
42953  * Originally Released Under LGPL - original licence link has changed is not relivant.
42954  *
42955  * Fork - LGPL
42956  * <script type="text/javascript">
42957  */
42958  
42959 /**
42960  * @class Roo.form.BasicForm
42961  * @extends Roo.util.Observable
42962  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
42963  * @constructor
42964  * @param {String/HTMLElement/Roo.Element} el The form element or its id
42965  * @param {Object} config Configuration options
42966  */
42967 Roo.form.BasicForm = function(el, config){
42968     this.allItems = [];
42969     this.childForms = [];
42970     Roo.apply(this, config);
42971     /*
42972      * The Roo.form.Field items in this form.
42973      * @type MixedCollection
42974      */
42975      
42976      
42977     this.items = new Roo.util.MixedCollection(false, function(o){
42978         return o.id || (o.id = Roo.id());
42979     });
42980     this.addEvents({
42981         /**
42982          * @event beforeaction
42983          * Fires before any action is performed. Return false to cancel the action.
42984          * @param {Form} this
42985          * @param {Action} action The action to be performed
42986          */
42987         beforeaction: true,
42988         /**
42989          * @event actionfailed
42990          * Fires when an action fails.
42991          * @param {Form} this
42992          * @param {Action} action The action that failed
42993          */
42994         actionfailed : true,
42995         /**
42996          * @event actioncomplete
42997          * Fires when an action is completed.
42998          * @param {Form} this
42999          * @param {Action} action The action that completed
43000          */
43001         actioncomplete : true
43002     });
43003     if(el){
43004         this.initEl(el);
43005     }
43006     Roo.form.BasicForm.superclass.constructor.call(this);
43007 };
43008
43009 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
43010     /**
43011      * @cfg {String} method
43012      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
43013      */
43014     /**
43015      * @cfg {DataReader} reader
43016      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
43017      * This is optional as there is built-in support for processing JSON.
43018      */
43019     /**
43020      * @cfg {DataReader} errorReader
43021      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
43022      * This is completely optional as there is built-in support for processing JSON.
43023      */
43024     /**
43025      * @cfg {String} url
43026      * The URL to use for form actions if one isn't supplied in the action options.
43027      */
43028     /**
43029      * @cfg {Boolean} fileUpload
43030      * Set to true if this form is a file upload.
43031      */
43032      
43033     /**
43034      * @cfg {Object} baseParams
43035      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
43036      */
43037      /**
43038      
43039     /**
43040      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
43041      */
43042     timeout: 30,
43043
43044     // private
43045     activeAction : null,
43046
43047     /**
43048      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
43049      * or setValues() data instead of when the form was first created.
43050      */
43051     trackResetOnLoad : false,
43052     
43053     
43054     /**
43055      * childForms - used for multi-tab forms
43056      * @type {Array}
43057      */
43058     childForms : false,
43059     
43060     /**
43061      * allItems - full list of fields.
43062      * @type {Array}
43063      */
43064     allItems : false,
43065     
43066     /**
43067      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
43068      * element by passing it or its id or mask the form itself by passing in true.
43069      * @type Mixed
43070      */
43071     waitMsgTarget : false,
43072
43073     // private
43074     initEl : function(el){
43075         this.el = Roo.get(el);
43076         this.id = this.el.id || Roo.id();
43077         this.el.on('submit', this.onSubmit, this);
43078         this.el.addClass('x-form');
43079     },
43080
43081     // private
43082     onSubmit : function(e){
43083         e.stopEvent();
43084     },
43085
43086     /**
43087      * Returns true if client-side validation on the form is successful.
43088      * @return Boolean
43089      */
43090     isValid : function(){
43091         var valid = true;
43092         this.items.each(function(f){
43093            if(!f.validate()){
43094                valid = false;
43095            }
43096         });
43097         return valid;
43098     },
43099
43100     /**
43101      * Returns true if any fields in this form have changed since their original load.
43102      * @return Boolean
43103      */
43104     isDirty : function(){
43105         var dirty = false;
43106         this.items.each(function(f){
43107            if(f.isDirty()){
43108                dirty = true;
43109                return false;
43110            }
43111         });
43112         return dirty;
43113     },
43114
43115     /**
43116      * Performs a predefined action (submit or load) or custom actions you define on this form.
43117      * @param {String} actionName The name of the action type
43118      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
43119      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
43120      * accept other config options):
43121      * <pre>
43122 Property          Type             Description
43123 ----------------  ---------------  ----------------------------------------------------------------------------------
43124 url               String           The url for the action (defaults to the form's url)
43125 method            String           The form method to use (defaults to the form's method, or POST if not defined)
43126 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
43127 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
43128                                    validate the form on the client (defaults to false)
43129      * </pre>
43130      * @return {BasicForm} this
43131      */
43132     doAction : function(action, options){
43133         if(typeof action == 'string'){
43134             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
43135         }
43136         if(this.fireEvent('beforeaction', this, action) !== false){
43137             this.beforeAction(action);
43138             action.run.defer(100, action);
43139         }
43140         return this;
43141     },
43142
43143     /**
43144      * Shortcut to do a submit action.
43145      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43146      * @return {BasicForm} this
43147      */
43148     submit : function(options){
43149         this.doAction('submit', options);
43150         return this;
43151     },
43152
43153     /**
43154      * Shortcut to do a load action.
43155      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
43156      * @return {BasicForm} this
43157      */
43158     load : function(options){
43159         this.doAction('load', options);
43160         return this;
43161     },
43162
43163     /**
43164      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
43165      * @param {Record} record The record to edit
43166      * @return {BasicForm} this
43167      */
43168     updateRecord : function(record){
43169         record.beginEdit();
43170         var fs = record.fields;
43171         fs.each(function(f){
43172             var field = this.findField(f.name);
43173             if(field){
43174                 record.set(f.name, field.getValue());
43175             }
43176         }, this);
43177         record.endEdit();
43178         return this;
43179     },
43180
43181     /**
43182      * Loads an Roo.data.Record into this form.
43183      * @param {Record} record The record to load
43184      * @return {BasicForm} this
43185      */
43186     loadRecord : function(record){
43187         this.setValues(record.data);
43188         return this;
43189     },
43190
43191     // private
43192     beforeAction : function(action){
43193         var o = action.options;
43194         
43195        
43196         if(this.waitMsgTarget === true){
43197             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
43198         }else if(this.waitMsgTarget){
43199             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
43200             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
43201         }else {
43202             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
43203         }
43204          
43205     },
43206
43207     // private
43208     afterAction : function(action, success){
43209         this.activeAction = null;
43210         var o = action.options;
43211         
43212         if(this.waitMsgTarget === true){
43213             this.el.unmask();
43214         }else if(this.waitMsgTarget){
43215             this.waitMsgTarget.unmask();
43216         }else{
43217             Roo.MessageBox.updateProgress(1);
43218             Roo.MessageBox.hide();
43219         }
43220          
43221         if(success){
43222             if(o.reset){
43223                 this.reset();
43224             }
43225             Roo.callback(o.success, o.scope, [this, action]);
43226             this.fireEvent('actioncomplete', this, action);
43227             
43228         }else{
43229             
43230             // failure condition..
43231             // we have a scenario where updates need confirming.
43232             // eg. if a locking scenario exists..
43233             // we look for { errors : { needs_confirm : true }} in the response.
43234             if (
43235                 (typeof(action.result) != 'undefined')  &&
43236                 (typeof(action.result.errors) != 'undefined')  &&
43237                 (typeof(action.result.errors.needs_confirm) != 'undefined')
43238            ){
43239                 var _t = this;
43240                 Roo.MessageBox.confirm(
43241                     "Change requires confirmation",
43242                     action.result.errorMsg,
43243                     function(r) {
43244                         if (r != 'yes') {
43245                             return;
43246                         }
43247                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
43248                     }
43249                     
43250                 );
43251                 
43252                 
43253                 
43254                 return;
43255             }
43256             
43257             Roo.callback(o.failure, o.scope, [this, action]);
43258             // show an error message if no failed handler is set..
43259             if (!this.hasListener('actionfailed')) {
43260                 Roo.MessageBox.alert("Error",
43261                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
43262                         action.result.errorMsg :
43263                         "Saving Failed, please check your entries or try again"
43264                 );
43265             }
43266             
43267             this.fireEvent('actionfailed', this, action);
43268         }
43269         
43270     },
43271
43272     /**
43273      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
43274      * @param {String} id The value to search for
43275      * @return Field
43276      */
43277     findField : function(id){
43278         var field = this.items.get(id);
43279         if(!field){
43280             this.items.each(function(f){
43281                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
43282                     field = f;
43283                     return false;
43284                 }
43285             });
43286         }
43287         return field || null;
43288     },
43289
43290     /**
43291      * Add a secondary form to this one, 
43292      * Used to provide tabbed forms. One form is primary, with hidden values 
43293      * which mirror the elements from the other forms.
43294      * 
43295      * @param {Roo.form.Form} form to add.
43296      * 
43297      */
43298     addForm : function(form)
43299     {
43300        
43301         if (this.childForms.indexOf(form) > -1) {
43302             // already added..
43303             return;
43304         }
43305         this.childForms.push(form);
43306         var n = '';
43307         Roo.each(form.allItems, function (fe) {
43308             
43309             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
43310             if (this.findField(n)) { // already added..
43311                 return;
43312             }
43313             var add = new Roo.form.Hidden({
43314                 name : n
43315             });
43316             add.render(this.el);
43317             
43318             this.add( add );
43319         }, this);
43320         
43321     },
43322     /**
43323      * Mark fields in this form invalid in bulk.
43324      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
43325      * @return {BasicForm} this
43326      */
43327     markInvalid : function(errors){
43328         if(errors instanceof Array){
43329             for(var i = 0, len = errors.length; i < len; i++){
43330                 var fieldError = errors[i];
43331                 var f = this.findField(fieldError.id);
43332                 if(f){
43333                     f.markInvalid(fieldError.msg);
43334                 }
43335             }
43336         }else{
43337             var field, id;
43338             for(id in errors){
43339                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
43340                     field.markInvalid(errors[id]);
43341                 }
43342             }
43343         }
43344         Roo.each(this.childForms || [], function (f) {
43345             f.markInvalid(errors);
43346         });
43347         
43348         return this;
43349     },
43350
43351     /**
43352      * Set values for fields in this form in bulk.
43353      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
43354      * @return {BasicForm} this
43355      */
43356     setValues : function(values){
43357         if(values instanceof Array){ // array of objects
43358             for(var i = 0, len = values.length; i < len; i++){
43359                 var v = values[i];
43360                 var f = this.findField(v.id);
43361                 if(f){
43362                     f.setValue(v.value);
43363                     if(this.trackResetOnLoad){
43364                         f.originalValue = f.getValue();
43365                     }
43366                 }
43367             }
43368         }else{ // object hash
43369             var field, id;
43370             for(id in values){
43371                 if(typeof values[id] != 'function' && (field = this.findField(id))){
43372                     
43373                     if (field.setFromData && 
43374                         field.valueField && 
43375                         field.displayField &&
43376                         // combos' with local stores can 
43377                         // be queried via setValue()
43378                         // to set their value..
43379                         (field.store && !field.store.isLocal)
43380                         ) {
43381                         // it's a combo
43382                         var sd = { };
43383                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
43384                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
43385                         field.setFromData(sd);
43386                         
43387                     } else {
43388                         field.setValue(values[id]);
43389                     }
43390                     
43391                     
43392                     if(this.trackResetOnLoad){
43393                         field.originalValue = field.getValue();
43394                     }
43395                 }
43396             }
43397         }
43398          
43399         Roo.each(this.childForms || [], function (f) {
43400             f.setValues(values);
43401         });
43402                 
43403         return this;
43404     },
43405
43406     /**
43407      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
43408      * they are returned as an array.
43409      * @param {Boolean} asString
43410      * @return {Object}
43411      */
43412     getValues : function(asString){
43413         if (this.childForms) {
43414             // copy values from the child forms
43415             Roo.each(this.childForms, function (f) {
43416                 this.setValues(f.getValues());
43417             }, this);
43418         }
43419         
43420         
43421         
43422         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
43423         if(asString === true){
43424             return fs;
43425         }
43426         return Roo.urlDecode(fs);
43427     },
43428     
43429     /**
43430      * Returns the fields in this form as an object with key/value pairs. 
43431      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
43432      * @return {Object}
43433      */
43434     getFieldValues : function(with_hidden)
43435     {
43436         if (this.childForms) {
43437             // copy values from the child forms
43438             // should this call getFieldValues - probably not as we do not currently copy
43439             // hidden fields when we generate..
43440             Roo.each(this.childForms, function (f) {
43441                 this.setValues(f.getValues());
43442             }, this);
43443         }
43444         
43445         var ret = {};
43446         this.items.each(function(f){
43447             if (!f.getName()) {
43448                 return;
43449             }
43450             var v = f.getValue();
43451             if (f.inputType =='radio') {
43452                 if (typeof(ret[f.getName()]) == 'undefined') {
43453                     ret[f.getName()] = ''; // empty..
43454                 }
43455                 
43456                 if (!f.el.dom.checked) {
43457                     return;
43458                     
43459                 }
43460                 v = f.el.dom.value;
43461                 
43462             }
43463             
43464             // not sure if this supported any more..
43465             if ((typeof(v) == 'object') && f.getRawValue) {
43466                 v = f.getRawValue() ; // dates..
43467             }
43468             // combo boxes where name != hiddenName...
43469             if (f.name != f.getName()) {
43470                 ret[f.name] = f.getRawValue();
43471             }
43472             ret[f.getName()] = v;
43473         });
43474         
43475         return ret;
43476     },
43477
43478     /**
43479      * Clears all invalid messages in this form.
43480      * @return {BasicForm} this
43481      */
43482     clearInvalid : function(){
43483         this.items.each(function(f){
43484            f.clearInvalid();
43485         });
43486         
43487         Roo.each(this.childForms || [], function (f) {
43488             f.clearInvalid();
43489         });
43490         
43491         
43492         return this;
43493     },
43494
43495     /**
43496      * Resets this form.
43497      * @return {BasicForm} this
43498      */
43499     reset : function(){
43500         this.items.each(function(f){
43501             f.reset();
43502         });
43503         
43504         Roo.each(this.childForms || [], function (f) {
43505             f.reset();
43506         });
43507        
43508         
43509         return this;
43510     },
43511
43512     /**
43513      * Add Roo.form components to this form.
43514      * @param {Field} field1
43515      * @param {Field} field2 (optional)
43516      * @param {Field} etc (optional)
43517      * @return {BasicForm} this
43518      */
43519     add : function(){
43520         this.items.addAll(Array.prototype.slice.call(arguments, 0));
43521         return this;
43522     },
43523
43524
43525     /**
43526      * Removes a field from the items collection (does NOT remove its markup).
43527      * @param {Field} field
43528      * @return {BasicForm} this
43529      */
43530     remove : function(field){
43531         this.items.remove(field);
43532         return this;
43533     },
43534
43535     /**
43536      * Looks at the fields in this form, checks them for an id attribute,
43537      * and calls applyTo on the existing dom element with that id.
43538      * @return {BasicForm} this
43539      */
43540     render : function(){
43541         this.items.each(function(f){
43542             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
43543                 f.applyTo(f.id);
43544             }
43545         });
43546         return this;
43547     },
43548
43549     /**
43550      * Calls {@link Ext#apply} for all fields in this form with the passed object.
43551      * @param {Object} values
43552      * @return {BasicForm} this
43553      */
43554     applyToFields : function(o){
43555         this.items.each(function(f){
43556            Roo.apply(f, o);
43557         });
43558         return this;
43559     },
43560
43561     /**
43562      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
43563      * @param {Object} values
43564      * @return {BasicForm} this
43565      */
43566     applyIfToFields : function(o){
43567         this.items.each(function(f){
43568            Roo.applyIf(f, o);
43569         });
43570         return this;
43571     }
43572 });
43573
43574 // back compat
43575 Roo.BasicForm = Roo.form.BasicForm;/*
43576  * Based on:
43577  * Ext JS Library 1.1.1
43578  * Copyright(c) 2006-2007, Ext JS, LLC.
43579  *
43580  * Originally Released Under LGPL - original licence link has changed is not relivant.
43581  *
43582  * Fork - LGPL
43583  * <script type="text/javascript">
43584  */
43585
43586 /**
43587  * @class Roo.form.Form
43588  * @extends Roo.form.BasicForm
43589  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
43590  * @constructor
43591  * @param {Object} config Configuration options
43592  */
43593 Roo.form.Form = function(config){
43594     var xitems =  [];
43595     if (config.items) {
43596         xitems = config.items;
43597         delete config.items;
43598     }
43599    
43600     
43601     Roo.form.Form.superclass.constructor.call(this, null, config);
43602     this.url = this.url || this.action;
43603     if(!this.root){
43604         this.root = new Roo.form.Layout(Roo.applyIf({
43605             id: Roo.id()
43606         }, config));
43607     }
43608     this.active = this.root;
43609     /**
43610      * Array of all the buttons that have been added to this form via {@link addButton}
43611      * @type Array
43612      */
43613     this.buttons = [];
43614     this.allItems = [];
43615     this.addEvents({
43616         /**
43617          * @event clientvalidation
43618          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
43619          * @param {Form} this
43620          * @param {Boolean} valid true if the form has passed client-side validation
43621          */
43622         clientvalidation: true,
43623         /**
43624          * @event rendered
43625          * Fires when the form is rendered
43626          * @param {Roo.form.Form} form
43627          */
43628         rendered : true
43629     });
43630     
43631     if (this.progressUrl) {
43632             // push a hidden field onto the list of fields..
43633             this.addxtype( {
43634                     xns: Roo.form, 
43635                     xtype : 'Hidden', 
43636                     name : 'UPLOAD_IDENTIFIER' 
43637             });
43638         }
43639         
43640     
43641     Roo.each(xitems, this.addxtype, this);
43642     
43643     
43644     
43645 };
43646
43647 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
43648     /**
43649      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
43650      */
43651     /**
43652      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
43653      */
43654     /**
43655      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
43656      */
43657     buttonAlign:'center',
43658
43659     /**
43660      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
43661      */
43662     minButtonWidth:75,
43663
43664     /**
43665      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
43666      * This property cascades to child containers if not set.
43667      */
43668     labelAlign:'left',
43669
43670     /**
43671      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
43672      * fires a looping event with that state. This is required to bind buttons to the valid
43673      * state using the config value formBind:true on the button.
43674      */
43675     monitorValid : false,
43676
43677     /**
43678      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
43679      */
43680     monitorPoll : 200,
43681     
43682     /**
43683      * @cfg {String} progressUrl - Url to return progress data 
43684      */
43685     
43686     progressUrl : false,
43687   
43688     /**
43689      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
43690      * fields are added and the column is closed. If no fields are passed the column remains open
43691      * until end() is called.
43692      * @param {Object} config The config to pass to the column
43693      * @param {Field} field1 (optional)
43694      * @param {Field} field2 (optional)
43695      * @param {Field} etc (optional)
43696      * @return Column The column container object
43697      */
43698     column : function(c){
43699         var col = new Roo.form.Column(c);
43700         this.start(col);
43701         if(arguments.length > 1){ // duplicate code required because of Opera
43702             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43703             this.end();
43704         }
43705         return col;
43706     },
43707
43708     /**
43709      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
43710      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
43711      * until end() is called.
43712      * @param {Object} config The config to pass to the fieldset
43713      * @param {Field} field1 (optional)
43714      * @param {Field} field2 (optional)
43715      * @param {Field} etc (optional)
43716      * @return FieldSet The fieldset container object
43717      */
43718     fieldset : function(c){
43719         var fs = new Roo.form.FieldSet(c);
43720         this.start(fs);
43721         if(arguments.length > 1){ // duplicate code required because of Opera
43722             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43723             this.end();
43724         }
43725         return fs;
43726     },
43727
43728     /**
43729      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
43730      * fields are added and the container is closed. If no fields are passed the container remains open
43731      * until end() is called.
43732      * @param {Object} config The config to pass to the Layout
43733      * @param {Field} field1 (optional)
43734      * @param {Field} field2 (optional)
43735      * @param {Field} etc (optional)
43736      * @return Layout The container object
43737      */
43738     container : function(c){
43739         var l = new Roo.form.Layout(c);
43740         this.start(l);
43741         if(arguments.length > 1){ // duplicate code required because of Opera
43742             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
43743             this.end();
43744         }
43745         return l;
43746     },
43747
43748     /**
43749      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
43750      * @param {Object} container A Roo.form.Layout or subclass of Layout
43751      * @return {Form} this
43752      */
43753     start : function(c){
43754         // cascade label info
43755         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
43756         this.active.stack.push(c);
43757         c.ownerCt = this.active;
43758         this.active = c;
43759         return this;
43760     },
43761
43762     /**
43763      * Closes the current open container
43764      * @return {Form} this
43765      */
43766     end : function(){
43767         if(this.active == this.root){
43768             return this;
43769         }
43770         this.active = this.active.ownerCt;
43771         return this;
43772     },
43773
43774     /**
43775      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
43776      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
43777      * as the label of the field.
43778      * @param {Field} field1
43779      * @param {Field} field2 (optional)
43780      * @param {Field} etc. (optional)
43781      * @return {Form} this
43782      */
43783     add : function(){
43784         this.active.stack.push.apply(this.active.stack, arguments);
43785         this.allItems.push.apply(this.allItems,arguments);
43786         var r = [];
43787         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
43788             if(a[i].isFormField){
43789                 r.push(a[i]);
43790             }
43791         }
43792         if(r.length > 0){
43793             Roo.form.Form.superclass.add.apply(this, r);
43794         }
43795         return this;
43796     },
43797     
43798
43799     
43800     
43801     
43802      /**
43803      * Find any element that has been added to a form, using it's ID or name
43804      * This can include framesets, columns etc. along with regular fields..
43805      * @param {String} id - id or name to find.
43806      
43807      * @return {Element} e - or false if nothing found.
43808      */
43809     findbyId : function(id)
43810     {
43811         var ret = false;
43812         if (!id) {
43813             return ret;
43814         }
43815         Roo.each(this.allItems, function(f){
43816             if (f.id == id || f.name == id ){
43817                 ret = f;
43818                 return false;
43819             }
43820         });
43821         return ret;
43822     },
43823
43824     
43825     
43826     /**
43827      * Render this form into the passed container. This should only be called once!
43828      * @param {String/HTMLElement/Element} container The element this component should be rendered into
43829      * @return {Form} this
43830      */
43831     render : function(ct)
43832     {
43833         
43834         
43835         
43836         ct = Roo.get(ct);
43837         var o = this.autoCreate || {
43838             tag: 'form',
43839             method : this.method || 'POST',
43840             id : this.id || Roo.id()
43841         };
43842         this.initEl(ct.createChild(o));
43843
43844         this.root.render(this.el);
43845         
43846        
43847              
43848         this.items.each(function(f){
43849             f.render('x-form-el-'+f.id);
43850         });
43851
43852         if(this.buttons.length > 0){
43853             // tables are required to maintain order and for correct IE layout
43854             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
43855                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
43856                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
43857             }}, null, true);
43858             var tr = tb.getElementsByTagName('tr')[0];
43859             for(var i = 0, len = this.buttons.length; i < len; i++) {
43860                 var b = this.buttons[i];
43861                 var td = document.createElement('td');
43862                 td.className = 'x-form-btn-td';
43863                 b.render(tr.appendChild(td));
43864             }
43865         }
43866         if(this.monitorValid){ // initialize after render
43867             this.startMonitoring();
43868         }
43869         this.fireEvent('rendered', this);
43870         return this;
43871     },
43872
43873     /**
43874      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
43875      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
43876      * object or a valid Roo.DomHelper element config
43877      * @param {Function} handler The function called when the button is clicked
43878      * @param {Object} scope (optional) The scope of the handler function
43879      * @return {Roo.Button}
43880      */
43881     addButton : function(config, handler, scope){
43882         var bc = {
43883             handler: handler,
43884             scope: scope,
43885             minWidth: this.minButtonWidth,
43886             hideParent:true
43887         };
43888         if(typeof config == "string"){
43889             bc.text = config;
43890         }else{
43891             Roo.apply(bc, config);
43892         }
43893         var btn = new Roo.Button(null, bc);
43894         this.buttons.push(btn);
43895         return btn;
43896     },
43897
43898      /**
43899      * Adds a series of form elements (using the xtype property as the factory method.
43900      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
43901      * @param {Object} config 
43902      */
43903     
43904     addxtype : function()
43905     {
43906         var ar = Array.prototype.slice.call(arguments, 0);
43907         var ret = false;
43908         for(var i = 0; i < ar.length; i++) {
43909             if (!ar[i]) {
43910                 continue; // skip -- if this happends something invalid got sent, we 
43911                 // should ignore it, as basically that interface element will not show up
43912                 // and that should be pretty obvious!!
43913             }
43914             
43915             if (Roo.form[ar[i].xtype]) {
43916                 ar[i].form = this;
43917                 var fe = Roo.factory(ar[i], Roo.form);
43918                 if (!ret) {
43919                     ret = fe;
43920                 }
43921                 fe.form = this;
43922                 if (fe.store) {
43923                     fe.store.form = this;
43924                 }
43925                 if (fe.isLayout) {  
43926                          
43927                     this.start(fe);
43928                     this.allItems.push(fe);
43929                     if (fe.items && fe.addxtype) {
43930                         fe.addxtype.apply(fe, fe.items);
43931                         delete fe.items;
43932                     }
43933                      this.end();
43934                     continue;
43935                 }
43936                 
43937                 
43938                  
43939                 this.add(fe);
43940               //  console.log('adding ' + ar[i].xtype);
43941             }
43942             if (ar[i].xtype == 'Button') {  
43943                 //console.log('adding button');
43944                 //console.log(ar[i]);
43945                 this.addButton(ar[i]);
43946                 this.allItems.push(fe);
43947                 continue;
43948             }
43949             
43950             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
43951                 alert('end is not supported on xtype any more, use items');
43952             //    this.end();
43953             //    //console.log('adding end');
43954             }
43955             
43956         }
43957         return ret;
43958     },
43959     
43960     /**
43961      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
43962      * option "monitorValid"
43963      */
43964     startMonitoring : function(){
43965         if(!this.bound){
43966             this.bound = true;
43967             Roo.TaskMgr.start({
43968                 run : this.bindHandler,
43969                 interval : this.monitorPoll || 200,
43970                 scope: this
43971             });
43972         }
43973     },
43974
43975     /**
43976      * Stops monitoring of the valid state of this form
43977      */
43978     stopMonitoring : function(){
43979         this.bound = false;
43980     },
43981
43982     // private
43983     bindHandler : function(){
43984         if(!this.bound){
43985             return false; // stops binding
43986         }
43987         var valid = true;
43988         this.items.each(function(f){
43989             if(!f.isValid(true)){
43990                 valid = false;
43991                 return false;
43992             }
43993         });
43994         for(var i = 0, len = this.buttons.length; i < len; i++){
43995             var btn = this.buttons[i];
43996             if(btn.formBind === true && btn.disabled === valid){
43997                 btn.setDisabled(!valid);
43998             }
43999         }
44000         this.fireEvent('clientvalidation', this, valid);
44001     }
44002     
44003     
44004     
44005     
44006     
44007     
44008     
44009     
44010 });
44011
44012
44013 // back compat
44014 Roo.Form = Roo.form.Form;
44015 /*
44016  * Based on:
44017  * Ext JS Library 1.1.1
44018  * Copyright(c) 2006-2007, Ext JS, LLC.
44019  *
44020  * Originally Released Under LGPL - original licence link has changed is not relivant.
44021  *
44022  * Fork - LGPL
44023  * <script type="text/javascript">
44024  */
44025  
44026  /**
44027  * @class Roo.form.Action
44028  * Internal Class used to handle form actions
44029  * @constructor
44030  * @param {Roo.form.BasicForm} el The form element or its id
44031  * @param {Object} config Configuration options
44032  */
44033  
44034  
44035 // define the action interface
44036 Roo.form.Action = function(form, options){
44037     this.form = form;
44038     this.options = options || {};
44039 };
44040 /**
44041  * Client Validation Failed
44042  * @const 
44043  */
44044 Roo.form.Action.CLIENT_INVALID = 'client';
44045 /**
44046  * Server Validation Failed
44047  * @const 
44048  */
44049  Roo.form.Action.SERVER_INVALID = 'server';
44050  /**
44051  * Connect to Server Failed
44052  * @const 
44053  */
44054 Roo.form.Action.CONNECT_FAILURE = 'connect';
44055 /**
44056  * Reading Data from Server Failed
44057  * @const 
44058  */
44059 Roo.form.Action.LOAD_FAILURE = 'load';
44060
44061 Roo.form.Action.prototype = {
44062     type : 'default',
44063     failureType : undefined,
44064     response : undefined,
44065     result : undefined,
44066
44067     // interface method
44068     run : function(options){
44069
44070     },
44071
44072     // interface method
44073     success : function(response){
44074
44075     },
44076
44077     // interface method
44078     handleResponse : function(response){
44079
44080     },
44081
44082     // default connection failure
44083     failure : function(response){
44084         
44085         this.response = response;
44086         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44087         this.form.afterAction(this, false);
44088     },
44089
44090     processResponse : function(response){
44091         this.response = response;
44092         if(!response.responseText){
44093             return true;
44094         }
44095         this.result = this.handleResponse(response);
44096         return this.result;
44097     },
44098
44099     // utility functions used internally
44100     getUrl : function(appendParams){
44101         var url = this.options.url || this.form.url || this.form.el.dom.action;
44102         if(appendParams){
44103             var p = this.getParams();
44104             if(p){
44105                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
44106             }
44107         }
44108         return url;
44109     },
44110
44111     getMethod : function(){
44112         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
44113     },
44114
44115     getParams : function(){
44116         var bp = this.form.baseParams;
44117         var p = this.options.params;
44118         if(p){
44119             if(typeof p == "object"){
44120                 p = Roo.urlEncode(Roo.applyIf(p, bp));
44121             }else if(typeof p == 'string' && bp){
44122                 p += '&' + Roo.urlEncode(bp);
44123             }
44124         }else if(bp){
44125             p = Roo.urlEncode(bp);
44126         }
44127         return p;
44128     },
44129
44130     createCallback : function(){
44131         return {
44132             success: this.success,
44133             failure: this.failure,
44134             scope: this,
44135             timeout: (this.form.timeout*1000),
44136             upload: this.form.fileUpload ? this.success : undefined
44137         };
44138     }
44139 };
44140
44141 Roo.form.Action.Submit = function(form, options){
44142     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
44143 };
44144
44145 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
44146     type : 'submit',
44147
44148     haveProgress : false,
44149     uploadComplete : false,
44150     
44151     // uploadProgress indicator.
44152     uploadProgress : function()
44153     {
44154         if (!this.form.progressUrl) {
44155             return;
44156         }
44157         
44158         if (!this.haveProgress) {
44159             Roo.MessageBox.progress("Uploading", "Uploading");
44160         }
44161         if (this.uploadComplete) {
44162            Roo.MessageBox.hide();
44163            return;
44164         }
44165         
44166         this.haveProgress = true;
44167    
44168         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
44169         
44170         var c = new Roo.data.Connection();
44171         c.request({
44172             url : this.form.progressUrl,
44173             params: {
44174                 id : uid
44175             },
44176             method: 'GET',
44177             success : function(req){
44178                //console.log(data);
44179                 var rdata = false;
44180                 var edata;
44181                 try  {
44182                    rdata = Roo.decode(req.responseText)
44183                 } catch (e) {
44184                     Roo.log("Invalid data from server..");
44185                     Roo.log(edata);
44186                     return;
44187                 }
44188                 if (!rdata || !rdata.success) {
44189                     Roo.log(rdata);
44190                     Roo.MessageBox.alert(Roo.encode(rdata));
44191                     return;
44192                 }
44193                 var data = rdata.data;
44194                 
44195                 if (this.uploadComplete) {
44196                    Roo.MessageBox.hide();
44197                    return;
44198                 }
44199                    
44200                 if (data){
44201                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
44202                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
44203                     );
44204                 }
44205                 this.uploadProgress.defer(2000,this);
44206             },
44207        
44208             failure: function(data) {
44209                 Roo.log('progress url failed ');
44210                 Roo.log(data);
44211             },
44212             scope : this
44213         });
44214            
44215     },
44216     
44217     
44218     run : function()
44219     {
44220         // run get Values on the form, so it syncs any secondary forms.
44221         this.form.getValues();
44222         
44223         var o = this.options;
44224         var method = this.getMethod();
44225         var isPost = method == 'POST';
44226         if(o.clientValidation === false || this.form.isValid()){
44227             
44228             if (this.form.progressUrl) {
44229                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
44230                     (new Date() * 1) + '' + Math.random());
44231                     
44232             } 
44233             
44234             
44235             Roo.Ajax.request(Roo.apply(this.createCallback(), {
44236                 form:this.form.el.dom,
44237                 url:this.getUrl(!isPost),
44238                 method: method,
44239                 params:isPost ? this.getParams() : null,
44240                 isUpload: this.form.fileUpload
44241             }));
44242             
44243             this.uploadProgress();
44244
44245         }else if (o.clientValidation !== false){ // client validation failed
44246             this.failureType = Roo.form.Action.CLIENT_INVALID;
44247             this.form.afterAction(this, false);
44248         }
44249     },
44250
44251     success : function(response)
44252     {
44253         this.uploadComplete= true;
44254         if (this.haveProgress) {
44255             Roo.MessageBox.hide();
44256         }
44257         
44258         
44259         var result = this.processResponse(response);
44260         if(result === true || result.success){
44261             this.form.afterAction(this, true);
44262             return;
44263         }
44264         if(result.errors){
44265             this.form.markInvalid(result.errors);
44266             this.failureType = Roo.form.Action.SERVER_INVALID;
44267         }
44268         this.form.afterAction(this, false);
44269     },
44270     failure : function(response)
44271     {
44272         this.uploadComplete= true;
44273         if (this.haveProgress) {
44274             Roo.MessageBox.hide();
44275         }
44276         
44277         this.response = response;
44278         this.failureType = Roo.form.Action.CONNECT_FAILURE;
44279         this.form.afterAction(this, false);
44280     },
44281     
44282     handleResponse : function(response){
44283         if(this.form.errorReader){
44284             var rs = this.form.errorReader.read(response);
44285             var errors = [];
44286             if(rs.records){
44287                 for(var i = 0, len = rs.records.length; i < len; i++) {
44288                     var r = rs.records[i];
44289                     errors[i] = r.data;
44290                 }
44291             }
44292             if(errors.length < 1){
44293                 errors = null;
44294             }
44295             return {
44296                 success : rs.success,
44297                 errors : errors
44298             };
44299         }
44300         var ret = false;
44301         try {
44302             ret = Roo.decode(response.responseText);
44303         } catch (e) {
44304             ret = {
44305                 success: false,
44306                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
44307                 errors : []
44308             };
44309         }
44310         return ret;
44311         
44312     }
44313 });
44314
44315
44316 Roo.form.Action.Load = function(form, options){
44317     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
44318     this.reader = this.form.reader;
44319 };
44320
44321 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
44322     type : 'load',
44323
44324     run : function(){
44325         
44326         Roo.Ajax.request(Roo.apply(
44327                 this.createCallback(), {
44328                     method:this.getMethod(),
44329                     url:this.getUrl(false),
44330                     params:this.getParams()
44331         }));
44332     },
44333
44334     success : function(response){
44335         
44336         var result = this.processResponse(response);
44337         if(result === true || !result.success || !result.data){
44338             this.failureType = Roo.form.Action.LOAD_FAILURE;
44339             this.form.afterAction(this, false);
44340             return;
44341         }
44342         this.form.clearInvalid();
44343         this.form.setValues(result.data);
44344         this.form.afterAction(this, true);
44345     },
44346
44347     handleResponse : function(response){
44348         if(this.form.reader){
44349             var rs = this.form.reader.read(response);
44350             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
44351             return {
44352                 success : rs.success,
44353                 data : data
44354             };
44355         }
44356         return Roo.decode(response.responseText);
44357     }
44358 });
44359
44360 Roo.form.Action.ACTION_TYPES = {
44361     'load' : Roo.form.Action.Load,
44362     'submit' : Roo.form.Action.Submit
44363 };/*
44364  * Based on:
44365  * Ext JS Library 1.1.1
44366  * Copyright(c) 2006-2007, Ext JS, LLC.
44367  *
44368  * Originally Released Under LGPL - original licence link has changed is not relivant.
44369  *
44370  * Fork - LGPL
44371  * <script type="text/javascript">
44372  */
44373  
44374 /**
44375  * @class Roo.form.Layout
44376  * @extends Roo.Component
44377  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
44378  * @constructor
44379  * @param {Object} config Configuration options
44380  */
44381 Roo.form.Layout = function(config){
44382     var xitems = [];
44383     if (config.items) {
44384         xitems = config.items;
44385         delete config.items;
44386     }
44387     Roo.form.Layout.superclass.constructor.call(this, config);
44388     this.stack = [];
44389     Roo.each(xitems, this.addxtype, this);
44390      
44391 };
44392
44393 Roo.extend(Roo.form.Layout, Roo.Component, {
44394     /**
44395      * @cfg {String/Object} autoCreate
44396      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
44397      */
44398     /**
44399      * @cfg {String/Object/Function} style
44400      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
44401      * a function which returns such a specification.
44402      */
44403     /**
44404      * @cfg {String} labelAlign
44405      * Valid values are "left," "top" and "right" (defaults to "left")
44406      */
44407     /**
44408      * @cfg {Number} labelWidth
44409      * Fixed width in pixels of all field labels (defaults to undefined)
44410      */
44411     /**
44412      * @cfg {Boolean} clear
44413      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
44414      */
44415     clear : true,
44416     /**
44417      * @cfg {String} labelSeparator
44418      * The separator to use after field labels (defaults to ':')
44419      */
44420     labelSeparator : ':',
44421     /**
44422      * @cfg {Boolean} hideLabels
44423      * True to suppress the display of field labels in this layout (defaults to false)
44424      */
44425     hideLabels : false,
44426
44427     // private
44428     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
44429     
44430     isLayout : true,
44431     
44432     // private
44433     onRender : function(ct, position){
44434         if(this.el){ // from markup
44435             this.el = Roo.get(this.el);
44436         }else {  // generate
44437             var cfg = this.getAutoCreate();
44438             this.el = ct.createChild(cfg, position);
44439         }
44440         if(this.style){
44441             this.el.applyStyles(this.style);
44442         }
44443         if(this.labelAlign){
44444             this.el.addClass('x-form-label-'+this.labelAlign);
44445         }
44446         if(this.hideLabels){
44447             this.labelStyle = "display:none";
44448             this.elementStyle = "padding-left:0;";
44449         }else{
44450             if(typeof this.labelWidth == 'number'){
44451                 this.labelStyle = "width:"+this.labelWidth+"px;";
44452                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
44453             }
44454             if(this.labelAlign == 'top'){
44455                 this.labelStyle = "width:auto;";
44456                 this.elementStyle = "padding-left:0;";
44457             }
44458         }
44459         var stack = this.stack;
44460         var slen = stack.length;
44461         if(slen > 0){
44462             if(!this.fieldTpl){
44463                 var t = new Roo.Template(
44464                     '<div class="x-form-item {5}">',
44465                         '<label for="{0}" style="{2}">{1}{4}</label>',
44466                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
44467                         '</div>',
44468                     '</div><div class="x-form-clear-left"></div>'
44469                 );
44470                 t.disableFormats = true;
44471                 t.compile();
44472                 Roo.form.Layout.prototype.fieldTpl = t;
44473             }
44474             for(var i = 0; i < slen; i++) {
44475                 if(stack[i].isFormField){
44476                     this.renderField(stack[i]);
44477                 }else{
44478                     this.renderComponent(stack[i]);
44479                 }
44480             }
44481         }
44482         if(this.clear){
44483             this.el.createChild({cls:'x-form-clear'});
44484         }
44485     },
44486
44487     // private
44488     renderField : function(f){
44489         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
44490                f.id, //0
44491                f.fieldLabel, //1
44492                f.labelStyle||this.labelStyle||'', //2
44493                this.elementStyle||'', //3
44494                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
44495                f.itemCls||this.itemCls||''  //5
44496        ], true).getPrevSibling());
44497     },
44498
44499     // private
44500     renderComponent : function(c){
44501         c.render(c.isLayout ? this.el : this.el.createChild());    
44502     },
44503     /**
44504      * Adds a object form elements (using the xtype property as the factory method.)
44505      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
44506      * @param {Object} config 
44507      */
44508     addxtype : function(o)
44509     {
44510         // create the lement.
44511         o.form = this.form;
44512         var fe = Roo.factory(o, Roo.form);
44513         this.form.allItems.push(fe);
44514         this.stack.push(fe);
44515         
44516         if (fe.isFormField) {
44517             this.form.items.add(fe);
44518         }
44519          
44520         return fe;
44521     }
44522 });
44523
44524 /**
44525  * @class Roo.form.Column
44526  * @extends Roo.form.Layout
44527  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
44528  * @constructor
44529  * @param {Object} config Configuration options
44530  */
44531 Roo.form.Column = function(config){
44532     Roo.form.Column.superclass.constructor.call(this, config);
44533 };
44534
44535 Roo.extend(Roo.form.Column, Roo.form.Layout, {
44536     /**
44537      * @cfg {Number/String} width
44538      * The fixed width of the column in pixels or CSS value (defaults to "auto")
44539      */
44540     /**
44541      * @cfg {String/Object} autoCreate
44542      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
44543      */
44544
44545     // private
44546     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
44547
44548     // private
44549     onRender : function(ct, position){
44550         Roo.form.Column.superclass.onRender.call(this, ct, position);
44551         if(this.width){
44552             this.el.setWidth(this.width);
44553         }
44554     }
44555 });
44556
44557
44558 /**
44559  * @class Roo.form.Row
44560  * @extends Roo.form.Layout
44561  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
44562  * @constructor
44563  * @param {Object} config Configuration options
44564  */
44565
44566  
44567 Roo.form.Row = function(config){
44568     Roo.form.Row.superclass.constructor.call(this, config);
44569 };
44570  
44571 Roo.extend(Roo.form.Row, Roo.form.Layout, {
44572       /**
44573      * @cfg {Number/String} width
44574      * The fixed width of the column in pixels or CSS value (defaults to "auto")
44575      */
44576     /**
44577      * @cfg {Number/String} height
44578      * The fixed height of the column in pixels or CSS value (defaults to "auto")
44579      */
44580     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
44581     
44582     padWidth : 20,
44583     // private
44584     onRender : function(ct, position){
44585         //console.log('row render');
44586         if(!this.rowTpl){
44587             var t = new Roo.Template(
44588                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
44589                     '<label for="{0}" style="{2}">{1}{4}</label>',
44590                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
44591                     '</div>',
44592                 '</div>'
44593             );
44594             t.disableFormats = true;
44595             t.compile();
44596             Roo.form.Layout.prototype.rowTpl = t;
44597         }
44598         this.fieldTpl = this.rowTpl;
44599         
44600         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
44601         var labelWidth = 100;
44602         
44603         if ((this.labelAlign != 'top')) {
44604             if (typeof this.labelWidth == 'number') {
44605                 labelWidth = this.labelWidth
44606             }
44607             this.padWidth =  20 + labelWidth;
44608             
44609         }
44610         
44611         Roo.form.Column.superclass.onRender.call(this, ct, position);
44612         if(this.width){
44613             this.el.setWidth(this.width);
44614         }
44615         if(this.height){
44616             this.el.setHeight(this.height);
44617         }
44618     },
44619     
44620     // private
44621     renderField : function(f){
44622         f.fieldEl = this.fieldTpl.append(this.el, [
44623                f.id, f.fieldLabel,
44624                f.labelStyle||this.labelStyle||'',
44625                this.elementStyle||'',
44626                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
44627                f.itemCls||this.itemCls||'',
44628                f.width ? f.width + this.padWidth : 160 + this.padWidth
44629        ],true);
44630     }
44631 });
44632  
44633
44634 /**
44635  * @class Roo.form.FieldSet
44636  * @extends Roo.form.Layout
44637  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
44638  * @constructor
44639  * @param {Object} config Configuration options
44640  */
44641 Roo.form.FieldSet = function(config){
44642     Roo.form.FieldSet.superclass.constructor.call(this, config);
44643 };
44644
44645 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
44646     /**
44647      * @cfg {String} legend
44648      * The text to display as the legend for the FieldSet (defaults to '')
44649      */
44650     /**
44651      * @cfg {String/Object} autoCreate
44652      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
44653      */
44654
44655     // private
44656     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
44657
44658     // private
44659     onRender : function(ct, position){
44660         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
44661         if(this.legend){
44662             this.setLegend(this.legend);
44663         }
44664     },
44665
44666     // private
44667     setLegend : function(text){
44668         if(this.rendered){
44669             this.el.child('legend').update(text);
44670         }
44671     }
44672 });/*
44673  * Based on:
44674  * Ext JS Library 1.1.1
44675  * Copyright(c) 2006-2007, Ext JS, LLC.
44676  *
44677  * Originally Released Under LGPL - original licence link has changed is not relivant.
44678  *
44679  * Fork - LGPL
44680  * <script type="text/javascript">
44681  */
44682 /**
44683  * @class Roo.form.VTypes
44684  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
44685  * @singleton
44686  */
44687 Roo.form.VTypes = function(){
44688     // closure these in so they are only created once.
44689     var alpha = /^[a-zA-Z_]+$/;
44690     var alphanum = /^[a-zA-Z0-9_]+$/;
44691     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
44692     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
44693
44694     // All these messages and functions are configurable
44695     return {
44696         /**
44697          * The function used to validate email addresses
44698          * @param {String} value The email address
44699          */
44700         'email' : function(v){
44701             return email.test(v);
44702         },
44703         /**
44704          * The error text to display when the email validation function returns false
44705          * @type String
44706          */
44707         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
44708         /**
44709          * The keystroke filter mask to be applied on email input
44710          * @type RegExp
44711          */
44712         'emailMask' : /[a-z0-9_\.\-@]/i,
44713
44714         /**
44715          * The function used to validate URLs
44716          * @param {String} value The URL
44717          */
44718         'url' : function(v){
44719             return url.test(v);
44720         },
44721         /**
44722          * The error text to display when the url validation function returns false
44723          * @type String
44724          */
44725         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
44726         
44727         /**
44728          * The function used to validate alpha values
44729          * @param {String} value The value
44730          */
44731         'alpha' : function(v){
44732             return alpha.test(v);
44733         },
44734         /**
44735          * The error text to display when the alpha validation function returns false
44736          * @type String
44737          */
44738         'alphaText' : 'This field should only contain letters and _',
44739         /**
44740          * The keystroke filter mask to be applied on alpha input
44741          * @type RegExp
44742          */
44743         'alphaMask' : /[a-z_]/i,
44744
44745         /**
44746          * The function used to validate alphanumeric values
44747          * @param {String} value The value
44748          */
44749         'alphanum' : function(v){
44750             return alphanum.test(v);
44751         },
44752         /**
44753          * The error text to display when the alphanumeric validation function returns false
44754          * @type String
44755          */
44756         'alphanumText' : 'This field should only contain letters, numbers and _',
44757         /**
44758          * The keystroke filter mask to be applied on alphanumeric input
44759          * @type RegExp
44760          */
44761         'alphanumMask' : /[a-z0-9_]/i
44762     };
44763 }();//<script type="text/javascript">
44764
44765 /**
44766  * @class Roo.form.FCKeditor
44767  * @extends Roo.form.TextArea
44768  * Wrapper around the FCKEditor http://www.fckeditor.net
44769  * @constructor
44770  * Creates a new FCKeditor
44771  * @param {Object} config Configuration options
44772  */
44773 Roo.form.FCKeditor = function(config){
44774     Roo.form.FCKeditor.superclass.constructor.call(this, config);
44775     this.addEvents({
44776          /**
44777          * @event editorinit
44778          * Fired when the editor is initialized - you can add extra handlers here..
44779          * @param {FCKeditor} this
44780          * @param {Object} the FCK object.
44781          */
44782         editorinit : true
44783     });
44784     
44785     
44786 };
44787 Roo.form.FCKeditor.editors = { };
44788 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
44789 {
44790     //defaultAutoCreate : {
44791     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
44792     //},
44793     // private
44794     /**
44795      * @cfg {Object} fck options - see fck manual for details.
44796      */
44797     fckconfig : false,
44798     
44799     /**
44800      * @cfg {Object} fck toolbar set (Basic or Default)
44801      */
44802     toolbarSet : 'Basic',
44803     /**
44804      * @cfg {Object} fck BasePath
44805      */ 
44806     basePath : '/fckeditor/',
44807     
44808     
44809     frame : false,
44810     
44811     value : '',
44812     
44813    
44814     onRender : function(ct, position)
44815     {
44816         if(!this.el){
44817             this.defaultAutoCreate = {
44818                 tag: "textarea",
44819                 style:"width:300px;height:60px;",
44820                 autocomplete: "off"
44821             };
44822         }
44823         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
44824         /*
44825         if(this.grow){
44826             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
44827             if(this.preventScrollbars){
44828                 this.el.setStyle("overflow", "hidden");
44829             }
44830             this.el.setHeight(this.growMin);
44831         }
44832         */
44833         //console.log('onrender' + this.getId() );
44834         Roo.form.FCKeditor.editors[this.getId()] = this;
44835          
44836
44837         this.replaceTextarea() ;
44838         
44839     },
44840     
44841     getEditor : function() {
44842         return this.fckEditor;
44843     },
44844     /**
44845      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
44846      * @param {Mixed} value The value to set
44847      */
44848     
44849     
44850     setValue : function(value)
44851     {
44852         //console.log('setValue: ' + value);
44853         
44854         if(typeof(value) == 'undefined') { // not sure why this is happending...
44855             return;
44856         }
44857         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44858         
44859         //if(!this.el || !this.getEditor()) {
44860         //    this.value = value;
44861             //this.setValue.defer(100,this,[value]);    
44862         //    return;
44863         //} 
44864         
44865         if(!this.getEditor()) {
44866             return;
44867         }
44868         
44869         this.getEditor().SetData(value);
44870         
44871         //
44872
44873     },
44874
44875     /**
44876      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
44877      * @return {Mixed} value The field value
44878      */
44879     getValue : function()
44880     {
44881         
44882         if (this.frame && this.frame.dom.style.display == 'none') {
44883             return Roo.form.FCKeditor.superclass.getValue.call(this);
44884         }
44885         
44886         if(!this.el || !this.getEditor()) {
44887            
44888            // this.getValue.defer(100,this); 
44889             return this.value;
44890         }
44891        
44892         
44893         var value=this.getEditor().GetData();
44894         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
44895         return Roo.form.FCKeditor.superclass.getValue.call(this);
44896         
44897
44898     },
44899
44900     /**
44901      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
44902      * @return {Mixed} value The field value
44903      */
44904     getRawValue : function()
44905     {
44906         if (this.frame && this.frame.dom.style.display == 'none') {
44907             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
44908         }
44909         
44910         if(!this.el || !this.getEditor()) {
44911             //this.getRawValue.defer(100,this); 
44912             return this.value;
44913             return;
44914         }
44915         
44916         
44917         
44918         var value=this.getEditor().GetData();
44919         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
44920         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
44921          
44922     },
44923     
44924     setSize : function(w,h) {
44925         
44926         
44927         
44928         //if (this.frame && this.frame.dom.style.display == 'none') {
44929         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
44930         //    return;
44931         //}
44932         //if(!this.el || !this.getEditor()) {
44933         //    this.setSize.defer(100,this, [w,h]); 
44934         //    return;
44935         //}
44936         
44937         
44938         
44939         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
44940         
44941         this.frame.dom.setAttribute('width', w);
44942         this.frame.dom.setAttribute('height', h);
44943         this.frame.setSize(w,h);
44944         
44945     },
44946     
44947     toggleSourceEdit : function(value) {
44948         
44949       
44950          
44951         this.el.dom.style.display = value ? '' : 'none';
44952         this.frame.dom.style.display = value ?  'none' : '';
44953         
44954     },
44955     
44956     
44957     focus: function(tag)
44958     {
44959         if (this.frame.dom.style.display == 'none') {
44960             return Roo.form.FCKeditor.superclass.focus.call(this);
44961         }
44962         if(!this.el || !this.getEditor()) {
44963             this.focus.defer(100,this, [tag]); 
44964             return;
44965         }
44966         
44967         
44968         
44969         
44970         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
44971         this.getEditor().Focus();
44972         if (tgs.length) {
44973             if (!this.getEditor().Selection.GetSelection()) {
44974                 this.focus.defer(100,this, [tag]); 
44975                 return;
44976             }
44977             
44978             
44979             var r = this.getEditor().EditorDocument.createRange();
44980             r.setStart(tgs[0],0);
44981             r.setEnd(tgs[0],0);
44982             this.getEditor().Selection.GetSelection().removeAllRanges();
44983             this.getEditor().Selection.GetSelection().addRange(r);
44984             this.getEditor().Focus();
44985         }
44986         
44987     },
44988     
44989     
44990     
44991     replaceTextarea : function()
44992     {
44993         if ( document.getElementById( this.getId() + '___Frame' ) )
44994             return ;
44995         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
44996         //{
44997             // We must check the elements firstly using the Id and then the name.
44998         var oTextarea = document.getElementById( this.getId() );
44999         
45000         var colElementsByName = document.getElementsByName( this.getId() ) ;
45001          
45002         oTextarea.style.display = 'none' ;
45003
45004         if ( oTextarea.tabIndex ) {            
45005             this.TabIndex = oTextarea.tabIndex ;
45006         }
45007         
45008         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
45009         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
45010         this.frame = Roo.get(this.getId() + '___Frame')
45011     },
45012     
45013     _getConfigHtml : function()
45014     {
45015         var sConfig = '' ;
45016
45017         for ( var o in this.fckconfig ) {
45018             sConfig += sConfig.length > 0  ? '&amp;' : '';
45019             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
45020         }
45021
45022         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
45023     },
45024     
45025     
45026     _getIFrameHtml : function()
45027     {
45028         var sFile = 'fckeditor.html' ;
45029         /* no idea what this is about..
45030         try
45031         {
45032             if ( (/fcksource=true/i).test( window.top.location.search ) )
45033                 sFile = 'fckeditor.original.html' ;
45034         }
45035         catch (e) { 
45036         */
45037
45038         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
45039         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
45040         
45041         
45042         var html = '<iframe id="' + this.getId() +
45043             '___Frame" src="' + sLink +
45044             '" width="' + this.width +
45045             '" height="' + this.height + '"' +
45046             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
45047             ' frameborder="0" scrolling="no"></iframe>' ;
45048
45049         return html ;
45050     },
45051     
45052     _insertHtmlBefore : function( html, element )
45053     {
45054         if ( element.insertAdjacentHTML )       {
45055             // IE
45056             element.insertAdjacentHTML( 'beforeBegin', html ) ;
45057         } else { // Gecko
45058             var oRange = document.createRange() ;
45059             oRange.setStartBefore( element ) ;
45060             var oFragment = oRange.createContextualFragment( html );
45061             element.parentNode.insertBefore( oFragment, element ) ;
45062         }
45063     }
45064     
45065     
45066   
45067     
45068     
45069     
45070     
45071
45072 });
45073
45074 //Roo.reg('fckeditor', Roo.form.FCKeditor);
45075
45076 function FCKeditor_OnComplete(editorInstance){
45077     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
45078     f.fckEditor = editorInstance;
45079     //console.log("loaded");
45080     f.fireEvent('editorinit', f, editorInstance);
45081
45082   
45083
45084  
45085
45086
45087
45088
45089
45090
45091
45092
45093
45094
45095
45096
45097
45098
45099
45100 //<script type="text/javascript">
45101 /**
45102  * @class Roo.form.GridField
45103  * @extends Roo.form.Field
45104  * Embed a grid (or editable grid into a form)
45105  * STATUS ALPHA
45106  * 
45107  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
45108  * it needs 
45109  * xgrid.store = Roo.data.Store
45110  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
45111  * xgrid.store.reader = Roo.data.JsonReader 
45112  * 
45113  * 
45114  * @constructor
45115  * Creates a new GridField
45116  * @param {Object} config Configuration options
45117  */
45118 Roo.form.GridField = function(config){
45119     Roo.form.GridField.superclass.constructor.call(this, config);
45120      
45121 };
45122
45123 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
45124     /**
45125      * @cfg {Number} width  - used to restrict width of grid..
45126      */
45127     width : 100,
45128     /**
45129      * @cfg {Number} height - used to restrict height of grid..
45130      */
45131     height : 50,
45132      /**
45133      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
45134          * 
45135          *}
45136      */
45137     xgrid : false, 
45138     /**
45139      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45140      * {tag: "input", type: "checkbox", autocomplete: "off"})
45141      */
45142    // defaultAutoCreate : { tag: 'div' },
45143     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45144     /**
45145      * @cfg {String} addTitle Text to include for adding a title.
45146      */
45147     addTitle : false,
45148     //
45149     onResize : function(){
45150         Roo.form.Field.superclass.onResize.apply(this, arguments);
45151     },
45152
45153     initEvents : function(){
45154         // Roo.form.Checkbox.superclass.initEvents.call(this);
45155         // has no events...
45156        
45157     },
45158
45159
45160     getResizeEl : function(){
45161         return this.wrap;
45162     },
45163
45164     getPositionEl : function(){
45165         return this.wrap;
45166     },
45167
45168     // private
45169     onRender : function(ct, position){
45170         
45171         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
45172         var style = this.style;
45173         delete this.style;
45174         
45175         Roo.form.GridField.superclass.onRender.call(this, ct, position);
45176         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
45177         this.viewEl = this.wrap.createChild({ tag: 'div' });
45178         if (style) {
45179             this.viewEl.applyStyles(style);
45180         }
45181         if (this.width) {
45182             this.viewEl.setWidth(this.width);
45183         }
45184         if (this.height) {
45185             this.viewEl.setHeight(this.height);
45186         }
45187         //if(this.inputValue !== undefined){
45188         //this.setValue(this.value);
45189         
45190         
45191         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
45192         
45193         
45194         this.grid.render();
45195         this.grid.getDataSource().on('remove', this.refreshValue, this);
45196         this.grid.getDataSource().on('update', this.refreshValue, this);
45197         this.grid.on('afteredit', this.refreshValue, this);
45198  
45199     },
45200      
45201     
45202     /**
45203      * Sets the value of the item. 
45204      * @param {String} either an object  or a string..
45205      */
45206     setValue : function(v){
45207         //this.value = v;
45208         v = v || []; // empty set..
45209         // this does not seem smart - it really only affects memoryproxy grids..
45210         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
45211             var ds = this.grid.getDataSource();
45212             // assumes a json reader..
45213             var data = {}
45214             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
45215             ds.loadData( data);
45216         }
45217         // clear selection so it does not get stale.
45218         if (this.grid.sm) { 
45219             this.grid.sm.clearSelections();
45220         }
45221         
45222         Roo.form.GridField.superclass.setValue.call(this, v);
45223         this.refreshValue();
45224         // should load data in the grid really....
45225     },
45226     
45227     // private
45228     refreshValue: function() {
45229          var val = [];
45230         this.grid.getDataSource().each(function(r) {
45231             val.push(r.data);
45232         });
45233         this.el.dom.value = Roo.encode(val);
45234     }
45235     
45236      
45237     
45238     
45239 });/*
45240  * Based on:
45241  * Ext JS Library 1.1.1
45242  * Copyright(c) 2006-2007, Ext JS, LLC.
45243  *
45244  * Originally Released Under LGPL - original licence link has changed is not relivant.
45245  *
45246  * Fork - LGPL
45247  * <script type="text/javascript">
45248  */
45249 /**
45250  * @class Roo.form.DisplayField
45251  * @extends Roo.form.Field
45252  * A generic Field to display non-editable data.
45253  * @constructor
45254  * Creates a new Display Field item.
45255  * @param {Object} config Configuration options
45256  */
45257 Roo.form.DisplayField = function(config){
45258     Roo.form.DisplayField.superclass.constructor.call(this, config);
45259     
45260 };
45261
45262 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
45263     inputType:      'hidden',
45264     allowBlank:     true,
45265     readOnly:         true,
45266     
45267  
45268     /**
45269      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45270      */
45271     focusClass : undefined,
45272     /**
45273      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45274      */
45275     fieldClass: 'x-form-field',
45276     
45277      /**
45278      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
45279      */
45280     valueRenderer: undefined,
45281     
45282     width: 100,
45283     /**
45284      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45285      * {tag: "input", type: "checkbox", autocomplete: "off"})
45286      */
45287      
45288  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
45289
45290     onResize : function(){
45291         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
45292         
45293     },
45294
45295     initEvents : function(){
45296         // Roo.form.Checkbox.superclass.initEvents.call(this);
45297         // has no events...
45298        
45299     },
45300
45301
45302     getResizeEl : function(){
45303         return this.wrap;
45304     },
45305
45306     getPositionEl : function(){
45307         return this.wrap;
45308     },
45309
45310     // private
45311     onRender : function(ct, position){
45312         
45313         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
45314         //if(this.inputValue !== undefined){
45315         this.wrap = this.el.wrap();
45316         
45317         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
45318         
45319         if (this.bodyStyle) {
45320             this.viewEl.applyStyles(this.bodyStyle);
45321         }
45322         //this.viewEl.setStyle('padding', '2px');
45323         
45324         this.setValue(this.value);
45325         
45326     },
45327 /*
45328     // private
45329     initValue : Roo.emptyFn,
45330
45331   */
45332
45333         // private
45334     onClick : function(){
45335         
45336     },
45337
45338     /**
45339      * Sets the checked state of the checkbox.
45340      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
45341      */
45342     setValue : function(v){
45343         this.value = v;
45344         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
45345         // this might be called before we have a dom element..
45346         if (!this.viewEl) {
45347             return;
45348         }
45349         this.viewEl.dom.innerHTML = html;
45350         Roo.form.DisplayField.superclass.setValue.call(this, v);
45351
45352     }
45353 });/*
45354  * 
45355  * Licence- LGPL
45356  * 
45357  */
45358
45359 /**
45360  * @class Roo.form.DayPicker
45361  * @extends Roo.form.Field
45362  * A Day picker show [M] [T] [W] ....
45363  * @constructor
45364  * Creates a new Day Picker
45365  * @param {Object} config Configuration options
45366  */
45367 Roo.form.DayPicker= function(config){
45368     Roo.form.DayPicker.superclass.constructor.call(this, config);
45369      
45370 };
45371
45372 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
45373     /**
45374      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
45375      */
45376     focusClass : undefined,
45377     /**
45378      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
45379      */
45380     fieldClass: "x-form-field",
45381    
45382     /**
45383      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
45384      * {tag: "input", type: "checkbox", autocomplete: "off"})
45385      */
45386     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
45387     
45388    
45389     actionMode : 'viewEl', 
45390     //
45391     // private
45392  
45393     inputType : 'hidden',
45394     
45395      
45396     inputElement: false, // real input element?
45397     basedOn: false, // ????
45398     
45399     isFormField: true, // not sure where this is needed!!!!
45400
45401     onResize : function(){
45402         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
45403         if(!this.boxLabel){
45404             this.el.alignTo(this.wrap, 'c-c');
45405         }
45406     },
45407
45408     initEvents : function(){
45409         Roo.form.Checkbox.superclass.initEvents.call(this);
45410         this.el.on("click", this.onClick,  this);
45411         this.el.on("change", this.onClick,  this);
45412     },
45413
45414
45415     getResizeEl : function(){
45416         return this.wrap;
45417     },
45418
45419     getPositionEl : function(){
45420         return this.wrap;
45421     },
45422
45423     
45424     // private
45425     onRender : function(ct, position){
45426         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45427        
45428         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
45429         
45430         var r1 = '<table><tr>';
45431         var r2 = '<tr class="x-form-daypick-icons">';
45432         for (var i=0; i < 7; i++) {
45433             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
45434             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
45435         }
45436         
45437         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
45438         viewEl.select('img').on('click', this.onClick, this);
45439         this.viewEl = viewEl;   
45440         
45441         
45442         // this will not work on Chrome!!!
45443         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
45444         this.el.on('propertychange', this.setFromHidden,  this);  //ie
45445         
45446         
45447           
45448
45449     },
45450
45451     // private
45452     initValue : Roo.emptyFn,
45453
45454     /**
45455      * Returns the checked state of the checkbox.
45456      * @return {Boolean} True if checked, else false
45457      */
45458     getValue : function(){
45459         return this.el.dom.value;
45460         
45461     },
45462
45463         // private
45464     onClick : function(e){ 
45465         //this.setChecked(!this.checked);
45466         Roo.get(e.target).toggleClass('x-menu-item-checked');
45467         this.refreshValue();
45468         //if(this.el.dom.checked != this.checked){
45469         //    this.setValue(this.el.dom.checked);
45470        // }
45471     },
45472     
45473     // private
45474     refreshValue : function()
45475     {
45476         var val = '';
45477         this.viewEl.select('img',true).each(function(e,i,n)  {
45478             val += e.is(".x-menu-item-checked") ? String(n) : '';
45479         });
45480         this.setValue(val, true);
45481     },
45482
45483     /**
45484      * Sets the checked state of the checkbox.
45485      * On is always based on a string comparison between inputValue and the param.
45486      * @param {Boolean/String} value - the value to set 
45487      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
45488      */
45489     setValue : function(v,suppressEvent){
45490         if (!this.el.dom) {
45491             return;
45492         }
45493         var old = this.el.dom.value ;
45494         this.el.dom.value = v;
45495         if (suppressEvent) {
45496             return ;
45497         }
45498          
45499         // update display..
45500         this.viewEl.select('img',true).each(function(e,i,n)  {
45501             
45502             var on = e.is(".x-menu-item-checked");
45503             var newv = v.indexOf(String(n)) > -1;
45504             if (on != newv) {
45505                 e.toggleClass('x-menu-item-checked');
45506             }
45507             
45508         });
45509         
45510         
45511         this.fireEvent('change', this, v, old);
45512         
45513         
45514     },
45515    
45516     // handle setting of hidden value by some other method!!?!?
45517     setFromHidden: function()
45518     {
45519         if(!this.el){
45520             return;
45521         }
45522         //console.log("SET FROM HIDDEN");
45523         //alert('setFrom hidden');
45524         this.setValue(this.el.dom.value);
45525     },
45526     
45527     onDestroy : function()
45528     {
45529         if(this.viewEl){
45530             Roo.get(this.viewEl).remove();
45531         }
45532          
45533         Roo.form.DayPicker.superclass.onDestroy.call(this);
45534     }
45535
45536 });/*
45537  * RooJS Library 1.1.1
45538  * Copyright(c) 2008-2011  Alan Knowles
45539  *
45540  * License - LGPL
45541  */
45542  
45543
45544 /**
45545  * @class Roo.form.ComboCheck
45546  * @extends Roo.form.ComboBox
45547  * A combobox for multiple select items.
45548  *
45549  * FIXME - could do with a reset button..
45550  * 
45551  * @constructor
45552  * Create a new ComboCheck
45553  * @param {Object} config Configuration options
45554  */
45555 Roo.form.ComboCheck = function(config){
45556     Roo.form.ComboCheck.superclass.constructor.call(this, config);
45557     // should verify some data...
45558     // like
45559     // hiddenName = required..
45560     // displayField = required
45561     // valudField == required
45562     var req= [ 'hiddenName', 'displayField', 'valueField' ];
45563     var _t = this;
45564     Roo.each(req, function(e) {
45565         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
45566             throw "Roo.form.ComboCheck : missing value for: " + e;
45567         }
45568     });
45569     
45570     
45571 };
45572
45573 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
45574      
45575      
45576     editable : false,
45577      
45578     selectedClass: 'x-menu-item-checked', 
45579     
45580     // private
45581     onRender : function(ct, position){
45582         var _t = this;
45583         
45584         
45585         
45586         if(!this.tpl){
45587             var cls = 'x-combo-list';
45588
45589             
45590             this.tpl =  new Roo.Template({
45591                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
45592                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
45593                    '<span>{' + this.displayField + '}</span>' +
45594                     '</div>' 
45595                 
45596             });
45597         }
45598  
45599         
45600         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
45601         this.view.singleSelect = false;
45602         this.view.multiSelect = true;
45603         this.view.toggleSelect = true;
45604         this.pageTb.add(new Roo.Toolbar.Fill(), {
45605             
45606             text: 'Done',
45607             handler: function()
45608             {
45609                 _t.collapse();
45610             }
45611         });
45612     },
45613     
45614     onViewOver : function(e, t){
45615         // do nothing...
45616         return;
45617         
45618     },
45619     
45620     onViewClick : function(doFocus,index){
45621         return;
45622         
45623     },
45624     select: function () {
45625         //Roo.log("SELECT CALLED");
45626     },
45627      
45628     selectByValue : function(xv, scrollIntoView){
45629         var ar = this.getValueArray();
45630         var sels = [];
45631         
45632         Roo.each(ar, function(v) {
45633             if(v === undefined || v === null){
45634                 return;
45635             }
45636             var r = this.findRecord(this.valueField, v);
45637             if(r){
45638                 sels.push(this.store.indexOf(r))
45639                 
45640             }
45641         },this);
45642         this.view.select(sels);
45643         return false;
45644     },
45645     
45646     
45647     
45648     onSelect : function(record, index){
45649        // Roo.log("onselect Called");
45650        // this is only called by the clear button now..
45651         this.view.clearSelections();
45652         this.setValue('[]');
45653         if (this.value != this.valueBefore) {
45654             this.fireEvent('change', this, this.value, this.valueBefore);
45655             this.valueBefore = this.value;
45656         }
45657     },
45658     getValueArray : function()
45659     {
45660         var ar = [] ;
45661         
45662         try {
45663             //Roo.log(this.value);
45664             if (typeof(this.value) == 'undefined') {
45665                 return [];
45666             }
45667             var ar = Roo.decode(this.value);
45668             return  ar instanceof Array ? ar : []; //?? valid?
45669             
45670         } catch(e) {
45671             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
45672             return [];
45673         }
45674          
45675     },
45676     expand : function ()
45677     {
45678         
45679         Roo.form.ComboCheck.superclass.expand.call(this);
45680         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
45681         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
45682         
45683
45684     },
45685     
45686     collapse : function(){
45687         Roo.form.ComboCheck.superclass.collapse.call(this);
45688         var sl = this.view.getSelectedIndexes();
45689         var st = this.store;
45690         var nv = [];
45691         var tv = [];
45692         var r;
45693         Roo.each(sl, function(i) {
45694             r = st.getAt(i);
45695             nv.push(r.get(this.valueField));
45696         },this);
45697         this.setValue(Roo.encode(nv));
45698         if (this.value != this.valueBefore) {
45699
45700             this.fireEvent('change', this, this.value, this.valueBefore);
45701             this.valueBefore = this.value;
45702         }
45703         
45704     },
45705     
45706     setValue : function(v){
45707         // Roo.log(v);
45708         this.value = v;
45709         
45710         var vals = this.getValueArray();
45711         var tv = [];
45712         Roo.each(vals, function(k) {
45713             var r = this.findRecord(this.valueField, k);
45714             if(r){
45715                 tv.push(r.data[this.displayField]);
45716             }else if(this.valueNotFoundText !== undefined){
45717                 tv.push( this.valueNotFoundText );
45718             }
45719         },this);
45720        // Roo.log(tv);
45721         
45722         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
45723         this.hiddenField.value = v;
45724         this.value = v;
45725     }
45726     
45727 });/*
45728  * Based on:
45729  * Ext JS Library 1.1.1
45730  * Copyright(c) 2006-2007, Ext JS, LLC.
45731  *
45732  * Originally Released Under LGPL - original licence link has changed is not relivant.
45733  *
45734  * Fork - LGPL
45735  * <script type="text/javascript">
45736  */
45737  
45738 /**
45739  * @class Roo.form.Signature
45740  * @extends Roo.form.Field
45741  * Signature field.  
45742  * @constructor
45743  * 
45744  * @param {Object} config Configuration options
45745  */
45746
45747 Roo.form.Signature = function(config){
45748     Roo.form.Signature.superclass.constructor.call(this, config);
45749     
45750     this.addEvents({// not in used??
45751          /**
45752          * @event confirm
45753          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
45754              * @param {Roo.form.Signature} combo This combo box
45755              */
45756         'confirm' : true,
45757         /**
45758          * @event reset
45759          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
45760              * @param {Roo.form.ComboBox} combo This combo box
45761              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
45762              */
45763         'reset' : true
45764     });
45765 };
45766
45767 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
45768     /**
45769      * @cfg {Object} labels Label to use when rendering a form.
45770      * defaults to 
45771      * labels : { 
45772      *      clear : "Clear",
45773      *      confirm : "Confirm"
45774      *  }
45775      */
45776     labels : { 
45777         clear : "Clear",
45778         confirm : "Confirm"
45779     },
45780     /**
45781      * @cfg {Number} width The signature panel width (defaults to 300)
45782      */
45783     width: 300,
45784     /**
45785      * @cfg {Number} height The signature panel height (defaults to 100)
45786      */
45787     height : 100,
45788     /**
45789      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
45790      */
45791     allowBlank : false,
45792     
45793     //private
45794     // {Object} signPanel The signature SVG panel element (defaults to {})
45795     signPanel : {},
45796     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
45797     isMouseDown : false,
45798     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
45799     isConfirmed : false,
45800     // {String} signatureTmp SVG mapping string (defaults to empty string)
45801     signatureTmp : '',
45802     
45803     
45804     defaultAutoCreate : { // modified by initCompnoent..
45805         tag: "input",
45806         type:"hidden"
45807     },
45808
45809     // private
45810     onRender : function(ct, position){
45811         
45812         Roo.form.Signature.superclass.onRender.call(this, ct, position);
45813         
45814         this.wrap = this.el.wrap({
45815             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
45816         });
45817         
45818         this.createToolbar(this);
45819         this.signPanel = this.wrap.createChild({
45820                 tag: 'div',
45821                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
45822             }, this.el
45823         );
45824             
45825         this.svgID = Roo.id();
45826         this.svgEl = this.signPanel.createChild({
45827               xmlns : 'http://www.w3.org/2000/svg',
45828               tag : 'svg',
45829               id : this.svgID + "-svg",
45830               width: this.width,
45831               height: this.height,
45832               viewBox: '0 0 '+this.width+' '+this.height,
45833               cn : [
45834                 {
45835                     tag: "rect",
45836                     id: this.svgID + "-svg-r",
45837                     width: this.width,
45838                     height: this.height,
45839                     fill: "#ffa"
45840                 },
45841                 {
45842                     tag: "line",
45843                     id: this.svgID + "-svg-l",
45844                     x1: "0", // start
45845                     y1: (this.height*0.8), // start set the line in 80% of height
45846                     x2: this.width, // end
45847                     y2: (this.height*0.8), // end set the line in 80% of height
45848                     'stroke': "#666",
45849                     'stroke-width': "1",
45850                     'stroke-dasharray': "3",
45851                     'shape-rendering': "crispEdges",
45852                     'pointer-events': "none"
45853                 },
45854                 {
45855                     tag: "path",
45856                     id: this.svgID + "-svg-p",
45857                     'stroke': "navy",
45858                     'stroke-width': "3",
45859                     'fill': "none",
45860                     'pointer-events': 'none'
45861                 }
45862               ]
45863         });
45864         this.createSVG();
45865         this.svgBox = this.svgEl.dom.getScreenCTM();
45866     },
45867     createSVG : function(){ 
45868         var svg = this.signPanel;
45869         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
45870         var t = this;
45871
45872         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
45873         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
45874         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
45875         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
45876         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
45877         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
45878         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
45879         
45880     },
45881     isTouchEvent : function(e){
45882         return e.type.match(/^touch/);
45883     },
45884     getCoords : function (e) {
45885         var pt    = this.svgEl.dom.createSVGPoint();
45886         pt.x = e.clientX; 
45887         pt.y = e.clientY;
45888         if (this.isTouchEvent(e)) {
45889             pt.x =  e.targetTouches[0].clientX 
45890             pt.y = e.targetTouches[0].clientY;
45891         }
45892         var a = this.svgEl.dom.getScreenCTM();
45893         var b = a.inverse();
45894         var mx = pt.matrixTransform(b);
45895         return mx.x + ',' + mx.y;
45896     },
45897     //mouse event headler 
45898     down : function (e) {
45899         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
45900         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
45901         
45902         this.isMouseDown = true;
45903         
45904         e.preventDefault();
45905     },
45906     move : function (e) {
45907         if (this.isMouseDown) {
45908             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
45909             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
45910         }
45911         
45912         e.preventDefault();
45913     },
45914     up : function (e) {
45915         this.isMouseDown = false;
45916         var sp = this.signatureTmp.split(' ');
45917         
45918         if(sp.length > 1){
45919             if(!sp[sp.length-2].match(/^L/)){
45920                 sp.pop();
45921                 sp.pop();
45922                 sp.push("");
45923                 this.signatureTmp = sp.join(" ");
45924             }
45925         }
45926         if(this.getValue() != this.signatureTmp){
45927             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
45928             this.isConfirmed = false;
45929         }
45930         e.preventDefault();
45931     },
45932     
45933     /**
45934      * Protected method that will not generally be called directly. It
45935      * is called when the editor creates its toolbar. Override this method if you need to
45936      * add custom toolbar buttons.
45937      * @param {HtmlEditor} editor
45938      */
45939     createToolbar : function(editor){
45940          function btn(id, toggle, handler){
45941             var xid = fid + '-'+ id ;
45942             return {
45943                 id : xid,
45944                 cmd : id,
45945                 cls : 'x-btn-icon x-edit-'+id,
45946                 enableToggle:toggle !== false,
45947                 scope: editor, // was editor...
45948                 handler:handler||editor.relayBtnCmd,
45949                 clickEvent:'mousedown',
45950                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45951                 tabIndex:-1
45952             };
45953         }
45954         
45955         
45956         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45957         this.tb = tb;
45958         this.tb.add(
45959            {
45960                 cls : ' x-signature-btn x-signature-'+id,
45961                 scope: editor, // was editor...
45962                 handler: this.reset,
45963                 clickEvent:'mousedown',
45964                 text: this.labels.clear
45965             },
45966             {
45967                  xtype : 'Fill',
45968                  xns: Roo.Toolbar
45969             }, 
45970             {
45971                 cls : '  x-signature-btn x-signature-'+id,
45972                 scope: editor, // was editor...
45973                 handler: this.confirmHandler,
45974                 clickEvent:'mousedown',
45975                 text: this.labels.confirm
45976             }
45977         );
45978     
45979     },
45980     //public
45981     /**
45982      * when user is clicked confirm then show this image.....
45983      * 
45984      * @return {String} Image Data URI
45985      */
45986     getImageDataURI : function(){
45987         var svg = this.svgEl.dom.parentNode.innerHTML;
45988         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
45989         return src; 
45990     },
45991     /**
45992      * 
45993      * @return {Boolean} this.isConfirmed
45994      */
45995     getConfirmed : function(){
45996         return this.isConfirmed;
45997     },
45998     /**
45999      * 
46000      * @return {Number} this.width
46001      */
46002     getWidth : function(){
46003         return this.width;
46004     },
46005     /**
46006      * 
46007      * @return {Number} this.height
46008      */
46009     getHeight : function(){
46010         return this.height;
46011     },
46012     // private
46013     getSignature : function(){
46014         return this.signatureTmp;
46015     },
46016     // private
46017     reset : function(){
46018         this.signatureTmp = '';
46019         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46020         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
46021         this.isConfirmed = false;
46022         Roo.form.Signature.superclass.reset.call(this);
46023     },
46024     setSignature : function(s){
46025         this.signatureTmp = s;
46026         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46027         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
46028         this.setValue(s);
46029         this.isConfirmed = false;
46030         Roo.form.Signature.superclass.reset.call(this);
46031     }, 
46032     test : function(){
46033 //        Roo.log(this.signPanel.dom.contentWindow.up())
46034     },
46035     //private
46036     setConfirmed : function(){
46037         
46038         
46039         
46040 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
46041     },
46042     // private
46043     confirmHandler : function(){
46044         if(!this.getSignature()){
46045             return;
46046         }
46047         
46048         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
46049         this.setValue(this.getSignature());
46050         this.isConfirmed = true;
46051         
46052         this.fireEvent('confirm', this);
46053     },
46054     // private
46055     // Subclasses should provide the validation implementation by overriding this
46056     validateValue : function(value){
46057         if(this.allowBlank){
46058             return true;
46059         }
46060         
46061         if(this.isConfirmed){
46062             return true;
46063         }
46064         return false;
46065     }
46066 });//<script type="text/javasscript">
46067  
46068
46069 /**
46070  * @class Roo.DDView
46071  * A DnD enabled version of Roo.View.
46072  * @param {Element/String} container The Element in which to create the View.
46073  * @param {String} tpl The template string used to create the markup for each element of the View
46074  * @param {Object} config The configuration properties. These include all the config options of
46075  * {@link Roo.View} plus some specific to this class.<br>
46076  * <p>
46077  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
46078  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
46079  * <p>
46080  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
46081 .x-view-drag-insert-above {
46082         border-top:1px dotted #3366cc;
46083 }
46084 .x-view-drag-insert-below {
46085         border-bottom:1px dotted #3366cc;
46086 }
46087 </code></pre>
46088  * 
46089  */
46090  
46091 Roo.DDView = function(container, tpl, config) {
46092     Roo.DDView.superclass.constructor.apply(this, arguments);
46093     this.getEl().setStyle("outline", "0px none");
46094     this.getEl().unselectable();
46095     if (this.dragGroup) {
46096                 this.setDraggable(this.dragGroup.split(","));
46097     }
46098     if (this.dropGroup) {
46099                 this.setDroppable(this.dropGroup.split(","));
46100     }
46101     if (this.deletable) {
46102         this.setDeletable();
46103     }
46104     this.isDirtyFlag = false;
46105         this.addEvents({
46106                 "drop" : true
46107         });
46108 };
46109
46110 Roo.extend(Roo.DDView, Roo.View, {
46111 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
46112 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
46113 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
46114 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
46115
46116         isFormField: true,
46117
46118         reset: Roo.emptyFn,
46119         
46120         clearInvalid: Roo.form.Field.prototype.clearInvalid,
46121
46122         validate: function() {
46123                 return true;
46124         },
46125         
46126         destroy: function() {
46127                 this.purgeListeners();
46128                 this.getEl.removeAllListeners();
46129                 this.getEl().remove();
46130                 if (this.dragZone) {
46131                         if (this.dragZone.destroy) {
46132                                 this.dragZone.destroy();
46133                         }
46134                 }
46135                 if (this.dropZone) {
46136                         if (this.dropZone.destroy) {
46137                                 this.dropZone.destroy();
46138                         }
46139                 }
46140         },
46141
46142 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
46143         getName: function() {
46144                 return this.name;
46145         },
46146
46147 /**     Loads the View from a JSON string representing the Records to put into the Store. */
46148         setValue: function(v) {
46149                 if (!this.store) {
46150                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
46151                 }
46152                 var data = {};
46153                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
46154                 this.store.proxy = new Roo.data.MemoryProxy(data);
46155                 this.store.load();
46156         },
46157
46158 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
46159         getValue: function() {
46160                 var result = '(';
46161                 this.store.each(function(rec) {
46162                         result += rec.id + ',';
46163                 });
46164                 return result.substr(0, result.length - 1) + ')';
46165         },
46166         
46167         getIds: function() {
46168                 var i = 0, result = new Array(this.store.getCount());
46169                 this.store.each(function(rec) {
46170                         result[i++] = rec.id;
46171                 });
46172                 return result;
46173         },
46174         
46175         isDirty: function() {
46176                 return this.isDirtyFlag;
46177         },
46178
46179 /**
46180  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
46181  *      whole Element becomes the target, and this causes the drop gesture to append.
46182  */
46183     getTargetFromEvent : function(e) {
46184                 var target = e.getTarget();
46185                 while ((target !== null) && (target.parentNode != this.el.dom)) {
46186                 target = target.parentNode;
46187                 }
46188                 if (!target) {
46189                         target = this.el.dom.lastChild || this.el.dom;
46190                 }
46191                 return target;
46192     },
46193
46194 /**
46195  *      Create the drag data which consists of an object which has the property "ddel" as
46196  *      the drag proxy element. 
46197  */
46198     getDragData : function(e) {
46199         var target = this.findItemFromChild(e.getTarget());
46200                 if(target) {
46201                         this.handleSelection(e);
46202                         var selNodes = this.getSelectedNodes();
46203             var dragData = {
46204                 source: this,
46205                 copy: this.copy || (this.allowCopy && e.ctrlKey),
46206                 nodes: selNodes,
46207                 records: []
46208                         };
46209                         var selectedIndices = this.getSelectedIndexes();
46210                         for (var i = 0; i < selectedIndices.length; i++) {
46211                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
46212                         }
46213                         if (selNodes.length == 1) {
46214                                 dragData.ddel = target.cloneNode(true); // the div element
46215                         } else {
46216                                 var div = document.createElement('div'); // create the multi element drag "ghost"
46217                                 div.className = 'multi-proxy';
46218                                 for (var i = 0, len = selNodes.length; i < len; i++) {
46219                                         div.appendChild(selNodes[i].cloneNode(true));
46220                                 }
46221                                 dragData.ddel = div;
46222                         }
46223             //console.log(dragData)
46224             //console.log(dragData.ddel.innerHTML)
46225                         return dragData;
46226                 }
46227         //console.log('nodragData')
46228                 return false;
46229     },
46230     
46231 /**     Specify to which ddGroup items in this DDView may be dragged. */
46232     setDraggable: function(ddGroup) {
46233         if (ddGroup instanceof Array) {
46234                 Roo.each(ddGroup, this.setDraggable, this);
46235                 return;
46236         }
46237         if (this.dragZone) {
46238                 this.dragZone.addToGroup(ddGroup);
46239         } else {
46240                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
46241                                 containerScroll: true,
46242                                 ddGroup: ddGroup 
46243
46244                         });
46245 //                      Draggability implies selection. DragZone's mousedown selects the element.
46246                         if (!this.multiSelect) { this.singleSelect = true; }
46247
46248 //                      Wire the DragZone's handlers up to methods in *this*
46249                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
46250                 }
46251     },
46252
46253 /**     Specify from which ddGroup this DDView accepts drops. */
46254     setDroppable: function(ddGroup) {
46255         if (ddGroup instanceof Array) {
46256                 Roo.each(ddGroup, this.setDroppable, this);
46257                 return;
46258         }
46259         if (this.dropZone) {
46260                 this.dropZone.addToGroup(ddGroup);
46261         } else {
46262                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
46263                                 containerScroll: true,
46264                                 ddGroup: ddGroup
46265                         });
46266
46267 //                      Wire the DropZone's handlers up to methods in *this*
46268                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
46269                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
46270                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
46271                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
46272                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
46273                 }
46274     },
46275
46276 /**     Decide whether to drop above or below a View node. */
46277     getDropPoint : function(e, n, dd){
46278         if (n == this.el.dom) { return "above"; }
46279                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
46280                 var c = t + (b - t) / 2;
46281                 var y = Roo.lib.Event.getPageY(e);
46282                 if(y <= c) {
46283                         return "above";
46284                 }else{
46285                         return "below";
46286                 }
46287     },
46288
46289     onNodeEnter : function(n, dd, e, data){
46290                 return false;
46291     },
46292     
46293     onNodeOver : function(n, dd, e, data){
46294                 var pt = this.getDropPoint(e, n, dd);
46295                 // set the insert point style on the target node
46296                 var dragElClass = this.dropNotAllowed;
46297                 if (pt) {
46298                         var targetElClass;
46299                         if (pt == "above"){
46300                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
46301                                 targetElClass = "x-view-drag-insert-above";
46302                         } else {
46303                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
46304                                 targetElClass = "x-view-drag-insert-below";
46305                         }
46306                         if (this.lastInsertClass != targetElClass){
46307                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
46308                                 this.lastInsertClass = targetElClass;
46309                         }
46310                 }
46311                 return dragElClass;
46312         },
46313
46314     onNodeOut : function(n, dd, e, data){
46315                 this.removeDropIndicators(n);
46316     },
46317
46318     onNodeDrop : function(n, dd, e, data){
46319         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
46320                 return false;
46321         }
46322         var pt = this.getDropPoint(e, n, dd);
46323                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
46324                 if (pt == "below") { insertAt++; }
46325                 for (var i = 0; i < data.records.length; i++) {
46326                         var r = data.records[i];
46327                         var dup = this.store.getById(r.id);
46328                         if (dup && (dd != this.dragZone)) {
46329                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
46330                         } else {
46331                                 if (data.copy) {
46332                                         this.store.insert(insertAt++, r.copy());
46333                                 } else {
46334                                         data.source.isDirtyFlag = true;
46335                                         r.store.remove(r);
46336                                         this.store.insert(insertAt++, r);
46337                                 }
46338                                 this.isDirtyFlag = true;
46339                         }
46340                 }
46341                 this.dragZone.cachedTarget = null;
46342                 return true;
46343     },
46344
46345     removeDropIndicators : function(n){
46346                 if(n){
46347                         Roo.fly(n).removeClass([
46348                                 "x-view-drag-insert-above",
46349                                 "x-view-drag-insert-below"]);
46350                         this.lastInsertClass = "_noclass";
46351                 }
46352     },
46353
46354 /**
46355  *      Utility method. Add a delete option to the DDView's context menu.
46356  *      @param {String} imageUrl The URL of the "delete" icon image.
46357  */
46358         setDeletable: function(imageUrl) {
46359                 if (!this.singleSelect && !this.multiSelect) {
46360                         this.singleSelect = true;
46361                 }
46362                 var c = this.getContextMenu();
46363                 this.contextMenu.on("itemclick", function(item) {
46364                         switch (item.id) {
46365                                 case "delete":
46366                                         this.remove(this.getSelectedIndexes());
46367                                         break;
46368                         }
46369                 }, this);
46370                 this.contextMenu.add({
46371                         icon: imageUrl,
46372                         id: "delete",
46373                         text: 'Delete'
46374                 });
46375         },
46376         
46377 /**     Return the context menu for this DDView. */
46378         getContextMenu: function() {
46379                 if (!this.contextMenu) {
46380 //                      Create the View's context menu
46381                         this.contextMenu = new Roo.menu.Menu({
46382                                 id: this.id + "-contextmenu"
46383                         });
46384                         this.el.on("contextmenu", this.showContextMenu, this);
46385                 }
46386                 return this.contextMenu;
46387         },
46388         
46389         disableContextMenu: function() {
46390                 if (this.contextMenu) {
46391                         this.el.un("contextmenu", this.showContextMenu, this);
46392                 }
46393         },
46394
46395         showContextMenu: function(e, item) {
46396         item = this.findItemFromChild(e.getTarget());
46397                 if (item) {
46398                         e.stopEvent();
46399                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
46400                         this.contextMenu.showAt(e.getXY());
46401             }
46402     },
46403
46404 /**
46405  *      Remove {@link Roo.data.Record}s at the specified indices.
46406  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
46407  */
46408     remove: function(selectedIndices) {
46409                 selectedIndices = [].concat(selectedIndices);
46410                 for (var i = 0; i < selectedIndices.length; i++) {
46411                         var rec = this.store.getAt(selectedIndices[i]);
46412                         this.store.remove(rec);
46413                 }
46414     },
46415
46416 /**
46417  *      Double click fires the event, but also, if this is draggable, and there is only one other
46418  *      related DropZone, it transfers the selected node.
46419  */
46420     onDblClick : function(e){
46421         var item = this.findItemFromChild(e.getTarget());
46422         if(item){
46423             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
46424                 return false;
46425             }
46426             if (this.dragGroup) {
46427                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
46428                     while (targets.indexOf(this.dropZone) > -1) {
46429                             targets.remove(this.dropZone);
46430                                 }
46431                     if (targets.length == 1) {
46432                                         this.dragZone.cachedTarget = null;
46433                         var el = Roo.get(targets[0].getEl());
46434                         var box = el.getBox(true);
46435                         targets[0].onNodeDrop(el.dom, {
46436                                 target: el.dom,
46437                                 xy: [box.x, box.y + box.height - 1]
46438                         }, null, this.getDragData(e));
46439                     }
46440                 }
46441         }
46442     },
46443     
46444     handleSelection: function(e) {
46445                 this.dragZone.cachedTarget = null;
46446         var item = this.findItemFromChild(e.getTarget());
46447         if (!item) {
46448                 this.clearSelections(true);
46449                 return;
46450         }
46451                 if (item && (this.multiSelect || this.singleSelect)){
46452                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
46453                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
46454                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
46455                                 this.unselect(item);
46456                         } else {
46457                                 this.select(item, this.multiSelect && e.ctrlKey);
46458                                 this.lastSelection = item;
46459                         }
46460                 }
46461     },
46462
46463     onItemClick : function(item, index, e){
46464                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
46465                         return false;
46466                 }
46467                 return true;
46468     },
46469
46470     unselect : function(nodeInfo, suppressEvent){
46471                 var node = this.getNode(nodeInfo);
46472                 if(node && this.isSelected(node)){
46473                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
46474                                 Roo.fly(node).removeClass(this.selectedClass);
46475                                 this.selections.remove(node);
46476                                 if(!suppressEvent){
46477                                         this.fireEvent("selectionchange", this, this.selections);
46478                                 }
46479                         }
46480                 }
46481     }
46482 });
46483 /*
46484  * Based on:
46485  * Ext JS Library 1.1.1
46486  * Copyright(c) 2006-2007, Ext JS, LLC.
46487  *
46488  * Originally Released Under LGPL - original licence link has changed is not relivant.
46489  *
46490  * Fork - LGPL
46491  * <script type="text/javascript">
46492  */
46493  
46494 /**
46495  * @class Roo.LayoutManager
46496  * @extends Roo.util.Observable
46497  * Base class for layout managers.
46498  */
46499 Roo.LayoutManager = function(container, config){
46500     Roo.LayoutManager.superclass.constructor.call(this);
46501     this.el = Roo.get(container);
46502     // ie scrollbar fix
46503     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
46504         document.body.scroll = "no";
46505     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
46506         this.el.position('relative');
46507     }
46508     this.id = this.el.id;
46509     this.el.addClass("x-layout-container");
46510     /** false to disable window resize monitoring @type Boolean */
46511     this.monitorWindowResize = true;
46512     this.regions = {};
46513     this.addEvents({
46514         /**
46515          * @event layout
46516          * Fires when a layout is performed. 
46517          * @param {Roo.LayoutManager} this
46518          */
46519         "layout" : true,
46520         /**
46521          * @event regionresized
46522          * Fires when the user resizes a region. 
46523          * @param {Roo.LayoutRegion} region The resized region
46524          * @param {Number} newSize The new size (width for east/west, height for north/south)
46525          */
46526         "regionresized" : true,
46527         /**
46528          * @event regioncollapsed
46529          * Fires when a region is collapsed. 
46530          * @param {Roo.LayoutRegion} region The collapsed region
46531          */
46532         "regioncollapsed" : true,
46533         /**
46534          * @event regionexpanded
46535          * Fires when a region is expanded.  
46536          * @param {Roo.LayoutRegion} region The expanded region
46537          */
46538         "regionexpanded" : true
46539     });
46540     this.updating = false;
46541     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
46542 };
46543
46544 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
46545     /**
46546      * Returns true if this layout is currently being updated
46547      * @return {Boolean}
46548      */
46549     isUpdating : function(){
46550         return this.updating; 
46551     },
46552     
46553     /**
46554      * Suspend the LayoutManager from doing auto-layouts while
46555      * making multiple add or remove calls
46556      */
46557     beginUpdate : function(){
46558         this.updating = true;    
46559     },
46560     
46561     /**
46562      * Restore auto-layouts and optionally disable the manager from performing a layout
46563      * @param {Boolean} noLayout true to disable a layout update 
46564      */
46565     endUpdate : function(noLayout){
46566         this.updating = false;
46567         if(!noLayout){
46568             this.layout();
46569         }    
46570     },
46571     
46572     layout: function(){
46573         
46574     },
46575     
46576     onRegionResized : function(region, newSize){
46577         this.fireEvent("regionresized", region, newSize);
46578         this.layout();
46579     },
46580     
46581     onRegionCollapsed : function(region){
46582         this.fireEvent("regioncollapsed", region);
46583     },
46584     
46585     onRegionExpanded : function(region){
46586         this.fireEvent("regionexpanded", region);
46587     },
46588         
46589     /**
46590      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
46591      * performs box-model adjustments.
46592      * @return {Object} The size as an object {width: (the width), height: (the height)}
46593      */
46594     getViewSize : function(){
46595         var size;
46596         if(this.el.dom != document.body){
46597             size = this.el.getSize();
46598         }else{
46599             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
46600         }
46601         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
46602         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46603         return size;
46604     },
46605     
46606     /**
46607      * Returns the Element this layout is bound to.
46608      * @return {Roo.Element}
46609      */
46610     getEl : function(){
46611         return this.el;
46612     },
46613     
46614     /**
46615      * Returns the specified region.
46616      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
46617      * @return {Roo.LayoutRegion}
46618      */
46619     getRegion : function(target){
46620         return this.regions[target.toLowerCase()];
46621     },
46622     
46623     onWindowResize : function(){
46624         if(this.monitorWindowResize){
46625             this.layout();
46626         }
46627     }
46628 });/*
46629  * Based on:
46630  * Ext JS Library 1.1.1
46631  * Copyright(c) 2006-2007, Ext JS, LLC.
46632  *
46633  * Originally Released Under LGPL - original licence link has changed is not relivant.
46634  *
46635  * Fork - LGPL
46636  * <script type="text/javascript">
46637  */
46638 /**
46639  * @class Roo.BorderLayout
46640  * @extends Roo.LayoutManager
46641  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
46642  * please see: <br><br>
46643  * <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>
46644  * <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>
46645  * Example:
46646  <pre><code>
46647  var layout = new Roo.BorderLayout(document.body, {
46648     north: {
46649         initialSize: 25,
46650         titlebar: false
46651     },
46652     west: {
46653         split:true,
46654         initialSize: 200,
46655         minSize: 175,
46656         maxSize: 400,
46657         titlebar: true,
46658         collapsible: true
46659     },
46660     east: {
46661         split:true,
46662         initialSize: 202,
46663         minSize: 175,
46664         maxSize: 400,
46665         titlebar: true,
46666         collapsible: true
46667     },
46668     south: {
46669         split:true,
46670         initialSize: 100,
46671         minSize: 100,
46672         maxSize: 200,
46673         titlebar: true,
46674         collapsible: true
46675     },
46676     center: {
46677         titlebar: true,
46678         autoScroll:true,
46679         resizeTabs: true,
46680         minTabWidth: 50,
46681         preferredTabWidth: 150
46682     }
46683 });
46684
46685 // shorthand
46686 var CP = Roo.ContentPanel;
46687
46688 layout.beginUpdate();
46689 layout.add("north", new CP("north", "North"));
46690 layout.add("south", new CP("south", {title: "South", closable: true}));
46691 layout.add("west", new CP("west", {title: "West"}));
46692 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
46693 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
46694 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
46695 layout.getRegion("center").showPanel("center1");
46696 layout.endUpdate();
46697 </code></pre>
46698
46699 <b>The container the layout is rendered into can be either the body element or any other element.
46700 If it is not the body element, the container needs to either be an absolute positioned element,
46701 or you will need to add "position:relative" to the css of the container.  You will also need to specify
46702 the container size if it is not the body element.</b>
46703
46704 * @constructor
46705 * Create a new BorderLayout
46706 * @param {String/HTMLElement/Element} container The container this layout is bound to
46707 * @param {Object} config Configuration options
46708  */
46709 Roo.BorderLayout = function(container, config){
46710     config = config || {};
46711     Roo.BorderLayout.superclass.constructor.call(this, container, config);
46712     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
46713     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
46714         var target = this.factory.validRegions[i];
46715         if(config[target]){
46716             this.addRegion(target, config[target]);
46717         }
46718     }
46719 };
46720
46721 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
46722     /**
46723      * Creates and adds a new region if it doesn't already exist.
46724      * @param {String} target The target region key (north, south, east, west or center).
46725      * @param {Object} config The regions config object
46726      * @return {BorderLayoutRegion} The new region
46727      */
46728     addRegion : function(target, config){
46729         if(!this.regions[target]){
46730             var r = this.factory.create(target, this, config);
46731             this.bindRegion(target, r);
46732         }
46733         return this.regions[target];
46734     },
46735
46736     // private (kinda)
46737     bindRegion : function(name, r){
46738         this.regions[name] = r;
46739         r.on("visibilitychange", this.layout, this);
46740         r.on("paneladded", this.layout, this);
46741         r.on("panelremoved", this.layout, this);
46742         r.on("invalidated", this.layout, this);
46743         r.on("resized", this.onRegionResized, this);
46744         r.on("collapsed", this.onRegionCollapsed, this);
46745         r.on("expanded", this.onRegionExpanded, this);
46746     },
46747
46748     /**
46749      * Performs a layout update.
46750      */
46751     layout : function(){
46752         if(this.updating) return;
46753         var size = this.getViewSize();
46754         var w = size.width;
46755         var h = size.height;
46756         var centerW = w;
46757         var centerH = h;
46758         var centerY = 0;
46759         var centerX = 0;
46760         //var x = 0, y = 0;
46761
46762         var rs = this.regions;
46763         var north = rs["north"];
46764         var south = rs["south"]; 
46765         var west = rs["west"];
46766         var east = rs["east"];
46767         var center = rs["center"];
46768         //if(this.hideOnLayout){ // not supported anymore
46769             //c.el.setStyle("display", "none");
46770         //}
46771         if(north && north.isVisible()){
46772             var b = north.getBox();
46773             var m = north.getMargins();
46774             b.width = w - (m.left+m.right);
46775             b.x = m.left;
46776             b.y = m.top;
46777             centerY = b.height + b.y + m.bottom;
46778             centerH -= centerY;
46779             north.updateBox(this.safeBox(b));
46780         }
46781         if(south && south.isVisible()){
46782             var b = south.getBox();
46783             var m = south.getMargins();
46784             b.width = w - (m.left+m.right);
46785             b.x = m.left;
46786             var totalHeight = (b.height + m.top + m.bottom);
46787             b.y = h - totalHeight + m.top;
46788             centerH -= totalHeight;
46789             south.updateBox(this.safeBox(b));
46790         }
46791         if(west && west.isVisible()){
46792             var b = west.getBox();
46793             var m = west.getMargins();
46794             b.height = centerH - (m.top+m.bottom);
46795             b.x = m.left;
46796             b.y = centerY + m.top;
46797             var totalWidth = (b.width + m.left + m.right);
46798             centerX += totalWidth;
46799             centerW -= totalWidth;
46800             west.updateBox(this.safeBox(b));
46801         }
46802         if(east && east.isVisible()){
46803             var b = east.getBox();
46804             var m = east.getMargins();
46805             b.height = centerH - (m.top+m.bottom);
46806             var totalWidth = (b.width + m.left + m.right);
46807             b.x = w - totalWidth + m.left;
46808             b.y = centerY + m.top;
46809             centerW -= totalWidth;
46810             east.updateBox(this.safeBox(b));
46811         }
46812         if(center){
46813             var m = center.getMargins();
46814             var centerBox = {
46815                 x: centerX + m.left,
46816                 y: centerY + m.top,
46817                 width: centerW - (m.left+m.right),
46818                 height: centerH - (m.top+m.bottom)
46819             };
46820             //if(this.hideOnLayout){
46821                 //center.el.setStyle("display", "block");
46822             //}
46823             center.updateBox(this.safeBox(centerBox));
46824         }
46825         this.el.repaint();
46826         this.fireEvent("layout", this);
46827     },
46828
46829     // private
46830     safeBox : function(box){
46831         box.width = Math.max(0, box.width);
46832         box.height = Math.max(0, box.height);
46833         return box;
46834     },
46835
46836     /**
46837      * Adds a ContentPanel (or subclass) to this layout.
46838      * @param {String} target The target region key (north, south, east, west or center).
46839      * @param {Roo.ContentPanel} panel The panel to add
46840      * @return {Roo.ContentPanel} The added panel
46841      */
46842     add : function(target, panel){
46843          
46844         target = target.toLowerCase();
46845         return this.regions[target].add(panel);
46846     },
46847
46848     /**
46849      * Remove a ContentPanel (or subclass) to this layout.
46850      * @param {String} target The target region key (north, south, east, west or center).
46851      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
46852      * @return {Roo.ContentPanel} The removed panel
46853      */
46854     remove : function(target, panel){
46855         target = target.toLowerCase();
46856         return this.regions[target].remove(panel);
46857     },
46858
46859     /**
46860      * Searches all regions for a panel with the specified id
46861      * @param {String} panelId
46862      * @return {Roo.ContentPanel} The panel or null if it wasn't found
46863      */
46864     findPanel : function(panelId){
46865         var rs = this.regions;
46866         for(var target in rs){
46867             if(typeof rs[target] != "function"){
46868                 var p = rs[target].getPanel(panelId);
46869                 if(p){
46870                     return p;
46871                 }
46872             }
46873         }
46874         return null;
46875     },
46876
46877     /**
46878      * Searches all regions for a panel with the specified id and activates (shows) it.
46879      * @param {String/ContentPanel} panelId The panels id or the panel itself
46880      * @return {Roo.ContentPanel} The shown panel or null
46881      */
46882     showPanel : function(panelId) {
46883       var rs = this.regions;
46884       for(var target in rs){
46885          var r = rs[target];
46886          if(typeof r != "function"){
46887             if(r.hasPanel(panelId)){
46888                return r.showPanel(panelId);
46889             }
46890          }
46891       }
46892       return null;
46893    },
46894
46895    /**
46896      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
46897      * @param {Roo.state.Provider} provider (optional) An alternate state provider
46898      */
46899     restoreState : function(provider){
46900         if(!provider){
46901             provider = Roo.state.Manager;
46902         }
46903         var sm = new Roo.LayoutStateManager();
46904         sm.init(this, provider);
46905     },
46906
46907     /**
46908      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
46909      * object should contain properties for each region to add ContentPanels to, and each property's value should be
46910      * a valid ContentPanel config object.  Example:
46911      * <pre><code>
46912 // Create the main layout
46913 var layout = new Roo.BorderLayout('main-ct', {
46914     west: {
46915         split:true,
46916         minSize: 175,
46917         titlebar: true
46918     },
46919     center: {
46920         title:'Components'
46921     }
46922 }, 'main-ct');
46923
46924 // Create and add multiple ContentPanels at once via configs
46925 layout.batchAdd({
46926    west: {
46927        id: 'source-files',
46928        autoCreate:true,
46929        title:'Ext Source Files',
46930        autoScroll:true,
46931        fitToFrame:true
46932    },
46933    center : {
46934        el: cview,
46935        autoScroll:true,
46936        fitToFrame:true,
46937        toolbar: tb,
46938        resizeEl:'cbody'
46939    }
46940 });
46941 </code></pre>
46942      * @param {Object} regions An object containing ContentPanel configs by region name
46943      */
46944     batchAdd : function(regions){
46945         this.beginUpdate();
46946         for(var rname in regions){
46947             var lr = this.regions[rname];
46948             if(lr){
46949                 this.addTypedPanels(lr, regions[rname]);
46950             }
46951         }
46952         this.endUpdate();
46953     },
46954
46955     // private
46956     addTypedPanels : function(lr, ps){
46957         if(typeof ps == 'string'){
46958             lr.add(new Roo.ContentPanel(ps));
46959         }
46960         else if(ps instanceof Array){
46961             for(var i =0, len = ps.length; i < len; i++){
46962                 this.addTypedPanels(lr, ps[i]);
46963             }
46964         }
46965         else if(!ps.events){ // raw config?
46966             var el = ps.el;
46967             delete ps.el; // prevent conflict
46968             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
46969         }
46970         else {  // panel object assumed!
46971             lr.add(ps);
46972         }
46973     },
46974     /**
46975      * Adds a xtype elements to the layout.
46976      * <pre><code>
46977
46978 layout.addxtype({
46979        xtype : 'ContentPanel',
46980        region: 'west',
46981        items: [ .... ]
46982    }
46983 );
46984
46985 layout.addxtype({
46986         xtype : 'NestedLayoutPanel',
46987         region: 'west',
46988         layout: {
46989            center: { },
46990            west: { }   
46991         },
46992         items : [ ... list of content panels or nested layout panels.. ]
46993    }
46994 );
46995 </code></pre>
46996      * @param {Object} cfg Xtype definition of item to add.
46997      */
46998     addxtype : function(cfg)
46999     {
47000         // basically accepts a pannel...
47001         // can accept a layout region..!?!?
47002         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
47003         
47004         if (!cfg.xtype.match(/Panel$/)) {
47005             return false;
47006         }
47007         var ret = false;
47008         
47009         if (typeof(cfg.region) == 'undefined') {
47010             Roo.log("Failed to add Panel, region was not set");
47011             Roo.log(cfg);
47012             return false;
47013         }
47014         var region = cfg.region;
47015         delete cfg.region;
47016         
47017           
47018         var xitems = [];
47019         if (cfg.items) {
47020             xitems = cfg.items;
47021             delete cfg.items;
47022         }
47023         var nb = false;
47024         
47025         switch(cfg.xtype) 
47026         {
47027             case 'ContentPanel':  // ContentPanel (el, cfg)
47028             case 'ScrollPanel':  // ContentPanel (el, cfg)
47029             case 'ViewPanel': 
47030                 if(cfg.autoCreate) {
47031                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
47032                 } else {
47033                     var el = this.el.createChild();
47034                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
47035                 }
47036                 
47037                 this.add(region, ret);
47038                 break;
47039             
47040             
47041             case 'TreePanel': // our new panel!
47042                 cfg.el = this.el.createChild();
47043                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
47044                 this.add(region, ret);
47045                 break;
47046             
47047             case 'NestedLayoutPanel': 
47048                 // create a new Layout (which is  a Border Layout...
47049                 var el = this.el.createChild();
47050                 var clayout = cfg.layout;
47051                 delete cfg.layout;
47052                 clayout.items   = clayout.items  || [];
47053                 // replace this exitems with the clayout ones..
47054                 xitems = clayout.items;
47055                  
47056                 
47057                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
47058                     cfg.background = false;
47059                 }
47060                 var layout = new Roo.BorderLayout(el, clayout);
47061                 
47062                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
47063                 //console.log('adding nested layout panel '  + cfg.toSource());
47064                 this.add(region, ret);
47065                 nb = {}; /// find first...
47066                 break;
47067                 
47068             case 'GridPanel': 
47069             
47070                 // needs grid and region
47071                 
47072                 //var el = this.getRegion(region).el.createChild();
47073                 var el = this.el.createChild();
47074                 // create the grid first...
47075                 
47076                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
47077                 delete cfg.grid;
47078                 if (region == 'center' && this.active ) {
47079                     cfg.background = false;
47080                 }
47081                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
47082                 
47083                 this.add(region, ret);
47084                 if (cfg.background) {
47085                     ret.on('activate', function(gp) {
47086                         if (!gp.grid.rendered) {
47087                             gp.grid.render();
47088                         }
47089                     });
47090                 } else {
47091                     grid.render();
47092                 }
47093                 break;
47094            
47095            
47096            
47097                 
47098                 
47099                 
47100             default: 
47101                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
47102                 return null;
47103              // GridPanel (grid, cfg)
47104             
47105         }
47106         this.beginUpdate();
47107         // add children..
47108         var region = '';
47109         var abn = {};
47110         Roo.each(xitems, function(i)  {
47111             region = nb && i.region ? i.region : false;
47112             
47113             var add = ret.addxtype(i);
47114            
47115             if (region) {
47116                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
47117                 if (!i.background) {
47118                     abn[region] = nb[region] ;
47119                 }
47120             }
47121             
47122         });
47123         this.endUpdate();
47124
47125         // make the last non-background panel active..
47126         //if (nb) { Roo.log(abn); }
47127         if (nb) {
47128             
47129             for(var r in abn) {
47130                 region = this.getRegion(r);
47131                 if (region) {
47132                     // tried using nb[r], but it does not work..
47133                      
47134                     region.showPanel(abn[r]);
47135                    
47136                 }
47137             }
47138         }
47139         return ret;
47140         
47141     }
47142 });
47143
47144 /**
47145  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
47146  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
47147  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
47148  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
47149  * <pre><code>
47150 // shorthand
47151 var CP = Roo.ContentPanel;
47152
47153 var layout = Roo.BorderLayout.create({
47154     north: {
47155         initialSize: 25,
47156         titlebar: false,
47157         panels: [new CP("north", "North")]
47158     },
47159     west: {
47160         split:true,
47161         initialSize: 200,
47162         minSize: 175,
47163         maxSize: 400,
47164         titlebar: true,
47165         collapsible: true,
47166         panels: [new CP("west", {title: "West"})]
47167     },
47168     east: {
47169         split:true,
47170         initialSize: 202,
47171         minSize: 175,
47172         maxSize: 400,
47173         titlebar: true,
47174         collapsible: true,
47175         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
47176     },
47177     south: {
47178         split:true,
47179         initialSize: 100,
47180         minSize: 100,
47181         maxSize: 200,
47182         titlebar: true,
47183         collapsible: true,
47184         panels: [new CP("south", {title: "South", closable: true})]
47185     },
47186     center: {
47187         titlebar: true,
47188         autoScroll:true,
47189         resizeTabs: true,
47190         minTabWidth: 50,
47191         preferredTabWidth: 150,
47192         panels: [
47193             new CP("center1", {title: "Close Me", closable: true}),
47194             new CP("center2", {title: "Center Panel", closable: false})
47195         ]
47196     }
47197 }, document.body);
47198
47199 layout.getRegion("center").showPanel("center1");
47200 </code></pre>
47201  * @param config
47202  * @param targetEl
47203  */
47204 Roo.BorderLayout.create = function(config, targetEl){
47205     var layout = new Roo.BorderLayout(targetEl || document.body, config);
47206     layout.beginUpdate();
47207     var regions = Roo.BorderLayout.RegionFactory.validRegions;
47208     for(var j = 0, jlen = regions.length; j < jlen; j++){
47209         var lr = regions[j];
47210         if(layout.regions[lr] && config[lr].panels){
47211             var r = layout.regions[lr];
47212             var ps = config[lr].panels;
47213             layout.addTypedPanels(r, ps);
47214         }
47215     }
47216     layout.endUpdate();
47217     return layout;
47218 };
47219
47220 // private
47221 Roo.BorderLayout.RegionFactory = {
47222     // private
47223     validRegions : ["north","south","east","west","center"],
47224
47225     // private
47226     create : function(target, mgr, config){
47227         target = target.toLowerCase();
47228         if(config.lightweight || config.basic){
47229             return new Roo.BasicLayoutRegion(mgr, config, target);
47230         }
47231         switch(target){
47232             case "north":
47233                 return new Roo.NorthLayoutRegion(mgr, config);
47234             case "south":
47235                 return new Roo.SouthLayoutRegion(mgr, config);
47236             case "east":
47237                 return new Roo.EastLayoutRegion(mgr, config);
47238             case "west":
47239                 return new Roo.WestLayoutRegion(mgr, config);
47240             case "center":
47241                 return new Roo.CenterLayoutRegion(mgr, config);
47242         }
47243         throw 'Layout region "'+target+'" not supported.';
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 /**
47257  * @class Roo.BasicLayoutRegion
47258  * @extends Roo.util.Observable
47259  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
47260  * and does not have a titlebar, tabs or any other features. All it does is size and position 
47261  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
47262  */
47263 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
47264     this.mgr = mgr;
47265     this.position  = pos;
47266     this.events = {
47267         /**
47268          * @scope Roo.BasicLayoutRegion
47269          */
47270         
47271         /**
47272          * @event beforeremove
47273          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
47274          * @param {Roo.LayoutRegion} this
47275          * @param {Roo.ContentPanel} panel The panel
47276          * @param {Object} e The cancel event object
47277          */
47278         "beforeremove" : true,
47279         /**
47280          * @event invalidated
47281          * Fires when the layout for this region is changed.
47282          * @param {Roo.LayoutRegion} this
47283          */
47284         "invalidated" : true,
47285         /**
47286          * @event visibilitychange
47287          * Fires when this region is shown or hidden 
47288          * @param {Roo.LayoutRegion} this
47289          * @param {Boolean} visibility true or false
47290          */
47291         "visibilitychange" : true,
47292         /**
47293          * @event paneladded
47294          * Fires when a panel is added. 
47295          * @param {Roo.LayoutRegion} this
47296          * @param {Roo.ContentPanel} panel The panel
47297          */
47298         "paneladded" : true,
47299         /**
47300          * @event panelremoved
47301          * Fires when a panel is removed. 
47302          * @param {Roo.LayoutRegion} this
47303          * @param {Roo.ContentPanel} panel The panel
47304          */
47305         "panelremoved" : true,
47306         /**
47307          * @event collapsed
47308          * Fires when this region is collapsed.
47309          * @param {Roo.LayoutRegion} this
47310          */
47311         "collapsed" : true,
47312         /**
47313          * @event expanded
47314          * Fires when this region is expanded.
47315          * @param {Roo.LayoutRegion} this
47316          */
47317         "expanded" : true,
47318         /**
47319          * @event slideshow
47320          * Fires when this region is slid into view.
47321          * @param {Roo.LayoutRegion} this
47322          */
47323         "slideshow" : true,
47324         /**
47325          * @event slidehide
47326          * Fires when this region slides out of view. 
47327          * @param {Roo.LayoutRegion} this
47328          */
47329         "slidehide" : true,
47330         /**
47331          * @event panelactivated
47332          * Fires when a panel is activated. 
47333          * @param {Roo.LayoutRegion} this
47334          * @param {Roo.ContentPanel} panel The activated panel
47335          */
47336         "panelactivated" : true,
47337         /**
47338          * @event resized
47339          * Fires when the user resizes this region. 
47340          * @param {Roo.LayoutRegion} this
47341          * @param {Number} newSize The new size (width for east/west, height for north/south)
47342          */
47343         "resized" : true
47344     };
47345     /** A collection of panels in this region. @type Roo.util.MixedCollection */
47346     this.panels = new Roo.util.MixedCollection();
47347     this.panels.getKey = this.getPanelId.createDelegate(this);
47348     this.box = null;
47349     this.activePanel = null;
47350     // ensure listeners are added...
47351     
47352     if (config.listeners || config.events) {
47353         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
47354             listeners : config.listeners || {},
47355             events : config.events || {}
47356         });
47357     }
47358     
47359     if(skipConfig !== true){
47360         this.applyConfig(config);
47361     }
47362 };
47363
47364 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
47365     getPanelId : function(p){
47366         return p.getId();
47367     },
47368     
47369     applyConfig : function(config){
47370         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
47371         this.config = config;
47372         
47373     },
47374     
47375     /**
47376      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
47377      * the width, for horizontal (north, south) the height.
47378      * @param {Number} newSize The new width or height
47379      */
47380     resizeTo : function(newSize){
47381         var el = this.el ? this.el :
47382                  (this.activePanel ? this.activePanel.getEl() : null);
47383         if(el){
47384             switch(this.position){
47385                 case "east":
47386                 case "west":
47387                     el.setWidth(newSize);
47388                     this.fireEvent("resized", this, newSize);
47389                 break;
47390                 case "north":
47391                 case "south":
47392                     el.setHeight(newSize);
47393                     this.fireEvent("resized", this, newSize);
47394                 break;                
47395             }
47396         }
47397     },
47398     
47399     getBox : function(){
47400         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
47401     },
47402     
47403     getMargins : function(){
47404         return this.margins;
47405     },
47406     
47407     updateBox : function(box){
47408         this.box = box;
47409         var el = this.activePanel.getEl();
47410         el.dom.style.left = box.x + "px";
47411         el.dom.style.top = box.y + "px";
47412         this.activePanel.setSize(box.width, box.height);
47413     },
47414     
47415     /**
47416      * Returns the container element for this region.
47417      * @return {Roo.Element}
47418      */
47419     getEl : function(){
47420         return this.activePanel;
47421     },
47422     
47423     /**
47424      * Returns true if this region is currently visible.
47425      * @return {Boolean}
47426      */
47427     isVisible : function(){
47428         return this.activePanel ? true : false;
47429     },
47430     
47431     setActivePanel : function(panel){
47432         panel = this.getPanel(panel);
47433         if(this.activePanel && this.activePanel != panel){
47434             this.activePanel.setActiveState(false);
47435             this.activePanel.getEl().setLeftTop(-10000,-10000);
47436         }
47437         this.activePanel = panel;
47438         panel.setActiveState(true);
47439         if(this.box){
47440             panel.setSize(this.box.width, this.box.height);
47441         }
47442         this.fireEvent("panelactivated", this, panel);
47443         this.fireEvent("invalidated");
47444     },
47445     
47446     /**
47447      * Show the specified panel.
47448      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
47449      * @return {Roo.ContentPanel} The shown panel or null
47450      */
47451     showPanel : function(panel){
47452         if(panel = this.getPanel(panel)){
47453             this.setActivePanel(panel);
47454         }
47455         return panel;
47456     },
47457     
47458     /**
47459      * Get the active panel for this region.
47460      * @return {Roo.ContentPanel} The active panel or null
47461      */
47462     getActivePanel : function(){
47463         return this.activePanel;
47464     },
47465     
47466     /**
47467      * Add the passed ContentPanel(s)
47468      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
47469      * @return {Roo.ContentPanel} The panel added (if only one was added)
47470      */
47471     add : function(panel){
47472         if(arguments.length > 1){
47473             for(var i = 0, len = arguments.length; i < len; i++) {
47474                 this.add(arguments[i]);
47475             }
47476             return null;
47477         }
47478         if(this.hasPanel(panel)){
47479             this.showPanel(panel);
47480             return panel;
47481         }
47482         var el = panel.getEl();
47483         if(el.dom.parentNode != this.mgr.el.dom){
47484             this.mgr.el.dom.appendChild(el.dom);
47485         }
47486         if(panel.setRegion){
47487             panel.setRegion(this);
47488         }
47489         this.panels.add(panel);
47490         el.setStyle("position", "absolute");
47491         if(!panel.background){
47492             this.setActivePanel(panel);
47493             if(this.config.initialSize && this.panels.getCount()==1){
47494                 this.resizeTo(this.config.initialSize);
47495             }
47496         }
47497         this.fireEvent("paneladded", this, panel);
47498         return panel;
47499     },
47500     
47501     /**
47502      * Returns true if the panel is in this region.
47503      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
47504      * @return {Boolean}
47505      */
47506     hasPanel : function(panel){
47507         if(typeof panel == "object"){ // must be panel obj
47508             panel = panel.getId();
47509         }
47510         return this.getPanel(panel) ? true : false;
47511     },
47512     
47513     /**
47514      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
47515      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
47516      * @param {Boolean} preservePanel Overrides the config preservePanel option
47517      * @return {Roo.ContentPanel} The panel that was removed
47518      */
47519     remove : function(panel, preservePanel){
47520         panel = this.getPanel(panel);
47521         if(!panel){
47522             return null;
47523         }
47524         var e = {};
47525         this.fireEvent("beforeremove", this, panel, e);
47526         if(e.cancel === true){
47527             return null;
47528         }
47529         var panelId = panel.getId();
47530         this.panels.removeKey(panelId);
47531         return panel;
47532     },
47533     
47534     /**
47535      * Returns the panel specified or null if it's not in this region.
47536      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
47537      * @return {Roo.ContentPanel}
47538      */
47539     getPanel : function(id){
47540         if(typeof id == "object"){ // must be panel obj
47541             return id;
47542         }
47543         return this.panels.get(id);
47544     },
47545     
47546     /**
47547      * Returns this regions position (north/south/east/west/center).
47548      * @return {String} 
47549      */
47550     getPosition: function(){
47551         return this.position;    
47552     }
47553 });/*
47554  * Based on:
47555  * Ext JS Library 1.1.1
47556  * Copyright(c) 2006-2007, Ext JS, LLC.
47557  *
47558  * Originally Released Under LGPL - original licence link has changed is not relivant.
47559  *
47560  * Fork - LGPL
47561  * <script type="text/javascript">
47562  */
47563  
47564 /**
47565  * @class Roo.LayoutRegion
47566  * @extends Roo.BasicLayoutRegion
47567  * This class represents a region in a layout manager.
47568  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
47569  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
47570  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
47571  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
47572  * @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})
47573  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
47574  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
47575  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
47576  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
47577  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
47578  * @cfg {String}    title           The title for the region (overrides panel titles)
47579  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
47580  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
47581  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
47582  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
47583  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
47584  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
47585  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
47586  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
47587  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
47588  * @cfg {Boolean}   showPin         True to show a pin button
47589  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
47590  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
47591  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
47592  * @cfg {Number}    width           For East/West panels
47593  * @cfg {Number}    height          For North/South panels
47594  * @cfg {Boolean}   split           To show the splitter
47595  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
47596  */
47597 Roo.LayoutRegion = function(mgr, config, pos){
47598     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
47599     var dh = Roo.DomHelper;
47600     /** This region's container element 
47601     * @type Roo.Element */
47602     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
47603     /** This region's title element 
47604     * @type Roo.Element */
47605
47606     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
47607         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
47608         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
47609     ]}, true);
47610     this.titleEl.enableDisplayMode();
47611     /** This region's title text element 
47612     * @type HTMLElement */
47613     this.titleTextEl = this.titleEl.dom.firstChild;
47614     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
47615     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
47616     this.closeBtn.enableDisplayMode();
47617     this.closeBtn.on("click", this.closeClicked, this);
47618     this.closeBtn.hide();
47619
47620     this.createBody(config);
47621     this.visible = true;
47622     this.collapsed = false;
47623
47624     if(config.hideWhenEmpty){
47625         this.hide();
47626         this.on("paneladded", this.validateVisibility, this);
47627         this.on("panelremoved", this.validateVisibility, this);
47628     }
47629     this.applyConfig(config);
47630 };
47631
47632 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
47633
47634     createBody : function(){
47635         /** This region's body element 
47636         * @type Roo.Element */
47637         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
47638     },
47639
47640     applyConfig : function(c){
47641         if(c.collapsible && this.position != "center" && !this.collapsedEl){
47642             var dh = Roo.DomHelper;
47643             if(c.titlebar !== false){
47644                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
47645                 this.collapseBtn.on("click", this.collapse, this);
47646                 this.collapseBtn.enableDisplayMode();
47647
47648                 if(c.showPin === true || this.showPin){
47649                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
47650                     this.stickBtn.enableDisplayMode();
47651                     this.stickBtn.on("click", this.expand, this);
47652                     this.stickBtn.hide();
47653                 }
47654             }
47655             /** This region's collapsed element
47656             * @type Roo.Element */
47657             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
47658                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
47659             ]}, true);
47660             if(c.floatable !== false){
47661                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
47662                this.collapsedEl.on("click", this.collapseClick, this);
47663             }
47664
47665             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
47666                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
47667                    id: "message", unselectable: "on", style:{"float":"left"}});
47668                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
47669              }
47670             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
47671             this.expandBtn.on("click", this.expand, this);
47672         }
47673         if(this.collapseBtn){
47674             this.collapseBtn.setVisible(c.collapsible == true);
47675         }
47676         this.cmargins = c.cmargins || this.cmargins ||
47677                          (this.position == "west" || this.position == "east" ?
47678                              {top: 0, left: 2, right:2, bottom: 0} :
47679                              {top: 2, left: 0, right:0, bottom: 2});
47680         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
47681         this.bottomTabs = c.tabPosition != "top";
47682         this.autoScroll = c.autoScroll || false;
47683         if(this.autoScroll){
47684             this.bodyEl.setStyle("overflow", "auto");
47685         }else{
47686             this.bodyEl.setStyle("overflow", "hidden");
47687         }
47688         //if(c.titlebar !== false){
47689             if((!c.titlebar && !c.title) || c.titlebar === false){
47690                 this.titleEl.hide();
47691             }else{
47692                 this.titleEl.show();
47693                 if(c.title){
47694                     this.titleTextEl.innerHTML = c.title;
47695                 }
47696             }
47697         //}
47698         this.duration = c.duration || .30;
47699         this.slideDuration = c.slideDuration || .45;
47700         this.config = c;
47701         if(c.collapsed){
47702             this.collapse(true);
47703         }
47704         if(c.hidden){
47705             this.hide();
47706         }
47707     },
47708     /**
47709      * Returns true if this region is currently visible.
47710      * @return {Boolean}
47711      */
47712     isVisible : function(){
47713         return this.visible;
47714     },
47715
47716     /**
47717      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
47718      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
47719      */
47720     setCollapsedTitle : function(title){
47721         title = title || "&#160;";
47722         if(this.collapsedTitleTextEl){
47723             this.collapsedTitleTextEl.innerHTML = title;
47724         }
47725     },
47726
47727     getBox : function(){
47728         var b;
47729         if(!this.collapsed){
47730             b = this.el.getBox(false, true);
47731         }else{
47732             b = this.collapsedEl.getBox(false, true);
47733         }
47734         return b;
47735     },
47736
47737     getMargins : function(){
47738         return this.collapsed ? this.cmargins : this.margins;
47739     },
47740
47741     highlight : function(){
47742         this.el.addClass("x-layout-panel-dragover");
47743     },
47744
47745     unhighlight : function(){
47746         this.el.removeClass("x-layout-panel-dragover");
47747     },
47748
47749     updateBox : function(box){
47750         this.box = box;
47751         if(!this.collapsed){
47752             this.el.dom.style.left = box.x + "px";
47753             this.el.dom.style.top = box.y + "px";
47754             this.updateBody(box.width, box.height);
47755         }else{
47756             this.collapsedEl.dom.style.left = box.x + "px";
47757             this.collapsedEl.dom.style.top = box.y + "px";
47758             this.collapsedEl.setSize(box.width, box.height);
47759         }
47760         if(this.tabs){
47761             this.tabs.autoSizeTabs();
47762         }
47763     },
47764
47765     updateBody : function(w, h){
47766         if(w !== null){
47767             this.el.setWidth(w);
47768             w -= this.el.getBorderWidth("rl");
47769             if(this.config.adjustments){
47770                 w += this.config.adjustments[0];
47771             }
47772         }
47773         if(h !== null){
47774             this.el.setHeight(h);
47775             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
47776             h -= this.el.getBorderWidth("tb");
47777             if(this.config.adjustments){
47778                 h += this.config.adjustments[1];
47779             }
47780             this.bodyEl.setHeight(h);
47781             if(this.tabs){
47782                 h = this.tabs.syncHeight(h);
47783             }
47784         }
47785         if(this.panelSize){
47786             w = w !== null ? w : this.panelSize.width;
47787             h = h !== null ? h : this.panelSize.height;
47788         }
47789         if(this.activePanel){
47790             var el = this.activePanel.getEl();
47791             w = w !== null ? w : el.getWidth();
47792             h = h !== null ? h : el.getHeight();
47793             this.panelSize = {width: w, height: h};
47794             this.activePanel.setSize(w, h);
47795         }
47796         if(Roo.isIE && this.tabs){
47797             this.tabs.el.repaint();
47798         }
47799     },
47800
47801     /**
47802      * Returns the container element for this region.
47803      * @return {Roo.Element}
47804      */
47805     getEl : function(){
47806         return this.el;
47807     },
47808
47809     /**
47810      * Hides this region.
47811      */
47812     hide : function(){
47813         if(!this.collapsed){
47814             this.el.dom.style.left = "-2000px";
47815             this.el.hide();
47816         }else{
47817             this.collapsedEl.dom.style.left = "-2000px";
47818             this.collapsedEl.hide();
47819         }
47820         this.visible = false;
47821         this.fireEvent("visibilitychange", this, false);
47822     },
47823
47824     /**
47825      * Shows this region if it was previously hidden.
47826      */
47827     show : function(){
47828         if(!this.collapsed){
47829             this.el.show();
47830         }else{
47831             this.collapsedEl.show();
47832         }
47833         this.visible = true;
47834         this.fireEvent("visibilitychange", this, true);
47835     },
47836
47837     closeClicked : function(){
47838         if(this.activePanel){
47839             this.remove(this.activePanel);
47840         }
47841     },
47842
47843     collapseClick : function(e){
47844         if(this.isSlid){
47845            e.stopPropagation();
47846            this.slideIn();
47847         }else{
47848            e.stopPropagation();
47849            this.slideOut();
47850         }
47851     },
47852
47853     /**
47854      * Collapses this region.
47855      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
47856      */
47857     collapse : function(skipAnim){
47858         if(this.collapsed) return;
47859         this.collapsed = true;
47860         if(this.split){
47861             this.split.el.hide();
47862         }
47863         if(this.config.animate && skipAnim !== true){
47864             this.fireEvent("invalidated", this);
47865             this.animateCollapse();
47866         }else{
47867             this.el.setLocation(-20000,-20000);
47868             this.el.hide();
47869             this.collapsedEl.show();
47870             this.fireEvent("collapsed", this);
47871             this.fireEvent("invalidated", this);
47872         }
47873     },
47874
47875     animateCollapse : function(){
47876         // overridden
47877     },
47878
47879     /**
47880      * Expands this region if it was previously collapsed.
47881      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
47882      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
47883      */
47884     expand : function(e, skipAnim){
47885         if(e) e.stopPropagation();
47886         if(!this.collapsed || this.el.hasActiveFx()) return;
47887         if(this.isSlid){
47888             this.afterSlideIn();
47889             skipAnim = true;
47890         }
47891         this.collapsed = false;
47892         if(this.config.animate && skipAnim !== true){
47893             this.animateExpand();
47894         }else{
47895             this.el.show();
47896             if(this.split){
47897                 this.split.el.show();
47898             }
47899             this.collapsedEl.setLocation(-2000,-2000);
47900             this.collapsedEl.hide();
47901             this.fireEvent("invalidated", this);
47902             this.fireEvent("expanded", this);
47903         }
47904     },
47905
47906     animateExpand : function(){
47907         // overridden
47908     },
47909
47910     initTabs : function()
47911     {
47912         this.bodyEl.setStyle("overflow", "hidden");
47913         var ts = new Roo.TabPanel(
47914                 this.bodyEl.dom,
47915                 {
47916                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
47917                     disableTooltips: this.config.disableTabTips,
47918                     toolbar : this.config.toolbar
47919                 }
47920         );
47921         if(this.config.hideTabs){
47922             ts.stripWrap.setDisplayed(false);
47923         }
47924         this.tabs = ts;
47925         ts.resizeTabs = this.config.resizeTabs === true;
47926         ts.minTabWidth = this.config.minTabWidth || 40;
47927         ts.maxTabWidth = this.config.maxTabWidth || 250;
47928         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
47929         ts.monitorResize = false;
47930         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
47931         ts.bodyEl.addClass('x-layout-tabs-body');
47932         this.panels.each(this.initPanelAsTab, this);
47933     },
47934
47935     initPanelAsTab : function(panel){
47936         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
47937                     this.config.closeOnTab && panel.isClosable());
47938         if(panel.tabTip !== undefined){
47939             ti.setTooltip(panel.tabTip);
47940         }
47941         ti.on("activate", function(){
47942               this.setActivePanel(panel);
47943         }, this);
47944         if(this.config.closeOnTab){
47945             ti.on("beforeclose", function(t, e){
47946                 e.cancel = true;
47947                 this.remove(panel);
47948             }, this);
47949         }
47950         return ti;
47951     },
47952
47953     updatePanelTitle : function(panel, title){
47954         if(this.activePanel == panel){
47955             this.updateTitle(title);
47956         }
47957         if(this.tabs){
47958             var ti = this.tabs.getTab(panel.getEl().id);
47959             ti.setText(title);
47960             if(panel.tabTip !== undefined){
47961                 ti.setTooltip(panel.tabTip);
47962             }
47963         }
47964     },
47965
47966     updateTitle : function(title){
47967         if(this.titleTextEl && !this.config.title){
47968             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
47969         }
47970     },
47971
47972     setActivePanel : function(panel){
47973         panel = this.getPanel(panel);
47974         if(this.activePanel && this.activePanel != panel){
47975             this.activePanel.setActiveState(false);
47976         }
47977         this.activePanel = panel;
47978         panel.setActiveState(true);
47979         if(this.panelSize){
47980             panel.setSize(this.panelSize.width, this.panelSize.height);
47981         }
47982         if(this.closeBtn){
47983             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
47984         }
47985         this.updateTitle(panel.getTitle());
47986         if(this.tabs){
47987             this.fireEvent("invalidated", this);
47988         }
47989         this.fireEvent("panelactivated", this, panel);
47990     },
47991
47992     /**
47993      * Shows the specified panel.
47994      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
47995      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
47996      */
47997     showPanel : function(panel){
47998         if(panel = this.getPanel(panel)){
47999             if(this.tabs){
48000                 var tab = this.tabs.getTab(panel.getEl().id);
48001                 if(tab.isHidden()){
48002                     this.tabs.unhideTab(tab.id);
48003                 }
48004                 tab.activate();
48005             }else{
48006                 this.setActivePanel(panel);
48007             }
48008         }
48009         return panel;
48010     },
48011
48012     /**
48013      * Get the active panel for this region.
48014      * @return {Roo.ContentPanel} The active panel or null
48015      */
48016     getActivePanel : function(){
48017         return this.activePanel;
48018     },
48019
48020     validateVisibility : function(){
48021         if(this.panels.getCount() < 1){
48022             this.updateTitle("&#160;");
48023             this.closeBtn.hide();
48024             this.hide();
48025         }else{
48026             if(!this.isVisible()){
48027                 this.show();
48028             }
48029         }
48030     },
48031
48032     /**
48033      * Adds the passed ContentPanel(s) to this region.
48034      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
48035      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
48036      */
48037     add : function(panel){
48038         if(arguments.length > 1){
48039             for(var i = 0, len = arguments.length; i < len; i++) {
48040                 this.add(arguments[i]);
48041             }
48042             return null;
48043         }
48044         if(this.hasPanel(panel)){
48045             this.showPanel(panel);
48046             return panel;
48047         }
48048         panel.setRegion(this);
48049         this.panels.add(panel);
48050         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
48051             this.bodyEl.dom.appendChild(panel.getEl().dom);
48052             if(panel.background !== true){
48053                 this.setActivePanel(panel);
48054             }
48055             this.fireEvent("paneladded", this, panel);
48056             return panel;
48057         }
48058         if(!this.tabs){
48059             this.initTabs();
48060         }else{
48061             this.initPanelAsTab(panel);
48062         }
48063         if(panel.background !== true){
48064             this.tabs.activate(panel.getEl().id);
48065         }
48066         this.fireEvent("paneladded", this, panel);
48067         return panel;
48068     },
48069
48070     /**
48071      * Hides the tab for the specified panel.
48072      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
48073      */
48074     hidePanel : function(panel){
48075         if(this.tabs && (panel = this.getPanel(panel))){
48076             this.tabs.hideTab(panel.getEl().id);
48077         }
48078     },
48079
48080     /**
48081      * Unhides the tab for a previously hidden panel.
48082      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
48083      */
48084     unhidePanel : function(panel){
48085         if(this.tabs && (panel = this.getPanel(panel))){
48086             this.tabs.unhideTab(panel.getEl().id);
48087         }
48088     },
48089
48090     clearPanels : function(){
48091         while(this.panels.getCount() > 0){
48092              this.remove(this.panels.first());
48093         }
48094     },
48095
48096     /**
48097      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
48098      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
48099      * @param {Boolean} preservePanel Overrides the config preservePanel option
48100      * @return {Roo.ContentPanel} The panel that was removed
48101      */
48102     remove : function(panel, preservePanel){
48103         panel = this.getPanel(panel);
48104         if(!panel){
48105             return null;
48106         }
48107         var e = {};
48108         this.fireEvent("beforeremove", this, panel, e);
48109         if(e.cancel === true){
48110             return null;
48111         }
48112         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
48113         var panelId = panel.getId();
48114         this.panels.removeKey(panelId);
48115         if(preservePanel){
48116             document.body.appendChild(panel.getEl().dom);
48117         }
48118         if(this.tabs){
48119             this.tabs.removeTab(panel.getEl().id);
48120         }else if (!preservePanel){
48121             this.bodyEl.dom.removeChild(panel.getEl().dom);
48122         }
48123         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
48124             var p = this.panels.first();
48125             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
48126             tempEl.appendChild(p.getEl().dom);
48127             this.bodyEl.update("");
48128             this.bodyEl.dom.appendChild(p.getEl().dom);
48129             tempEl = null;
48130             this.updateTitle(p.getTitle());
48131             this.tabs = null;
48132             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
48133             this.setActivePanel(p);
48134         }
48135         panel.setRegion(null);
48136         if(this.activePanel == panel){
48137             this.activePanel = null;
48138         }
48139         if(this.config.autoDestroy !== false && preservePanel !== true){
48140             try{panel.destroy();}catch(e){}
48141         }
48142         this.fireEvent("panelremoved", this, panel);
48143         return panel;
48144     },
48145
48146     /**
48147      * Returns the TabPanel component used by this region
48148      * @return {Roo.TabPanel}
48149      */
48150     getTabs : function(){
48151         return this.tabs;
48152     },
48153
48154     createTool : function(parentEl, className){
48155         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
48156             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
48157         btn.addClassOnOver("x-layout-tools-button-over");
48158         return btn;
48159     }
48160 });/*
48161  * Based on:
48162  * Ext JS Library 1.1.1
48163  * Copyright(c) 2006-2007, Ext JS, LLC.
48164  *
48165  * Originally Released Under LGPL - original licence link has changed is not relivant.
48166  *
48167  * Fork - LGPL
48168  * <script type="text/javascript">
48169  */
48170  
48171
48172
48173 /**
48174  * @class Roo.SplitLayoutRegion
48175  * @extends Roo.LayoutRegion
48176  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
48177  */
48178 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
48179     this.cursor = cursor;
48180     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
48181 };
48182
48183 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
48184     splitTip : "Drag to resize.",
48185     collapsibleSplitTip : "Drag to resize. Double click to hide.",
48186     useSplitTips : false,
48187
48188     applyConfig : function(config){
48189         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
48190         if(config.split){
48191             if(!this.split){
48192                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
48193                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
48194                 /** The SplitBar for this region 
48195                 * @type Roo.SplitBar */
48196                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
48197                 this.split.on("moved", this.onSplitMove, this);
48198                 this.split.useShim = config.useShim === true;
48199                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
48200                 if(this.useSplitTips){
48201                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
48202                 }
48203                 if(config.collapsible){
48204                     this.split.el.on("dblclick", this.collapse,  this);
48205                 }
48206             }
48207             if(typeof config.minSize != "undefined"){
48208                 this.split.minSize = config.minSize;
48209             }
48210             if(typeof config.maxSize != "undefined"){
48211                 this.split.maxSize = config.maxSize;
48212             }
48213             if(config.hideWhenEmpty || config.hidden || config.collapsed){
48214                 this.hideSplitter();
48215             }
48216         }
48217     },
48218
48219     getHMaxSize : function(){
48220          var cmax = this.config.maxSize || 10000;
48221          var center = this.mgr.getRegion("center");
48222          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
48223     },
48224
48225     getVMaxSize : function(){
48226          var cmax = this.config.maxSize || 10000;
48227          var center = this.mgr.getRegion("center");
48228          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
48229     },
48230
48231     onSplitMove : function(split, newSize){
48232         this.fireEvent("resized", this, newSize);
48233     },
48234     
48235     /** 
48236      * Returns the {@link Roo.SplitBar} for this region.
48237      * @return {Roo.SplitBar}
48238      */
48239     getSplitBar : function(){
48240         return this.split;
48241     },
48242     
48243     hide : function(){
48244         this.hideSplitter();
48245         Roo.SplitLayoutRegion.superclass.hide.call(this);
48246     },
48247
48248     hideSplitter : function(){
48249         if(this.split){
48250             this.split.el.setLocation(-2000,-2000);
48251             this.split.el.hide();
48252         }
48253     },
48254
48255     show : function(){
48256         if(this.split){
48257             this.split.el.show();
48258         }
48259         Roo.SplitLayoutRegion.superclass.show.call(this);
48260     },
48261     
48262     beforeSlide: function(){
48263         if(Roo.isGecko){// firefox overflow auto bug workaround
48264             this.bodyEl.clip();
48265             if(this.tabs) this.tabs.bodyEl.clip();
48266             if(this.activePanel){
48267                 this.activePanel.getEl().clip();
48268                 
48269                 if(this.activePanel.beforeSlide){
48270                     this.activePanel.beforeSlide();
48271                 }
48272             }
48273         }
48274     },
48275     
48276     afterSlide : function(){
48277         if(Roo.isGecko){// firefox overflow auto bug workaround
48278             this.bodyEl.unclip();
48279             if(this.tabs) this.tabs.bodyEl.unclip();
48280             if(this.activePanel){
48281                 this.activePanel.getEl().unclip();
48282                 if(this.activePanel.afterSlide){
48283                     this.activePanel.afterSlide();
48284                 }
48285             }
48286         }
48287     },
48288
48289     initAutoHide : function(){
48290         if(this.autoHide !== false){
48291             if(!this.autoHideHd){
48292                 var st = new Roo.util.DelayedTask(this.slideIn, this);
48293                 this.autoHideHd = {
48294                     "mouseout": function(e){
48295                         if(!e.within(this.el, true)){
48296                             st.delay(500);
48297                         }
48298                     },
48299                     "mouseover" : function(e){
48300                         st.cancel();
48301                     },
48302                     scope : this
48303                 };
48304             }
48305             this.el.on(this.autoHideHd);
48306         }
48307     },
48308
48309     clearAutoHide : function(){
48310         if(this.autoHide !== false){
48311             this.el.un("mouseout", this.autoHideHd.mouseout);
48312             this.el.un("mouseover", this.autoHideHd.mouseover);
48313         }
48314     },
48315
48316     clearMonitor : function(){
48317         Roo.get(document).un("click", this.slideInIf, this);
48318     },
48319
48320     // these names are backwards but not changed for compat
48321     slideOut : function(){
48322         if(this.isSlid || this.el.hasActiveFx()){
48323             return;
48324         }
48325         this.isSlid = true;
48326         if(this.collapseBtn){
48327             this.collapseBtn.hide();
48328         }
48329         this.closeBtnState = this.closeBtn.getStyle('display');
48330         this.closeBtn.hide();
48331         if(this.stickBtn){
48332             this.stickBtn.show();
48333         }
48334         this.el.show();
48335         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
48336         this.beforeSlide();
48337         this.el.setStyle("z-index", 10001);
48338         this.el.slideIn(this.getSlideAnchor(), {
48339             callback: function(){
48340                 this.afterSlide();
48341                 this.initAutoHide();
48342                 Roo.get(document).on("click", this.slideInIf, this);
48343                 this.fireEvent("slideshow", this);
48344             },
48345             scope: this,
48346             block: true
48347         });
48348     },
48349
48350     afterSlideIn : function(){
48351         this.clearAutoHide();
48352         this.isSlid = false;
48353         this.clearMonitor();
48354         this.el.setStyle("z-index", "");
48355         if(this.collapseBtn){
48356             this.collapseBtn.show();
48357         }
48358         this.closeBtn.setStyle('display', this.closeBtnState);
48359         if(this.stickBtn){
48360             this.stickBtn.hide();
48361         }
48362         this.fireEvent("slidehide", this);
48363     },
48364
48365     slideIn : function(cb){
48366         if(!this.isSlid || this.el.hasActiveFx()){
48367             Roo.callback(cb);
48368             return;
48369         }
48370         this.isSlid = false;
48371         this.beforeSlide();
48372         this.el.slideOut(this.getSlideAnchor(), {
48373             callback: function(){
48374                 this.el.setLeftTop(-10000, -10000);
48375                 this.afterSlide();
48376                 this.afterSlideIn();
48377                 Roo.callback(cb);
48378             },
48379             scope: this,
48380             block: true
48381         });
48382     },
48383     
48384     slideInIf : function(e){
48385         if(!e.within(this.el)){
48386             this.slideIn();
48387         }
48388     },
48389
48390     animateCollapse : function(){
48391         this.beforeSlide();
48392         this.el.setStyle("z-index", 20000);
48393         var anchor = this.getSlideAnchor();
48394         this.el.slideOut(anchor, {
48395             callback : function(){
48396                 this.el.setStyle("z-index", "");
48397                 this.collapsedEl.slideIn(anchor, {duration:.3});
48398                 this.afterSlide();
48399                 this.el.setLocation(-10000,-10000);
48400                 this.el.hide();
48401                 this.fireEvent("collapsed", this);
48402             },
48403             scope: this,
48404             block: true
48405         });
48406     },
48407
48408     animateExpand : function(){
48409         this.beforeSlide();
48410         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
48411         this.el.setStyle("z-index", 20000);
48412         this.collapsedEl.hide({
48413             duration:.1
48414         });
48415         this.el.slideIn(this.getSlideAnchor(), {
48416             callback : function(){
48417                 this.el.setStyle("z-index", "");
48418                 this.afterSlide();
48419                 if(this.split){
48420                     this.split.el.show();
48421                 }
48422                 this.fireEvent("invalidated", this);
48423                 this.fireEvent("expanded", this);
48424             },
48425             scope: this,
48426             block: true
48427         });
48428     },
48429
48430     anchors : {
48431         "west" : "left",
48432         "east" : "right",
48433         "north" : "top",
48434         "south" : "bottom"
48435     },
48436
48437     sanchors : {
48438         "west" : "l",
48439         "east" : "r",
48440         "north" : "t",
48441         "south" : "b"
48442     },
48443
48444     canchors : {
48445         "west" : "tl-tr",
48446         "east" : "tr-tl",
48447         "north" : "tl-bl",
48448         "south" : "bl-tl"
48449     },
48450
48451     getAnchor : function(){
48452         return this.anchors[this.position];
48453     },
48454
48455     getCollapseAnchor : function(){
48456         return this.canchors[this.position];
48457     },
48458
48459     getSlideAnchor : function(){
48460         return this.sanchors[this.position];
48461     },
48462
48463     getAlignAdj : function(){
48464         var cm = this.cmargins;
48465         switch(this.position){
48466             case "west":
48467                 return [0, 0];
48468             break;
48469             case "east":
48470                 return [0, 0];
48471             break;
48472             case "north":
48473                 return [0, 0];
48474             break;
48475             case "south":
48476                 return [0, 0];
48477             break;
48478         }
48479     },
48480
48481     getExpandAdj : function(){
48482         var c = this.collapsedEl, cm = this.cmargins;
48483         switch(this.position){
48484             case "west":
48485                 return [-(cm.right+c.getWidth()+cm.left), 0];
48486             break;
48487             case "east":
48488                 return [cm.right+c.getWidth()+cm.left, 0];
48489             break;
48490             case "north":
48491                 return [0, -(cm.top+cm.bottom+c.getHeight())];
48492             break;
48493             case "south":
48494                 return [0, cm.top+cm.bottom+c.getHeight()];
48495             break;
48496         }
48497     }
48498 });/*
48499  * Based on:
48500  * Ext JS Library 1.1.1
48501  * Copyright(c) 2006-2007, Ext JS, LLC.
48502  *
48503  * Originally Released Under LGPL - original licence link has changed is not relivant.
48504  *
48505  * Fork - LGPL
48506  * <script type="text/javascript">
48507  */
48508 /*
48509  * These classes are private internal classes
48510  */
48511 Roo.CenterLayoutRegion = function(mgr, config){
48512     Roo.LayoutRegion.call(this, mgr, config, "center");
48513     this.visible = true;
48514     this.minWidth = config.minWidth || 20;
48515     this.minHeight = config.minHeight || 20;
48516 };
48517
48518 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
48519     hide : function(){
48520         // center panel can't be hidden
48521     },
48522     
48523     show : function(){
48524         // center panel can't be hidden
48525     },
48526     
48527     getMinWidth: function(){
48528         return this.minWidth;
48529     },
48530     
48531     getMinHeight: function(){
48532         return this.minHeight;
48533     }
48534 });
48535
48536
48537 Roo.NorthLayoutRegion = function(mgr, config){
48538     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
48539     if(this.split){
48540         this.split.placement = Roo.SplitBar.TOP;
48541         this.split.orientation = Roo.SplitBar.VERTICAL;
48542         this.split.el.addClass("x-layout-split-v");
48543     }
48544     var size = config.initialSize || config.height;
48545     if(typeof size != "undefined"){
48546         this.el.setHeight(size);
48547     }
48548 };
48549 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
48550     orientation: Roo.SplitBar.VERTICAL,
48551     getBox : function(){
48552         if(this.collapsed){
48553             return this.collapsedEl.getBox();
48554         }
48555         var box = this.el.getBox();
48556         if(this.split){
48557             box.height += this.split.el.getHeight();
48558         }
48559         return box;
48560     },
48561     
48562     updateBox : function(box){
48563         if(this.split && !this.collapsed){
48564             box.height -= this.split.el.getHeight();
48565             this.split.el.setLeft(box.x);
48566             this.split.el.setTop(box.y+box.height);
48567             this.split.el.setWidth(box.width);
48568         }
48569         if(this.collapsed){
48570             this.updateBody(box.width, null);
48571         }
48572         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48573     }
48574 });
48575
48576 Roo.SouthLayoutRegion = function(mgr, config){
48577     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
48578     if(this.split){
48579         this.split.placement = Roo.SplitBar.BOTTOM;
48580         this.split.orientation = Roo.SplitBar.VERTICAL;
48581         this.split.el.addClass("x-layout-split-v");
48582     }
48583     var size = config.initialSize || config.height;
48584     if(typeof size != "undefined"){
48585         this.el.setHeight(size);
48586     }
48587 };
48588 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
48589     orientation: Roo.SplitBar.VERTICAL,
48590     getBox : function(){
48591         if(this.collapsed){
48592             return this.collapsedEl.getBox();
48593         }
48594         var box = this.el.getBox();
48595         if(this.split){
48596             var sh = this.split.el.getHeight();
48597             box.height += sh;
48598             box.y -= sh;
48599         }
48600         return box;
48601     },
48602     
48603     updateBox : function(box){
48604         if(this.split && !this.collapsed){
48605             var sh = this.split.el.getHeight();
48606             box.height -= sh;
48607             box.y += sh;
48608             this.split.el.setLeft(box.x);
48609             this.split.el.setTop(box.y-sh);
48610             this.split.el.setWidth(box.width);
48611         }
48612         if(this.collapsed){
48613             this.updateBody(box.width, null);
48614         }
48615         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48616     }
48617 });
48618
48619 Roo.EastLayoutRegion = function(mgr, config){
48620     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
48621     if(this.split){
48622         this.split.placement = Roo.SplitBar.RIGHT;
48623         this.split.orientation = Roo.SplitBar.HORIZONTAL;
48624         this.split.el.addClass("x-layout-split-h");
48625     }
48626     var size = config.initialSize || config.width;
48627     if(typeof size != "undefined"){
48628         this.el.setWidth(size);
48629     }
48630 };
48631 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
48632     orientation: Roo.SplitBar.HORIZONTAL,
48633     getBox : function(){
48634         if(this.collapsed){
48635             return this.collapsedEl.getBox();
48636         }
48637         var box = this.el.getBox();
48638         if(this.split){
48639             var sw = this.split.el.getWidth();
48640             box.width += sw;
48641             box.x -= sw;
48642         }
48643         return box;
48644     },
48645
48646     updateBox : function(box){
48647         if(this.split && !this.collapsed){
48648             var sw = this.split.el.getWidth();
48649             box.width -= sw;
48650             this.split.el.setLeft(box.x);
48651             this.split.el.setTop(box.y);
48652             this.split.el.setHeight(box.height);
48653             box.x += sw;
48654         }
48655         if(this.collapsed){
48656             this.updateBody(null, box.height);
48657         }
48658         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48659     }
48660 });
48661
48662 Roo.WestLayoutRegion = function(mgr, config){
48663     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
48664     if(this.split){
48665         this.split.placement = Roo.SplitBar.LEFT;
48666         this.split.orientation = Roo.SplitBar.HORIZONTAL;
48667         this.split.el.addClass("x-layout-split-h");
48668     }
48669     var size = config.initialSize || config.width;
48670     if(typeof size != "undefined"){
48671         this.el.setWidth(size);
48672     }
48673 };
48674 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
48675     orientation: Roo.SplitBar.HORIZONTAL,
48676     getBox : function(){
48677         if(this.collapsed){
48678             return this.collapsedEl.getBox();
48679         }
48680         var box = this.el.getBox();
48681         if(this.split){
48682             box.width += this.split.el.getWidth();
48683         }
48684         return box;
48685     },
48686     
48687     updateBox : function(box){
48688         if(this.split && !this.collapsed){
48689             var sw = this.split.el.getWidth();
48690             box.width -= sw;
48691             this.split.el.setLeft(box.x+box.width);
48692             this.split.el.setTop(box.y);
48693             this.split.el.setHeight(box.height);
48694         }
48695         if(this.collapsed){
48696             this.updateBody(null, box.height);
48697         }
48698         Roo.LayoutRegion.prototype.updateBox.call(this, box);
48699     }
48700 });
48701 /*
48702  * Based on:
48703  * Ext JS Library 1.1.1
48704  * Copyright(c) 2006-2007, Ext JS, LLC.
48705  *
48706  * Originally Released Under LGPL - original licence link has changed is not relivant.
48707  *
48708  * Fork - LGPL
48709  * <script type="text/javascript">
48710  */
48711  
48712  
48713 /*
48714  * Private internal class for reading and applying state
48715  */
48716 Roo.LayoutStateManager = function(layout){
48717      // default empty state
48718      this.state = {
48719         north: {},
48720         south: {},
48721         east: {},
48722         west: {}       
48723     };
48724 };
48725
48726 Roo.LayoutStateManager.prototype = {
48727     init : function(layout, provider){
48728         this.provider = provider;
48729         var state = provider.get(layout.id+"-layout-state");
48730         if(state){
48731             var wasUpdating = layout.isUpdating();
48732             if(!wasUpdating){
48733                 layout.beginUpdate();
48734             }
48735             for(var key in state){
48736                 if(typeof state[key] != "function"){
48737                     var rstate = state[key];
48738                     var r = layout.getRegion(key);
48739                     if(r && rstate){
48740                         if(rstate.size){
48741                             r.resizeTo(rstate.size);
48742                         }
48743                         if(rstate.collapsed == true){
48744                             r.collapse(true);
48745                         }else{
48746                             r.expand(null, true);
48747                         }
48748                     }
48749                 }
48750             }
48751             if(!wasUpdating){
48752                 layout.endUpdate();
48753             }
48754             this.state = state; 
48755         }
48756         this.layout = layout;
48757         layout.on("regionresized", this.onRegionResized, this);
48758         layout.on("regioncollapsed", this.onRegionCollapsed, this);
48759         layout.on("regionexpanded", this.onRegionExpanded, this);
48760     },
48761     
48762     storeState : function(){
48763         this.provider.set(this.layout.id+"-layout-state", this.state);
48764     },
48765     
48766     onRegionResized : function(region, newSize){
48767         this.state[region.getPosition()].size = newSize;
48768         this.storeState();
48769     },
48770     
48771     onRegionCollapsed : function(region){
48772         this.state[region.getPosition()].collapsed = true;
48773         this.storeState();
48774     },
48775     
48776     onRegionExpanded : function(region){
48777         this.state[region.getPosition()].collapsed = false;
48778         this.storeState();
48779     }
48780 };/*
48781  * Based on:
48782  * Ext JS Library 1.1.1
48783  * Copyright(c) 2006-2007, Ext JS, LLC.
48784  *
48785  * Originally Released Under LGPL - original licence link has changed is not relivant.
48786  *
48787  * Fork - LGPL
48788  * <script type="text/javascript">
48789  */
48790 /**
48791  * @class Roo.ContentPanel
48792  * @extends Roo.util.Observable
48793  * A basic ContentPanel element.
48794  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
48795  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
48796  * @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
48797  * @cfg {Boolean}   closable      True if the panel can be closed/removed
48798  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
48799  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
48800  * @cfg {Toolbar}   toolbar       A toolbar for this panel
48801  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
48802  * @cfg {String} title          The title for this panel
48803  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
48804  * @cfg {String} url            Calls {@link #setUrl} with this value
48805  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
48806  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
48807  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
48808  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
48809
48810  * @constructor
48811  * Create a new ContentPanel.
48812  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
48813  * @param {String/Object} config A string to set only the title or a config object
48814  * @param {String} content (optional) Set the HTML content for this panel
48815  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
48816  */
48817 Roo.ContentPanel = function(el, config, content){
48818     
48819      
48820     /*
48821     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
48822         config = el;
48823         el = Roo.id();
48824     }
48825     if (config && config.parentLayout) { 
48826         el = config.parentLayout.el.createChild(); 
48827     }
48828     */
48829     if(el.autoCreate){ // xtype is available if this is called from factory
48830         config = el;
48831         el = Roo.id();
48832     }
48833     this.el = Roo.get(el);
48834     if(!this.el && config && config.autoCreate){
48835         if(typeof config.autoCreate == "object"){
48836             if(!config.autoCreate.id){
48837                 config.autoCreate.id = config.id||el;
48838             }
48839             this.el = Roo.DomHelper.append(document.body,
48840                         config.autoCreate, true);
48841         }else{
48842             this.el = Roo.DomHelper.append(document.body,
48843                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
48844         }
48845     }
48846     this.closable = false;
48847     this.loaded = false;
48848     this.active = false;
48849     if(typeof config == "string"){
48850         this.title = config;
48851     }else{
48852         Roo.apply(this, config);
48853     }
48854     
48855     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
48856         this.wrapEl = this.el.wrap();
48857         this.toolbar.container = this.el.insertSibling(false, 'before');
48858         this.toolbar = new Roo.Toolbar(this.toolbar);
48859     }
48860     
48861     // xtype created footer. - not sure if will work as we normally have to render first..
48862     if (this.footer && !this.footer.el && this.footer.xtype) {
48863         if (!this.wrapEl) {
48864             this.wrapEl = this.el.wrap();
48865         }
48866     
48867         this.footer.container = this.wrapEl.createChild();
48868          
48869         this.footer = Roo.factory(this.footer, Roo);
48870         
48871     }
48872     
48873     if(this.resizeEl){
48874         this.resizeEl = Roo.get(this.resizeEl, true);
48875     }else{
48876         this.resizeEl = this.el;
48877     }
48878     // handle view.xtype
48879     
48880  
48881     
48882     
48883     this.addEvents({
48884         /**
48885          * @event activate
48886          * Fires when this panel is activated. 
48887          * @param {Roo.ContentPanel} this
48888          */
48889         "activate" : true,
48890         /**
48891          * @event deactivate
48892          * Fires when this panel is activated. 
48893          * @param {Roo.ContentPanel} this
48894          */
48895         "deactivate" : true,
48896
48897         /**
48898          * @event resize
48899          * Fires when this panel is resized if fitToFrame is true.
48900          * @param {Roo.ContentPanel} this
48901          * @param {Number} width The width after any component adjustments
48902          * @param {Number} height The height after any component adjustments
48903          */
48904         "resize" : true,
48905         
48906          /**
48907          * @event render
48908          * Fires when this tab is created
48909          * @param {Roo.ContentPanel} this
48910          */
48911         "render" : true
48912         
48913         
48914         
48915     });
48916     
48917
48918     
48919     
48920     if(this.autoScroll){
48921         this.resizeEl.setStyle("overflow", "auto");
48922     } else {
48923         // fix randome scrolling
48924         this.el.on('scroll', function() {
48925             Roo.log('fix random scolling');
48926             this.scrollTo('top',0); 
48927         });
48928     }
48929     content = content || this.content;
48930     if(content){
48931         this.setContent(content);
48932     }
48933     if(config && config.url){
48934         this.setUrl(this.url, this.params, this.loadOnce);
48935     }
48936     
48937     
48938     
48939     Roo.ContentPanel.superclass.constructor.call(this);
48940     
48941     if (this.view && typeof(this.view.xtype) != 'undefined') {
48942         this.view.el = this.el.appendChild(document.createElement("div"));
48943         this.view = Roo.factory(this.view); 
48944         this.view.render  &&  this.view.render(false, '');  
48945     }
48946     
48947     
48948     this.fireEvent('render', this);
48949 };
48950
48951 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
48952     tabTip:'',
48953     setRegion : function(region){
48954         this.region = region;
48955         if(region){
48956            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
48957         }else{
48958            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
48959         } 
48960     },
48961     
48962     /**
48963      * Returns the toolbar for this Panel if one was configured. 
48964      * @return {Roo.Toolbar} 
48965      */
48966     getToolbar : function(){
48967         return this.toolbar;
48968     },
48969     
48970     setActiveState : function(active){
48971         this.active = active;
48972         if(!active){
48973             this.fireEvent("deactivate", this);
48974         }else{
48975             this.fireEvent("activate", this);
48976         }
48977     },
48978     /**
48979      * Updates this panel's element
48980      * @param {String} content The new content
48981      * @param {Boolean} loadScripts (optional) true to look for and process scripts
48982     */
48983     setContent : function(content, loadScripts){
48984         this.el.update(content, loadScripts);
48985     },
48986
48987     ignoreResize : function(w, h){
48988         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
48989             return true;
48990         }else{
48991             this.lastSize = {width: w, height: h};
48992             return false;
48993         }
48994     },
48995     /**
48996      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
48997      * @return {Roo.UpdateManager} The UpdateManager
48998      */
48999     getUpdateManager : function(){
49000         return this.el.getUpdateManager();
49001     },
49002      /**
49003      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
49004      * @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:
49005 <pre><code>
49006 panel.load({
49007     url: "your-url.php",
49008     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
49009     callback: yourFunction,
49010     scope: yourObject, //(optional scope)
49011     discardUrl: false,
49012     nocache: false,
49013     text: "Loading...",
49014     timeout: 30,
49015     scripts: false
49016 });
49017 </code></pre>
49018      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
49019      * 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.
49020      * @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}
49021      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
49022      * @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.
49023      * @return {Roo.ContentPanel} this
49024      */
49025     load : function(){
49026         var um = this.el.getUpdateManager();
49027         um.update.apply(um, arguments);
49028         return this;
49029     },
49030
49031
49032     /**
49033      * 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.
49034      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
49035      * @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)
49036      * @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)
49037      * @return {Roo.UpdateManager} The UpdateManager
49038      */
49039     setUrl : function(url, params, loadOnce){
49040         if(this.refreshDelegate){
49041             this.removeListener("activate", this.refreshDelegate);
49042         }
49043         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
49044         this.on("activate", this.refreshDelegate);
49045         return this.el.getUpdateManager();
49046     },
49047     
49048     _handleRefresh : function(url, params, loadOnce){
49049         if(!loadOnce || !this.loaded){
49050             var updater = this.el.getUpdateManager();
49051             updater.update(url, params, this._setLoaded.createDelegate(this));
49052         }
49053     },
49054     
49055     _setLoaded : function(){
49056         this.loaded = true;
49057     }, 
49058     
49059     /**
49060      * Returns this panel's id
49061      * @return {String} 
49062      */
49063     getId : function(){
49064         return this.el.id;
49065     },
49066     
49067     /** 
49068      * Returns this panel's element - used by regiosn to add.
49069      * @return {Roo.Element} 
49070      */
49071     getEl : function(){
49072         return this.wrapEl || this.el;
49073     },
49074     
49075     adjustForComponents : function(width, height)
49076     {
49077         //Roo.log('adjustForComponents ');
49078         if(this.resizeEl != this.el){
49079             width -= this.el.getFrameWidth('lr');
49080             height -= this.el.getFrameWidth('tb');
49081         }
49082         if(this.toolbar){
49083             var te = this.toolbar.getEl();
49084             height -= te.getHeight();
49085             te.setWidth(width);
49086         }
49087         if(this.footer){
49088             var te = this.footer.getEl();
49089             Roo.log("footer:" + te.getHeight());
49090             
49091             height -= te.getHeight();
49092             te.setWidth(width);
49093         }
49094         
49095         
49096         if(this.adjustments){
49097             width += this.adjustments[0];
49098             height += this.adjustments[1];
49099         }
49100         return {"width": width, "height": height};
49101     },
49102     
49103     setSize : function(width, height){
49104         if(this.fitToFrame && !this.ignoreResize(width, height)){
49105             if(this.fitContainer && this.resizeEl != this.el){
49106                 this.el.setSize(width, height);
49107             }
49108             var size = this.adjustForComponents(width, height);
49109             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
49110             this.fireEvent('resize', this, size.width, size.height);
49111         }
49112     },
49113     
49114     /**
49115      * Returns this panel's title
49116      * @return {String} 
49117      */
49118     getTitle : function(){
49119         return this.title;
49120     },
49121     
49122     /**
49123      * Set this panel's title
49124      * @param {String} title
49125      */
49126     setTitle : function(title){
49127         this.title = title;
49128         if(this.region){
49129             this.region.updatePanelTitle(this, title);
49130         }
49131     },
49132     
49133     /**
49134      * Returns true is this panel was configured to be closable
49135      * @return {Boolean} 
49136      */
49137     isClosable : function(){
49138         return this.closable;
49139     },
49140     
49141     beforeSlide : function(){
49142         this.el.clip();
49143         this.resizeEl.clip();
49144     },
49145     
49146     afterSlide : function(){
49147         this.el.unclip();
49148         this.resizeEl.unclip();
49149     },
49150     
49151     /**
49152      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
49153      *   Will fail silently if the {@link #setUrl} method has not been called.
49154      *   This does not activate the panel, just updates its content.
49155      */
49156     refresh : function(){
49157         if(this.refreshDelegate){
49158            this.loaded = false;
49159            this.refreshDelegate();
49160         }
49161     },
49162     
49163     /**
49164      * Destroys this panel
49165      */
49166     destroy : function(){
49167         this.el.removeAllListeners();
49168         var tempEl = document.createElement("span");
49169         tempEl.appendChild(this.el.dom);
49170         tempEl.innerHTML = "";
49171         this.el.remove();
49172         this.el = null;
49173     },
49174     
49175     /**
49176      * form - if the content panel contains a form - this is a reference to it.
49177      * @type {Roo.form.Form}
49178      */
49179     form : false,
49180     /**
49181      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
49182      *    This contains a reference to it.
49183      * @type {Roo.View}
49184      */
49185     view : false,
49186     
49187       /**
49188      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
49189      * <pre><code>
49190
49191 layout.addxtype({
49192        xtype : 'Form',
49193        items: [ .... ]
49194    }
49195 );
49196
49197 </code></pre>
49198      * @param {Object} cfg Xtype definition of item to add.
49199      */
49200     
49201     addxtype : function(cfg) {
49202         // add form..
49203         if (cfg.xtype.match(/^Form$/)) {
49204             
49205             var el;
49206             //if (this.footer) {
49207             //    el = this.footer.container.insertSibling(false, 'before');
49208             //} else {
49209                 el = this.el.createChild();
49210             //}
49211
49212             this.form = new  Roo.form.Form(cfg);
49213             
49214             
49215             if ( this.form.allItems.length) this.form.render(el.dom);
49216             return this.form;
49217         }
49218         // should only have one of theses..
49219         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
49220             // views.. should not be just added - used named prop 'view''
49221             
49222             cfg.el = this.el.appendChild(document.createElement("div"));
49223             // factory?
49224             
49225             var ret = new Roo.factory(cfg);
49226              
49227              ret.render && ret.render(false, ''); // render blank..
49228             this.view = ret;
49229             return ret;
49230         }
49231         return false;
49232     }
49233 });
49234
49235 /**
49236  * @class Roo.GridPanel
49237  * @extends Roo.ContentPanel
49238  * @constructor
49239  * Create a new GridPanel.
49240  * @param {Roo.grid.Grid} grid The grid for this panel
49241  * @param {String/Object} config A string to set only the panel's title, or a config object
49242  */
49243 Roo.GridPanel = function(grid, config){
49244     
49245   
49246     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
49247         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
49248         
49249     this.wrapper.dom.appendChild(grid.getGridEl().dom);
49250     
49251     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
49252     
49253     if(this.toolbar){
49254         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
49255     }
49256     // xtype created footer. - not sure if will work as we normally have to render first..
49257     if (this.footer && !this.footer.el && this.footer.xtype) {
49258         
49259         this.footer.container = this.grid.getView().getFooterPanel(true);
49260         this.footer.dataSource = this.grid.dataSource;
49261         this.footer = Roo.factory(this.footer, Roo);
49262         
49263     }
49264     
49265     grid.monitorWindowResize = false; // turn off autosizing
49266     grid.autoHeight = false;
49267     grid.autoWidth = false;
49268     this.grid = grid;
49269     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
49270 };
49271
49272 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
49273     getId : function(){
49274         return this.grid.id;
49275     },
49276     
49277     /**
49278      * Returns the grid for this panel
49279      * @return {Roo.grid.Grid} 
49280      */
49281     getGrid : function(){
49282         return this.grid;    
49283     },
49284     
49285     setSize : function(width, height){
49286         if(!this.ignoreResize(width, height)){
49287             var grid = this.grid;
49288             var size = this.adjustForComponents(width, height);
49289             grid.getGridEl().setSize(size.width, size.height);
49290             grid.autoSize();
49291         }
49292     },
49293     
49294     beforeSlide : function(){
49295         this.grid.getView().scroller.clip();
49296     },
49297     
49298     afterSlide : function(){
49299         this.grid.getView().scroller.unclip();
49300     },
49301     
49302     destroy : function(){
49303         this.grid.destroy();
49304         delete this.grid;
49305         Roo.GridPanel.superclass.destroy.call(this); 
49306     }
49307 });
49308
49309
49310 /**
49311  * @class Roo.NestedLayoutPanel
49312  * @extends Roo.ContentPanel
49313  * @constructor
49314  * Create a new NestedLayoutPanel.
49315  * 
49316  * 
49317  * @param {Roo.BorderLayout} layout The layout for this panel
49318  * @param {String/Object} config A string to set only the title or a config object
49319  */
49320 Roo.NestedLayoutPanel = function(layout, config)
49321 {
49322     // construct with only one argument..
49323     /* FIXME - implement nicer consturctors
49324     if (layout.layout) {
49325         config = layout;
49326         layout = config.layout;
49327         delete config.layout;
49328     }
49329     if (layout.xtype && !layout.getEl) {
49330         // then layout needs constructing..
49331         layout = Roo.factory(layout, Roo);
49332     }
49333     */
49334     
49335     
49336     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
49337     
49338     layout.monitorWindowResize = false; // turn off autosizing
49339     this.layout = layout;
49340     this.layout.getEl().addClass("x-layout-nested-layout");
49341     
49342     
49343     
49344     
49345 };
49346
49347 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
49348
49349     setSize : function(width, height){
49350         if(!this.ignoreResize(width, height)){
49351             var size = this.adjustForComponents(width, height);
49352             var el = this.layout.getEl();
49353             el.setSize(size.width, size.height);
49354             var touch = el.dom.offsetWidth;
49355             this.layout.layout();
49356             // ie requires a double layout on the first pass
49357             if(Roo.isIE && !this.initialized){
49358                 this.initialized = true;
49359                 this.layout.layout();
49360             }
49361         }
49362     },
49363     
49364     // activate all subpanels if not currently active..
49365     
49366     setActiveState : function(active){
49367         this.active = active;
49368         if(!active){
49369             this.fireEvent("deactivate", this);
49370             return;
49371         }
49372         
49373         this.fireEvent("activate", this);
49374         // not sure if this should happen before or after..
49375         if (!this.layout) {
49376             return; // should not happen..
49377         }
49378         var reg = false;
49379         for (var r in this.layout.regions) {
49380             reg = this.layout.getRegion(r);
49381             if (reg.getActivePanel()) {
49382                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
49383                 reg.setActivePanel(reg.getActivePanel());
49384                 continue;
49385             }
49386             if (!reg.panels.length) {
49387                 continue;
49388             }
49389             reg.showPanel(reg.getPanel(0));
49390         }
49391         
49392         
49393         
49394         
49395     },
49396     
49397     /**
49398      * Returns the nested BorderLayout for this panel
49399      * @return {Roo.BorderLayout} 
49400      */
49401     getLayout : function(){
49402         return this.layout;
49403     },
49404     
49405      /**
49406      * Adds a xtype elements to the layout of the nested panel
49407      * <pre><code>
49408
49409 panel.addxtype({
49410        xtype : 'ContentPanel',
49411        region: 'west',
49412        items: [ .... ]
49413    }
49414 );
49415
49416 panel.addxtype({
49417         xtype : 'NestedLayoutPanel',
49418         region: 'west',
49419         layout: {
49420            center: { },
49421            west: { }   
49422         },
49423         items : [ ... list of content panels or nested layout panels.. ]
49424    }
49425 );
49426 </code></pre>
49427      * @param {Object} cfg Xtype definition of item to add.
49428      */
49429     addxtype : function(cfg) {
49430         return this.layout.addxtype(cfg);
49431     
49432     }
49433 });
49434
49435 Roo.ScrollPanel = function(el, config, content){
49436     config = config || {};
49437     config.fitToFrame = true;
49438     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
49439     
49440     this.el.dom.style.overflow = "hidden";
49441     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
49442     this.el.removeClass("x-layout-inactive-content");
49443     this.el.on("mousewheel", this.onWheel, this);
49444
49445     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
49446     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
49447     up.unselectable(); down.unselectable();
49448     up.on("click", this.scrollUp, this);
49449     down.on("click", this.scrollDown, this);
49450     up.addClassOnOver("x-scroller-btn-over");
49451     down.addClassOnOver("x-scroller-btn-over");
49452     up.addClassOnClick("x-scroller-btn-click");
49453     down.addClassOnClick("x-scroller-btn-click");
49454     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
49455
49456     this.resizeEl = this.el;
49457     this.el = wrap; this.up = up; this.down = down;
49458 };
49459
49460 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
49461     increment : 100,
49462     wheelIncrement : 5,
49463     scrollUp : function(){
49464         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
49465     },
49466
49467     scrollDown : function(){
49468         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
49469     },
49470
49471     afterScroll : function(){
49472         var el = this.resizeEl;
49473         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
49474         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
49475         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
49476     },
49477
49478     setSize : function(){
49479         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
49480         this.afterScroll();
49481     },
49482
49483     onWheel : function(e){
49484         var d = e.getWheelDelta();
49485         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
49486         this.afterScroll();
49487         e.stopEvent();
49488     },
49489
49490     setContent : function(content, loadScripts){
49491         this.resizeEl.update(content, loadScripts);
49492     }
49493
49494 });
49495
49496
49497
49498
49499
49500
49501
49502
49503
49504 /**
49505  * @class Roo.TreePanel
49506  * @extends Roo.ContentPanel
49507  * @constructor
49508  * Create a new TreePanel. - defaults to fit/scoll contents.
49509  * @param {String/Object} config A string to set only the panel's title, or a config object
49510  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
49511  */
49512 Roo.TreePanel = function(config){
49513     var el = config.el;
49514     var tree = config.tree;
49515     delete config.tree; 
49516     delete config.el; // hopefull!
49517     
49518     // wrapper for IE7 strict & safari scroll issue
49519     
49520     var treeEl = el.createChild();
49521     config.resizeEl = treeEl;
49522     
49523     
49524     
49525     Roo.TreePanel.superclass.constructor.call(this, el, config);
49526  
49527  
49528     this.tree = new Roo.tree.TreePanel(treeEl , tree);
49529     //console.log(tree);
49530     this.on('activate', function()
49531     {
49532         if (this.tree.rendered) {
49533             return;
49534         }
49535         //console.log('render tree');
49536         this.tree.render();
49537     });
49538     // this should not be needed.. - it's actually the 'el' that resizes?
49539     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
49540     
49541     //this.on('resize',  function (cp, w, h) {
49542     //        this.tree.innerCt.setWidth(w);
49543     //        this.tree.innerCt.setHeight(h);
49544     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
49545     //});
49546
49547         
49548     
49549 };
49550
49551 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
49552     fitToFrame : true,
49553     autoScroll : true
49554 });
49555
49556
49557
49558
49559
49560
49561
49562
49563
49564
49565
49566 /*
49567  * Based on:
49568  * Ext JS Library 1.1.1
49569  * Copyright(c) 2006-2007, Ext JS, LLC.
49570  *
49571  * Originally Released Under LGPL - original licence link has changed is not relivant.
49572  *
49573  * Fork - LGPL
49574  * <script type="text/javascript">
49575  */
49576  
49577
49578 /**
49579  * @class Roo.ReaderLayout
49580  * @extends Roo.BorderLayout
49581  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
49582  * center region containing two nested regions (a top one for a list view and one for item preview below),
49583  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
49584  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
49585  * expedites the setup of the overall layout and regions for this common application style.
49586  * Example:
49587  <pre><code>
49588 var reader = new Roo.ReaderLayout();
49589 var CP = Roo.ContentPanel;  // shortcut for adding
49590
49591 reader.beginUpdate();
49592 reader.add("north", new CP("north", "North"));
49593 reader.add("west", new CP("west", {title: "West"}));
49594 reader.add("east", new CP("east", {title: "East"}));
49595
49596 reader.regions.listView.add(new CP("listView", "List"));
49597 reader.regions.preview.add(new CP("preview", "Preview"));
49598 reader.endUpdate();
49599 </code></pre>
49600 * @constructor
49601 * Create a new ReaderLayout
49602 * @param {Object} config Configuration options
49603 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
49604 * document.body if omitted)
49605 */
49606 Roo.ReaderLayout = function(config, renderTo){
49607     var c = config || {size:{}};
49608     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
49609         north: c.north !== false ? Roo.apply({
49610             split:false,
49611             initialSize: 32,
49612             titlebar: false
49613         }, c.north) : false,
49614         west: c.west !== false ? Roo.apply({
49615             split:true,
49616             initialSize: 200,
49617             minSize: 175,
49618             maxSize: 400,
49619             titlebar: true,
49620             collapsible: true,
49621             animate: true,
49622             margins:{left:5,right:0,bottom:5,top:5},
49623             cmargins:{left:5,right:5,bottom:5,top:5}
49624         }, c.west) : false,
49625         east: c.east !== false ? Roo.apply({
49626             split:true,
49627             initialSize: 200,
49628             minSize: 175,
49629             maxSize: 400,
49630             titlebar: true,
49631             collapsible: true,
49632             animate: true,
49633             margins:{left:0,right:5,bottom:5,top:5},
49634             cmargins:{left:5,right:5,bottom:5,top:5}
49635         }, c.east) : false,
49636         center: Roo.apply({
49637             tabPosition: 'top',
49638             autoScroll:false,
49639             closeOnTab: true,
49640             titlebar:false,
49641             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
49642         }, c.center)
49643     });
49644
49645     this.el.addClass('x-reader');
49646
49647     this.beginUpdate();
49648
49649     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
49650         south: c.preview !== false ? Roo.apply({
49651             split:true,
49652             initialSize: 200,
49653             minSize: 100,
49654             autoScroll:true,
49655             collapsible:true,
49656             titlebar: true,
49657             cmargins:{top:5,left:0, right:0, bottom:0}
49658         }, c.preview) : false,
49659         center: Roo.apply({
49660             autoScroll:false,
49661             titlebar:false,
49662             minHeight:200
49663         }, c.listView)
49664     });
49665     this.add('center', new Roo.NestedLayoutPanel(inner,
49666             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
49667
49668     this.endUpdate();
49669
49670     this.regions.preview = inner.getRegion('south');
49671     this.regions.listView = inner.getRegion('center');
49672 };
49673
49674 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
49675  * Based on:
49676  * Ext JS Library 1.1.1
49677  * Copyright(c) 2006-2007, Ext JS, LLC.
49678  *
49679  * Originally Released Under LGPL - original licence link has changed is not relivant.
49680  *
49681  * Fork - LGPL
49682  * <script type="text/javascript">
49683  */
49684  
49685 /**
49686  * @class Roo.grid.Grid
49687  * @extends Roo.util.Observable
49688  * This class represents the primary interface of a component based grid control.
49689  * <br><br>Usage:<pre><code>
49690  var grid = new Roo.grid.Grid("my-container-id", {
49691      ds: myDataStore,
49692      cm: myColModel,
49693      selModel: mySelectionModel,
49694      autoSizeColumns: true,
49695      monitorWindowResize: false,
49696      trackMouseOver: true
49697  });
49698  // set any options
49699  grid.render();
49700  * </code></pre>
49701  * <b>Common Problems:</b><br/>
49702  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
49703  * element will correct this<br/>
49704  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
49705  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
49706  * are unpredictable.<br/>
49707  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
49708  * grid to calculate dimensions/offsets.<br/>
49709   * @constructor
49710  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
49711  * The container MUST have some type of size defined for the grid to fill. The container will be
49712  * automatically set to position relative if it isn't already.
49713  * @param {Object} config A config object that sets properties on this grid.
49714  */
49715 Roo.grid.Grid = function(container, config){
49716         // initialize the container
49717         this.container = Roo.get(container);
49718         this.container.update("");
49719         this.container.setStyle("overflow", "hidden");
49720     this.container.addClass('x-grid-container');
49721
49722     this.id = this.container.id;
49723
49724     Roo.apply(this, config);
49725     // check and correct shorthanded configs
49726     if(this.ds){
49727         this.dataSource = this.ds;
49728         delete this.ds;
49729     }
49730     if(this.cm){
49731         this.colModel = this.cm;
49732         delete this.cm;
49733     }
49734     if(this.sm){
49735         this.selModel = this.sm;
49736         delete this.sm;
49737     }
49738
49739     if (this.selModel) {
49740         this.selModel = Roo.factory(this.selModel, Roo.grid);
49741         this.sm = this.selModel;
49742         this.sm.xmodule = this.xmodule || false;
49743     }
49744     if (typeof(this.colModel.config) == 'undefined') {
49745         this.colModel = new Roo.grid.ColumnModel(this.colModel);
49746         this.cm = this.colModel;
49747         this.cm.xmodule = this.xmodule || false;
49748     }
49749     if (this.dataSource) {
49750         this.dataSource= Roo.factory(this.dataSource, Roo.data);
49751         this.ds = this.dataSource;
49752         this.ds.xmodule = this.xmodule || false;
49753          
49754     }
49755     
49756     
49757     
49758     if(this.width){
49759         this.container.setWidth(this.width);
49760     }
49761
49762     if(this.height){
49763         this.container.setHeight(this.height);
49764     }
49765     /** @private */
49766         this.addEvents({
49767         // raw events
49768         /**
49769          * @event click
49770          * The raw click event for the entire grid.
49771          * @param {Roo.EventObject} e
49772          */
49773         "click" : true,
49774         /**
49775          * @event dblclick
49776          * The raw dblclick event for the entire grid.
49777          * @param {Roo.EventObject} e
49778          */
49779         "dblclick" : true,
49780         /**
49781          * @event contextmenu
49782          * The raw contextmenu event for the entire grid.
49783          * @param {Roo.EventObject} e
49784          */
49785         "contextmenu" : true,
49786         /**
49787          * @event mousedown
49788          * The raw mousedown event for the entire grid.
49789          * @param {Roo.EventObject} e
49790          */
49791         "mousedown" : true,
49792         /**
49793          * @event mouseup
49794          * The raw mouseup event for the entire grid.
49795          * @param {Roo.EventObject} e
49796          */
49797         "mouseup" : true,
49798         /**
49799          * @event mouseover
49800          * The raw mouseover event for the entire grid.
49801          * @param {Roo.EventObject} e
49802          */
49803         "mouseover" : true,
49804         /**
49805          * @event mouseout
49806          * The raw mouseout event for the entire grid.
49807          * @param {Roo.EventObject} e
49808          */
49809         "mouseout" : true,
49810         /**
49811          * @event keypress
49812          * The raw keypress event for the entire grid.
49813          * @param {Roo.EventObject} e
49814          */
49815         "keypress" : true,
49816         /**
49817          * @event keydown
49818          * The raw keydown event for the entire grid.
49819          * @param {Roo.EventObject} e
49820          */
49821         "keydown" : true,
49822
49823         // custom events
49824
49825         /**
49826          * @event cellclick
49827          * Fires when a cell is clicked
49828          * @param {Grid} this
49829          * @param {Number} rowIndex
49830          * @param {Number} columnIndex
49831          * @param {Roo.EventObject} e
49832          */
49833         "cellclick" : true,
49834         /**
49835          * @event celldblclick
49836          * Fires when a cell is double clicked
49837          * @param {Grid} this
49838          * @param {Number} rowIndex
49839          * @param {Number} columnIndex
49840          * @param {Roo.EventObject} e
49841          */
49842         "celldblclick" : true,
49843         /**
49844          * @event rowclick
49845          * Fires when a row is clicked
49846          * @param {Grid} this
49847          * @param {Number} rowIndex
49848          * @param {Roo.EventObject} e
49849          */
49850         "rowclick" : true,
49851         /**
49852          * @event rowdblclick
49853          * Fires when a row is double clicked
49854          * @param {Grid} this
49855          * @param {Number} rowIndex
49856          * @param {Roo.EventObject} e
49857          */
49858         "rowdblclick" : true,
49859         /**
49860          * @event headerclick
49861          * Fires when a header is clicked
49862          * @param {Grid} this
49863          * @param {Number} columnIndex
49864          * @param {Roo.EventObject} e
49865          */
49866         "headerclick" : true,
49867         /**
49868          * @event headerdblclick
49869          * Fires when a header cell is double clicked
49870          * @param {Grid} this
49871          * @param {Number} columnIndex
49872          * @param {Roo.EventObject} e
49873          */
49874         "headerdblclick" : true,
49875         /**
49876          * @event rowcontextmenu
49877          * Fires when a row is right clicked
49878          * @param {Grid} this
49879          * @param {Number} rowIndex
49880          * @param {Roo.EventObject} e
49881          */
49882         "rowcontextmenu" : true,
49883         /**
49884          * @event cellcontextmenu
49885          * Fires when a cell is right clicked
49886          * @param {Grid} this
49887          * @param {Number} rowIndex
49888          * @param {Number} cellIndex
49889          * @param {Roo.EventObject} e
49890          */
49891          "cellcontextmenu" : true,
49892         /**
49893          * @event headercontextmenu
49894          * Fires when a header is right clicked
49895          * @param {Grid} this
49896          * @param {Number} columnIndex
49897          * @param {Roo.EventObject} e
49898          */
49899         "headercontextmenu" : true,
49900         /**
49901          * @event bodyscroll
49902          * Fires when the body element is scrolled
49903          * @param {Number} scrollLeft
49904          * @param {Number} scrollTop
49905          */
49906         "bodyscroll" : true,
49907         /**
49908          * @event columnresize
49909          * Fires when the user resizes a column
49910          * @param {Number} columnIndex
49911          * @param {Number} newSize
49912          */
49913         "columnresize" : true,
49914         /**
49915          * @event columnmove
49916          * Fires when the user moves a column
49917          * @param {Number} oldIndex
49918          * @param {Number} newIndex
49919          */
49920         "columnmove" : true,
49921         /**
49922          * @event startdrag
49923          * Fires when row(s) start being dragged
49924          * @param {Grid} this
49925          * @param {Roo.GridDD} dd The drag drop object
49926          * @param {event} e The raw browser event
49927          */
49928         "startdrag" : true,
49929         /**
49930          * @event enddrag
49931          * Fires when a drag operation is complete
49932          * @param {Grid} this
49933          * @param {Roo.GridDD} dd The drag drop object
49934          * @param {event} e The raw browser event
49935          */
49936         "enddrag" : true,
49937         /**
49938          * @event dragdrop
49939          * Fires when dragged row(s) are dropped on a valid DD target
49940          * @param {Grid} this
49941          * @param {Roo.GridDD} dd The drag drop object
49942          * @param {String} targetId The target drag drop object
49943          * @param {event} e The raw browser event
49944          */
49945         "dragdrop" : true,
49946         /**
49947          * @event dragover
49948          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
49949          * @param {Grid} this
49950          * @param {Roo.GridDD} dd The drag drop object
49951          * @param {String} targetId The target drag drop object
49952          * @param {event} e The raw browser event
49953          */
49954         "dragover" : true,
49955         /**
49956          * @event dragenter
49957          *  Fires when the dragged row(s) first cross another DD target while being dragged
49958          * @param {Grid} this
49959          * @param {Roo.GridDD} dd The drag drop object
49960          * @param {String} targetId The target drag drop object
49961          * @param {event} e The raw browser event
49962          */
49963         "dragenter" : true,
49964         /**
49965          * @event dragout
49966          * Fires when the dragged row(s) leave another DD target while being dragged
49967          * @param {Grid} this
49968          * @param {Roo.GridDD} dd The drag drop object
49969          * @param {String} targetId The target drag drop object
49970          * @param {event} e The raw browser event
49971          */
49972         "dragout" : true,
49973         /**
49974          * @event rowclass
49975          * Fires when a row is rendered, so you can change add a style to it.
49976          * @param {GridView} gridview   The grid view
49977          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
49978          */
49979         'rowclass' : true,
49980
49981         /**
49982          * @event render
49983          * Fires when the grid is rendered
49984          * @param {Grid} grid
49985          */
49986         'render' : true
49987     });
49988
49989     Roo.grid.Grid.superclass.constructor.call(this);
49990 };
49991 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
49992     
49993     /**
49994      * @cfg {String} ddGroup - drag drop group.
49995      */
49996
49997     /**
49998      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
49999      */
50000     minColumnWidth : 25,
50001
50002     /**
50003      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
50004      * <b>on initial render.</b> It is more efficient to explicitly size the columns
50005      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
50006      */
50007     autoSizeColumns : false,
50008
50009     /**
50010      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
50011      */
50012     autoSizeHeaders : true,
50013
50014     /**
50015      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
50016      */
50017     monitorWindowResize : true,
50018
50019     /**
50020      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
50021      * rows measured to get a columns size. Default is 0 (all rows).
50022      */
50023     maxRowsToMeasure : 0,
50024
50025     /**
50026      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
50027      */
50028     trackMouseOver : true,
50029
50030     /**
50031     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
50032     */
50033     
50034     /**
50035     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
50036     */
50037     enableDragDrop : false,
50038     
50039     /**
50040     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
50041     */
50042     enableColumnMove : true,
50043     
50044     /**
50045     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
50046     */
50047     enableColumnHide : true,
50048     
50049     /**
50050     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
50051     */
50052     enableRowHeightSync : false,
50053     
50054     /**
50055     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
50056     */
50057     stripeRows : true,
50058     
50059     /**
50060     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
50061     */
50062     autoHeight : false,
50063
50064     /**
50065      * @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.
50066      */
50067     autoExpandColumn : false,
50068
50069     /**
50070     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
50071     * Default is 50.
50072     */
50073     autoExpandMin : 50,
50074
50075     /**
50076     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
50077     */
50078     autoExpandMax : 1000,
50079
50080     /**
50081     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
50082     */
50083     view : null,
50084
50085     /**
50086     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
50087     */
50088     loadMask : false,
50089     /**
50090     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
50091     */
50092     dropTarget: false,
50093     
50094    
50095     
50096     // private
50097     rendered : false,
50098
50099     /**
50100     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
50101     * of a fixed width. Default is false.
50102     */
50103     /**
50104     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
50105     */
50106     /**
50107      * Called once after all setup has been completed and the grid is ready to be rendered.
50108      * @return {Roo.grid.Grid} this
50109      */
50110     render : function()
50111     {
50112         var c = this.container;
50113         // try to detect autoHeight/width mode
50114         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
50115             this.autoHeight = true;
50116         }
50117         var view = this.getView();
50118         view.init(this);
50119
50120         c.on("click", this.onClick, this);
50121         c.on("dblclick", this.onDblClick, this);
50122         c.on("contextmenu", this.onContextMenu, this);
50123         c.on("keydown", this.onKeyDown, this);
50124
50125         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
50126
50127         this.getSelectionModel().init(this);
50128
50129         view.render();
50130
50131         if(this.loadMask){
50132             this.loadMask = new Roo.LoadMask(this.container,
50133                     Roo.apply({store:this.dataSource}, this.loadMask));
50134         }
50135         
50136         
50137         if (this.toolbar && this.toolbar.xtype) {
50138             this.toolbar.container = this.getView().getHeaderPanel(true);
50139             this.toolbar = new Roo.Toolbar(this.toolbar);
50140         }
50141         if (this.footer && this.footer.xtype) {
50142             this.footer.dataSource = this.getDataSource();
50143             this.footer.container = this.getView().getFooterPanel(true);
50144             this.footer = Roo.factory(this.footer, Roo);
50145         }
50146         if (this.dropTarget && this.dropTarget.xtype) {
50147             delete this.dropTarget.xtype;
50148             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
50149         }
50150         
50151         
50152         this.rendered = true;
50153         this.fireEvent('render', this);
50154         return this;
50155     },
50156
50157         /**
50158          * Reconfigures the grid to use a different Store and Column Model.
50159          * The View will be bound to the new objects and refreshed.
50160          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
50161          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
50162          */
50163     reconfigure : function(dataSource, colModel){
50164         if(this.loadMask){
50165             this.loadMask.destroy();
50166             this.loadMask = new Roo.LoadMask(this.container,
50167                     Roo.apply({store:dataSource}, this.loadMask));
50168         }
50169         this.view.bind(dataSource, colModel);
50170         this.dataSource = dataSource;
50171         this.colModel = colModel;
50172         this.view.refresh(true);
50173     },
50174
50175     // private
50176     onKeyDown : function(e){
50177         this.fireEvent("keydown", e);
50178     },
50179
50180     /**
50181      * Destroy this grid.
50182      * @param {Boolean} removeEl True to remove the element
50183      */
50184     destroy : function(removeEl, keepListeners){
50185         if(this.loadMask){
50186             this.loadMask.destroy();
50187         }
50188         var c = this.container;
50189         c.removeAllListeners();
50190         this.view.destroy();
50191         this.colModel.purgeListeners();
50192         if(!keepListeners){
50193             this.purgeListeners();
50194         }
50195         c.update("");
50196         if(removeEl === true){
50197             c.remove();
50198         }
50199     },
50200
50201     // private
50202     processEvent : function(name, e){
50203         this.fireEvent(name, e);
50204         var t = e.getTarget();
50205         var v = this.view;
50206         var header = v.findHeaderIndex(t);
50207         if(header !== false){
50208             this.fireEvent("header" + name, this, header, e);
50209         }else{
50210             var row = v.findRowIndex(t);
50211             var cell = v.findCellIndex(t);
50212             if(row !== false){
50213                 this.fireEvent("row" + name, this, row, e);
50214                 if(cell !== false){
50215                     this.fireEvent("cell" + name, this, row, cell, e);
50216                 }
50217             }
50218         }
50219     },
50220
50221     // private
50222     onClick : function(e){
50223         this.processEvent("click", e);
50224     },
50225
50226     // private
50227     onContextMenu : function(e, t){
50228         this.processEvent("contextmenu", e);
50229     },
50230
50231     // private
50232     onDblClick : function(e){
50233         this.processEvent("dblclick", e);
50234     },
50235
50236     // private
50237     walkCells : function(row, col, step, fn, scope){
50238         var cm = this.colModel, clen = cm.getColumnCount();
50239         var ds = this.dataSource, rlen = ds.getCount(), first = true;
50240         if(step < 0){
50241             if(col < 0){
50242                 row--;
50243                 first = false;
50244             }
50245             while(row >= 0){
50246                 if(!first){
50247                     col = clen-1;
50248                 }
50249                 first = false;
50250                 while(col >= 0){
50251                     if(fn.call(scope || this, row, col, cm) === true){
50252                         return [row, col];
50253                     }
50254                     col--;
50255                 }
50256                 row--;
50257             }
50258         } else {
50259             if(col >= clen){
50260                 row++;
50261                 first = false;
50262             }
50263             while(row < rlen){
50264                 if(!first){
50265                     col = 0;
50266                 }
50267                 first = false;
50268                 while(col < clen){
50269                     if(fn.call(scope || this, row, col, cm) === true){
50270                         return [row, col];
50271                     }
50272                     col++;
50273                 }
50274                 row++;
50275             }
50276         }
50277         return null;
50278     },
50279
50280     // private
50281     getSelections : function(){
50282         return this.selModel.getSelections();
50283     },
50284
50285     /**
50286      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
50287      * but if manual update is required this method will initiate it.
50288      */
50289     autoSize : function(){
50290         if(this.rendered){
50291             this.view.layout();
50292             if(this.view.adjustForScroll){
50293                 this.view.adjustForScroll();
50294             }
50295         }
50296     },
50297
50298     /**
50299      * Returns the grid's underlying element.
50300      * @return {Element} The element
50301      */
50302     getGridEl : function(){
50303         return this.container;
50304     },
50305
50306     // private for compatibility, overridden by editor grid
50307     stopEditing : function(){},
50308
50309     /**
50310      * Returns the grid's SelectionModel.
50311      * @return {SelectionModel}
50312      */
50313     getSelectionModel : function(){
50314         if(!this.selModel){
50315             this.selModel = new Roo.grid.RowSelectionModel();
50316         }
50317         return this.selModel;
50318     },
50319
50320     /**
50321      * Returns the grid's DataSource.
50322      * @return {DataSource}
50323      */
50324     getDataSource : function(){
50325         return this.dataSource;
50326     },
50327
50328     /**
50329      * Returns the grid's ColumnModel.
50330      * @return {ColumnModel}
50331      */
50332     getColumnModel : function(){
50333         return this.colModel;
50334     },
50335
50336     /**
50337      * Returns the grid's GridView object.
50338      * @return {GridView}
50339      */
50340     getView : function(){
50341         if(!this.view){
50342             this.view = new Roo.grid.GridView(this.viewConfig);
50343         }
50344         return this.view;
50345     },
50346     /**
50347      * Called to get grid's drag proxy text, by default returns this.ddText.
50348      * @return {String}
50349      */
50350     getDragDropText : function(){
50351         var count = this.selModel.getCount();
50352         return String.format(this.ddText, count, count == 1 ? '' : 's');
50353     }
50354 });
50355 /**
50356  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
50357  * %0 is replaced with the number of selected rows.
50358  * @type String
50359  */
50360 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
50361  * Based on:
50362  * Ext JS Library 1.1.1
50363  * Copyright(c) 2006-2007, Ext JS, LLC.
50364  *
50365  * Originally Released Under LGPL - original licence link has changed is not relivant.
50366  *
50367  * Fork - LGPL
50368  * <script type="text/javascript">
50369  */
50370  
50371 Roo.grid.AbstractGridView = function(){
50372         this.grid = null;
50373         
50374         this.events = {
50375             "beforerowremoved" : true,
50376             "beforerowsinserted" : true,
50377             "beforerefresh" : true,
50378             "rowremoved" : true,
50379             "rowsinserted" : true,
50380             "rowupdated" : true,
50381             "refresh" : true
50382         };
50383     Roo.grid.AbstractGridView.superclass.constructor.call(this);
50384 };
50385
50386 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
50387     rowClass : "x-grid-row",
50388     cellClass : "x-grid-cell",
50389     tdClass : "x-grid-td",
50390     hdClass : "x-grid-hd",
50391     splitClass : "x-grid-hd-split",
50392     
50393         init: function(grid){
50394         this.grid = grid;
50395                 var cid = this.grid.getGridEl().id;
50396         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
50397         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
50398         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
50399         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
50400         },
50401         
50402         getColumnRenderers : function(){
50403         var renderers = [];
50404         var cm = this.grid.colModel;
50405         var colCount = cm.getColumnCount();
50406         for(var i = 0; i < colCount; i++){
50407             renderers[i] = cm.getRenderer(i);
50408         }
50409         return renderers;
50410     },
50411     
50412     getColumnIds : function(){
50413         var ids = [];
50414         var cm = this.grid.colModel;
50415         var colCount = cm.getColumnCount();
50416         for(var i = 0; i < colCount; i++){
50417             ids[i] = cm.getColumnId(i);
50418         }
50419         return ids;
50420     },
50421     
50422     getDataIndexes : function(){
50423         if(!this.indexMap){
50424             this.indexMap = this.buildIndexMap();
50425         }
50426         return this.indexMap.colToData;
50427     },
50428     
50429     getColumnIndexByDataIndex : function(dataIndex){
50430         if(!this.indexMap){
50431             this.indexMap = this.buildIndexMap();
50432         }
50433         return this.indexMap.dataToCol[dataIndex];
50434     },
50435     
50436     /**
50437      * Set a css style for a column dynamically. 
50438      * @param {Number} colIndex The index of the column
50439      * @param {String} name The css property name
50440      * @param {String} value The css value
50441      */
50442     setCSSStyle : function(colIndex, name, value){
50443         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
50444         Roo.util.CSS.updateRule(selector, name, value);
50445     },
50446     
50447     generateRules : function(cm){
50448         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
50449         Roo.util.CSS.removeStyleSheet(rulesId);
50450         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
50451             var cid = cm.getColumnId(i);
50452             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
50453                          this.tdSelector, cid, " {\n}\n",
50454                          this.hdSelector, cid, " {\n}\n",
50455                          this.splitSelector, cid, " {\n}\n");
50456         }
50457         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
50458     }
50459 });/*
50460  * Based on:
50461  * Ext JS Library 1.1.1
50462  * Copyright(c) 2006-2007, Ext JS, LLC.
50463  *
50464  * Originally Released Under LGPL - original licence link has changed is not relivant.
50465  *
50466  * Fork - LGPL
50467  * <script type="text/javascript">
50468  */
50469
50470 // private
50471 // This is a support class used internally by the Grid components
50472 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
50473     this.grid = grid;
50474     this.view = grid.getView();
50475     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
50476     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
50477     if(hd2){
50478         this.setHandleElId(Roo.id(hd));
50479         this.setOuterHandleElId(Roo.id(hd2));
50480     }
50481     this.scroll = false;
50482 };
50483 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
50484     maxDragWidth: 120,
50485     getDragData : function(e){
50486         var t = Roo.lib.Event.getTarget(e);
50487         var h = this.view.findHeaderCell(t);
50488         if(h){
50489             return {ddel: h.firstChild, header:h};
50490         }
50491         return false;
50492     },
50493
50494     onInitDrag : function(e){
50495         this.view.headersDisabled = true;
50496         var clone = this.dragData.ddel.cloneNode(true);
50497         clone.id = Roo.id();
50498         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
50499         this.proxy.update(clone);
50500         return true;
50501     },
50502
50503     afterValidDrop : function(){
50504         var v = this.view;
50505         setTimeout(function(){
50506             v.headersDisabled = false;
50507         }, 50);
50508     },
50509
50510     afterInvalidDrop : function(){
50511         var v = this.view;
50512         setTimeout(function(){
50513             v.headersDisabled = false;
50514         }, 50);
50515     }
50516 });
50517 /*
50518  * Based on:
50519  * Ext JS Library 1.1.1
50520  * Copyright(c) 2006-2007, Ext JS, LLC.
50521  *
50522  * Originally Released Under LGPL - original licence link has changed is not relivant.
50523  *
50524  * Fork - LGPL
50525  * <script type="text/javascript">
50526  */
50527 // private
50528 // This is a support class used internally by the Grid components
50529 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
50530     this.grid = grid;
50531     this.view = grid.getView();
50532     // split the proxies so they don't interfere with mouse events
50533     this.proxyTop = Roo.DomHelper.append(document.body, {
50534         cls:"col-move-top", html:"&#160;"
50535     }, true);
50536     this.proxyBottom = Roo.DomHelper.append(document.body, {
50537         cls:"col-move-bottom", html:"&#160;"
50538     }, true);
50539     this.proxyTop.hide = this.proxyBottom.hide = function(){
50540         this.setLeftTop(-100,-100);
50541         this.setStyle("visibility", "hidden");
50542     };
50543     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
50544     // temporarily disabled
50545     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
50546     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
50547 };
50548 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
50549     proxyOffsets : [-4, -9],
50550     fly: Roo.Element.fly,
50551
50552     getTargetFromEvent : function(e){
50553         var t = Roo.lib.Event.getTarget(e);
50554         var cindex = this.view.findCellIndex(t);
50555         if(cindex !== false){
50556             return this.view.getHeaderCell(cindex);
50557         }
50558         return null;
50559     },
50560
50561     nextVisible : function(h){
50562         var v = this.view, cm = this.grid.colModel;
50563         h = h.nextSibling;
50564         while(h){
50565             if(!cm.isHidden(v.getCellIndex(h))){
50566                 return h;
50567             }
50568             h = h.nextSibling;
50569         }
50570         return null;
50571     },
50572
50573     prevVisible : function(h){
50574         var v = this.view, cm = this.grid.colModel;
50575         h = h.prevSibling;
50576         while(h){
50577             if(!cm.isHidden(v.getCellIndex(h))){
50578                 return h;
50579             }
50580             h = h.prevSibling;
50581         }
50582         return null;
50583     },
50584
50585     positionIndicator : function(h, n, e){
50586         var x = Roo.lib.Event.getPageX(e);
50587         var r = Roo.lib.Dom.getRegion(n.firstChild);
50588         var px, pt, py = r.top + this.proxyOffsets[1];
50589         if((r.right - x) <= (r.right-r.left)/2){
50590             px = r.right+this.view.borderWidth;
50591             pt = "after";
50592         }else{
50593             px = r.left;
50594             pt = "before";
50595         }
50596         var oldIndex = this.view.getCellIndex(h);
50597         var newIndex = this.view.getCellIndex(n);
50598
50599         if(this.grid.colModel.isFixed(newIndex)){
50600             return false;
50601         }
50602
50603         var locked = this.grid.colModel.isLocked(newIndex);
50604
50605         if(pt == "after"){
50606             newIndex++;
50607         }
50608         if(oldIndex < newIndex){
50609             newIndex--;
50610         }
50611         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
50612             return false;
50613         }
50614         px +=  this.proxyOffsets[0];
50615         this.proxyTop.setLeftTop(px, py);
50616         this.proxyTop.show();
50617         if(!this.bottomOffset){
50618             this.bottomOffset = this.view.mainHd.getHeight();
50619         }
50620         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
50621         this.proxyBottom.show();
50622         return pt;
50623     },
50624
50625     onNodeEnter : function(n, dd, e, data){
50626         if(data.header != n){
50627             this.positionIndicator(data.header, n, e);
50628         }
50629     },
50630
50631     onNodeOver : function(n, dd, e, data){
50632         var result = false;
50633         if(data.header != n){
50634             result = this.positionIndicator(data.header, n, e);
50635         }
50636         if(!result){
50637             this.proxyTop.hide();
50638             this.proxyBottom.hide();
50639         }
50640         return result ? this.dropAllowed : this.dropNotAllowed;
50641     },
50642
50643     onNodeOut : function(n, dd, e, data){
50644         this.proxyTop.hide();
50645         this.proxyBottom.hide();
50646     },
50647
50648     onNodeDrop : function(n, dd, e, data){
50649         var h = data.header;
50650         if(h != n){
50651             var cm = this.grid.colModel;
50652             var x = Roo.lib.Event.getPageX(e);
50653             var r = Roo.lib.Dom.getRegion(n.firstChild);
50654             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
50655             var oldIndex = this.view.getCellIndex(h);
50656             var newIndex = this.view.getCellIndex(n);
50657             var locked = cm.isLocked(newIndex);
50658             if(pt == "after"){
50659                 newIndex++;
50660             }
50661             if(oldIndex < newIndex){
50662                 newIndex--;
50663             }
50664             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
50665                 return false;
50666             }
50667             cm.setLocked(oldIndex, locked, true);
50668             cm.moveColumn(oldIndex, newIndex);
50669             this.grid.fireEvent("columnmove", oldIndex, newIndex);
50670             return true;
50671         }
50672         return false;
50673     }
50674 });
50675 /*
50676  * Based on:
50677  * Ext JS Library 1.1.1
50678  * Copyright(c) 2006-2007, Ext JS, LLC.
50679  *
50680  * Originally Released Under LGPL - original licence link has changed is not relivant.
50681  *
50682  * Fork - LGPL
50683  * <script type="text/javascript">
50684  */
50685   
50686 /**
50687  * @class Roo.grid.GridView
50688  * @extends Roo.util.Observable
50689  *
50690  * @constructor
50691  * @param {Object} config
50692  */
50693 Roo.grid.GridView = function(config){
50694     Roo.grid.GridView.superclass.constructor.call(this);
50695     this.el = null;
50696
50697     Roo.apply(this, config);
50698 };
50699
50700 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
50701
50702     unselectable :  'unselectable="on"',
50703     unselectableCls :  'x-unselectable',
50704     
50705     
50706     rowClass : "x-grid-row",
50707
50708     cellClass : "x-grid-col",
50709
50710     tdClass : "x-grid-td",
50711
50712     hdClass : "x-grid-hd",
50713
50714     splitClass : "x-grid-split",
50715
50716     sortClasses : ["sort-asc", "sort-desc"],
50717
50718     enableMoveAnim : false,
50719
50720     hlColor: "C3DAF9",
50721
50722     dh : Roo.DomHelper,
50723
50724     fly : Roo.Element.fly,
50725
50726     css : Roo.util.CSS,
50727
50728     borderWidth: 1,
50729
50730     splitOffset: 3,
50731
50732     scrollIncrement : 22,
50733
50734     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
50735
50736     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
50737
50738     bind : function(ds, cm){
50739         if(this.ds){
50740             this.ds.un("load", this.onLoad, this);
50741             this.ds.un("datachanged", this.onDataChange, this);
50742             this.ds.un("add", this.onAdd, this);
50743             this.ds.un("remove", this.onRemove, this);
50744             this.ds.un("update", this.onUpdate, this);
50745             this.ds.un("clear", this.onClear, this);
50746         }
50747         if(ds){
50748             ds.on("load", this.onLoad, this);
50749             ds.on("datachanged", this.onDataChange, this);
50750             ds.on("add", this.onAdd, this);
50751             ds.on("remove", this.onRemove, this);
50752             ds.on("update", this.onUpdate, this);
50753             ds.on("clear", this.onClear, this);
50754         }
50755         this.ds = ds;
50756
50757         if(this.cm){
50758             this.cm.un("widthchange", this.onColWidthChange, this);
50759             this.cm.un("headerchange", this.onHeaderChange, this);
50760             this.cm.un("hiddenchange", this.onHiddenChange, this);
50761             this.cm.un("columnmoved", this.onColumnMove, this);
50762             this.cm.un("columnlockchange", this.onColumnLock, this);
50763         }
50764         if(cm){
50765             this.generateRules(cm);
50766             cm.on("widthchange", this.onColWidthChange, this);
50767             cm.on("headerchange", this.onHeaderChange, this);
50768             cm.on("hiddenchange", this.onHiddenChange, this);
50769             cm.on("columnmoved", this.onColumnMove, this);
50770             cm.on("columnlockchange", this.onColumnLock, this);
50771         }
50772         this.cm = cm;
50773     },
50774
50775     init: function(grid){
50776         Roo.grid.GridView.superclass.init.call(this, grid);
50777
50778         this.bind(grid.dataSource, grid.colModel);
50779
50780         grid.on("headerclick", this.handleHeaderClick, this);
50781
50782         if(grid.trackMouseOver){
50783             grid.on("mouseover", this.onRowOver, this);
50784             grid.on("mouseout", this.onRowOut, this);
50785         }
50786         grid.cancelTextSelection = function(){};
50787         this.gridId = grid.id;
50788
50789         var tpls = this.templates || {};
50790
50791         if(!tpls.master){
50792             tpls.master = new Roo.Template(
50793                '<div class="x-grid" hidefocus="true">',
50794                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
50795                   '<div class="x-grid-topbar"></div>',
50796                   '<div class="x-grid-scroller"><div></div></div>',
50797                   '<div class="x-grid-locked">',
50798                       '<div class="x-grid-header">{lockedHeader}</div>',
50799                       '<div class="x-grid-body">{lockedBody}</div>',
50800                   "</div>",
50801                   '<div class="x-grid-viewport">',
50802                       '<div class="x-grid-header">{header}</div>',
50803                       '<div class="x-grid-body">{body}</div>',
50804                   "</div>",
50805                   '<div class="x-grid-bottombar"></div>',
50806                  
50807                   '<div class="x-grid-resize-proxy">&#160;</div>',
50808                "</div>"
50809             );
50810             tpls.master.disableformats = true;
50811         }
50812
50813         if(!tpls.header){
50814             tpls.header = new Roo.Template(
50815                '<table border="0" cellspacing="0" cellpadding="0">',
50816                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
50817                "</table>{splits}"
50818             );
50819             tpls.header.disableformats = true;
50820         }
50821         tpls.header.compile();
50822
50823         if(!tpls.hcell){
50824             tpls.hcell = new Roo.Template(
50825                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
50826                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
50827                 "</div></td>"
50828              );
50829              tpls.hcell.disableFormats = true;
50830         }
50831         tpls.hcell.compile();
50832
50833         if(!tpls.hsplit){
50834             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
50835                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
50836             tpls.hsplit.disableFormats = true;
50837         }
50838         tpls.hsplit.compile();
50839
50840         if(!tpls.body){
50841             tpls.body = new Roo.Template(
50842                '<table border="0" cellspacing="0" cellpadding="0">',
50843                "<tbody>{rows}</tbody>",
50844                "</table>"
50845             );
50846             tpls.body.disableFormats = true;
50847         }
50848         tpls.body.compile();
50849
50850         if(!tpls.row){
50851             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
50852             tpls.row.disableFormats = true;
50853         }
50854         tpls.row.compile();
50855
50856         if(!tpls.cell){
50857             tpls.cell = new Roo.Template(
50858                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
50859                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
50860                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
50861                 "</td>"
50862             );
50863             tpls.cell.disableFormats = true;
50864         }
50865         tpls.cell.compile();
50866
50867         this.templates = tpls;
50868     },
50869
50870     // remap these for backwards compat
50871     onColWidthChange : function(){
50872         this.updateColumns.apply(this, arguments);
50873     },
50874     onHeaderChange : function(){
50875         this.updateHeaders.apply(this, arguments);
50876     }, 
50877     onHiddenChange : function(){
50878         this.handleHiddenChange.apply(this, arguments);
50879     },
50880     onColumnMove : function(){
50881         this.handleColumnMove.apply(this, arguments);
50882     },
50883     onColumnLock : function(){
50884         this.handleLockChange.apply(this, arguments);
50885     },
50886
50887     onDataChange : function(){
50888         this.refresh();
50889         this.updateHeaderSortState();
50890     },
50891
50892     onClear : function(){
50893         this.refresh();
50894     },
50895
50896     onUpdate : function(ds, record){
50897         this.refreshRow(record);
50898     },
50899
50900     refreshRow : function(record){
50901         var ds = this.ds, index;
50902         if(typeof record == 'number'){
50903             index = record;
50904             record = ds.getAt(index);
50905         }else{
50906             index = ds.indexOf(record);
50907         }
50908         this.insertRows(ds, index, index, true);
50909         this.onRemove(ds, record, index+1, true);
50910         this.syncRowHeights(index, index);
50911         this.layout();
50912         this.fireEvent("rowupdated", this, index, record);
50913     },
50914
50915     onAdd : function(ds, records, index){
50916         this.insertRows(ds, index, index + (records.length-1));
50917     },
50918
50919     onRemove : function(ds, record, index, isUpdate){
50920         if(isUpdate !== true){
50921             this.fireEvent("beforerowremoved", this, index, record);
50922         }
50923         var bt = this.getBodyTable(), lt = this.getLockedTable();
50924         if(bt.rows[index]){
50925             bt.firstChild.removeChild(bt.rows[index]);
50926         }
50927         if(lt.rows[index]){
50928             lt.firstChild.removeChild(lt.rows[index]);
50929         }
50930         if(isUpdate !== true){
50931             this.stripeRows(index);
50932             this.syncRowHeights(index, index);
50933             this.layout();
50934             this.fireEvent("rowremoved", this, index, record);
50935         }
50936     },
50937
50938     onLoad : function(){
50939         this.scrollToTop();
50940     },
50941
50942     /**
50943      * Scrolls the grid to the top
50944      */
50945     scrollToTop : function(){
50946         if(this.scroller){
50947             this.scroller.dom.scrollTop = 0;
50948             this.syncScroll();
50949         }
50950     },
50951
50952     /**
50953      * Gets a panel in the header of the grid that can be used for toolbars etc.
50954      * After modifying the contents of this panel a call to grid.autoSize() may be
50955      * required to register any changes in size.
50956      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
50957      * @return Roo.Element
50958      */
50959     getHeaderPanel : function(doShow){
50960         if(doShow){
50961             this.headerPanel.show();
50962         }
50963         return this.headerPanel;
50964     },
50965
50966     /**
50967      * Gets a panel in the footer of the grid that can be used for toolbars etc.
50968      * After modifying the contents of this panel a call to grid.autoSize() may be
50969      * required to register any changes in size.
50970      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
50971      * @return Roo.Element
50972      */
50973     getFooterPanel : function(doShow){
50974         if(doShow){
50975             this.footerPanel.show();
50976         }
50977         return this.footerPanel;
50978     },
50979
50980     initElements : function(){
50981         var E = Roo.Element;
50982         var el = this.grid.getGridEl().dom.firstChild;
50983         var cs = el.childNodes;
50984
50985         this.el = new E(el);
50986         
50987          this.focusEl = new E(el.firstChild);
50988         this.focusEl.swallowEvent("click", true);
50989         
50990         this.headerPanel = new E(cs[1]);
50991         this.headerPanel.enableDisplayMode("block");
50992
50993         this.scroller = new E(cs[2]);
50994         this.scrollSizer = new E(this.scroller.dom.firstChild);
50995
50996         this.lockedWrap = new E(cs[3]);
50997         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
50998         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
50999
51000         this.mainWrap = new E(cs[4]);
51001         this.mainHd = new E(this.mainWrap.dom.firstChild);
51002         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
51003
51004         this.footerPanel = new E(cs[5]);
51005         this.footerPanel.enableDisplayMode("block");
51006
51007         this.resizeProxy = new E(cs[6]);
51008
51009         this.headerSelector = String.format(
51010            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
51011            this.lockedHd.id, this.mainHd.id
51012         );
51013
51014         this.splitterSelector = String.format(
51015            '#{0} div.x-grid-split, #{1} div.x-grid-split',
51016            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
51017         );
51018     },
51019     idToCssName : function(s)
51020     {
51021         return s.replace(/[^a-z0-9]+/ig, '-');
51022     },
51023
51024     getHeaderCell : function(index){
51025         return Roo.DomQuery.select(this.headerSelector)[index];
51026     },
51027
51028     getHeaderCellMeasure : function(index){
51029         return this.getHeaderCell(index).firstChild;
51030     },
51031
51032     getHeaderCellText : function(index){
51033         return this.getHeaderCell(index).firstChild.firstChild;
51034     },
51035
51036     getLockedTable : function(){
51037         return this.lockedBody.dom.firstChild;
51038     },
51039
51040     getBodyTable : function(){
51041         return this.mainBody.dom.firstChild;
51042     },
51043
51044     getLockedRow : function(index){
51045         return this.getLockedTable().rows[index];
51046     },
51047
51048     getRow : function(index){
51049         return this.getBodyTable().rows[index];
51050     },
51051
51052     getRowComposite : function(index){
51053         if(!this.rowEl){
51054             this.rowEl = new Roo.CompositeElementLite();
51055         }
51056         var els = [], lrow, mrow;
51057         if(lrow = this.getLockedRow(index)){
51058             els.push(lrow);
51059         }
51060         if(mrow = this.getRow(index)){
51061             els.push(mrow);
51062         }
51063         this.rowEl.elements = els;
51064         return this.rowEl;
51065     },
51066     /**
51067      * Gets the 'td' of the cell
51068      * 
51069      * @param {Integer} rowIndex row to select
51070      * @param {Integer} colIndex column to select
51071      * 
51072      * @return {Object} 
51073      */
51074     getCell : function(rowIndex, colIndex){
51075         var locked = this.cm.getLockedCount();
51076         var source;
51077         if(colIndex < locked){
51078             source = this.lockedBody.dom.firstChild;
51079         }else{
51080             source = this.mainBody.dom.firstChild;
51081             colIndex -= locked;
51082         }
51083         return source.rows[rowIndex].childNodes[colIndex];
51084     },
51085
51086     getCellText : function(rowIndex, colIndex){
51087         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
51088     },
51089
51090     getCellBox : function(cell){
51091         var b = this.fly(cell).getBox();
51092         if(Roo.isOpera){ // opera fails to report the Y
51093             b.y = cell.offsetTop + this.mainBody.getY();
51094         }
51095         return b;
51096     },
51097
51098     getCellIndex : function(cell){
51099         var id = String(cell.className).match(this.cellRE);
51100         if(id){
51101             return parseInt(id[1], 10);
51102         }
51103         return 0;
51104     },
51105
51106     findHeaderIndex : function(n){
51107         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
51108         return r ? this.getCellIndex(r) : false;
51109     },
51110
51111     findHeaderCell : function(n){
51112         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
51113         return r ? r : false;
51114     },
51115
51116     findRowIndex : function(n){
51117         if(!n){
51118             return false;
51119         }
51120         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
51121         return r ? r.rowIndex : false;
51122     },
51123
51124     findCellIndex : function(node){
51125         var stop = this.el.dom;
51126         while(node && node != stop){
51127             if(this.findRE.test(node.className)){
51128                 return this.getCellIndex(node);
51129             }
51130             node = node.parentNode;
51131         }
51132         return false;
51133     },
51134
51135     getColumnId : function(index){
51136         return this.cm.getColumnId(index);
51137     },
51138
51139     getSplitters : function()
51140     {
51141         if(this.splitterSelector){
51142            return Roo.DomQuery.select(this.splitterSelector);
51143         }else{
51144             return null;
51145       }
51146     },
51147
51148     getSplitter : function(index){
51149         return this.getSplitters()[index];
51150     },
51151
51152     onRowOver : function(e, t){
51153         var row;
51154         if((row = this.findRowIndex(t)) !== false){
51155             this.getRowComposite(row).addClass("x-grid-row-over");
51156         }
51157     },
51158
51159     onRowOut : function(e, t){
51160         var row;
51161         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
51162             this.getRowComposite(row).removeClass("x-grid-row-over");
51163         }
51164     },
51165
51166     renderHeaders : function(){
51167         var cm = this.cm;
51168         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
51169         var cb = [], lb = [], sb = [], lsb = [], p = {};
51170         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51171             p.cellId = "x-grid-hd-0-" + i;
51172             p.splitId = "x-grid-csplit-0-" + i;
51173             p.id = cm.getColumnId(i);
51174             p.title = cm.getColumnTooltip(i) || "";
51175             p.value = cm.getColumnHeader(i) || "";
51176             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
51177             if(!cm.isLocked(i)){
51178                 cb[cb.length] = ct.apply(p);
51179                 sb[sb.length] = st.apply(p);
51180             }else{
51181                 lb[lb.length] = ct.apply(p);
51182                 lsb[lsb.length] = st.apply(p);
51183             }
51184         }
51185         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
51186                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
51187     },
51188
51189     updateHeaders : function(){
51190         var html = this.renderHeaders();
51191         this.lockedHd.update(html[0]);
51192         this.mainHd.update(html[1]);
51193     },
51194
51195     /**
51196      * Focuses the specified row.
51197      * @param {Number} row The row index
51198      */
51199     focusRow : function(row)
51200     {
51201         //Roo.log('GridView.focusRow');
51202         var x = this.scroller.dom.scrollLeft;
51203         this.focusCell(row, 0, false);
51204         this.scroller.dom.scrollLeft = x;
51205     },
51206
51207     /**
51208      * Focuses the specified cell.
51209      * @param {Number} row The row index
51210      * @param {Number} col The column index
51211      * @param {Boolean} hscroll false to disable horizontal scrolling
51212      */
51213     focusCell : function(row, col, hscroll)
51214     {
51215         //Roo.log('GridView.focusCell');
51216         var el = this.ensureVisible(row, col, hscroll);
51217         this.focusEl.alignTo(el, "tl-tl");
51218         if(Roo.isGecko){
51219             this.focusEl.focus();
51220         }else{
51221             this.focusEl.focus.defer(1, this.focusEl);
51222         }
51223     },
51224
51225     /**
51226      * Scrolls the specified cell into view
51227      * @param {Number} row The row index
51228      * @param {Number} col The column index
51229      * @param {Boolean} hscroll false to disable horizontal scrolling
51230      */
51231     ensureVisible : function(row, col, hscroll)
51232     {
51233         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
51234         //return null; //disable for testing.
51235         if(typeof row != "number"){
51236             row = row.rowIndex;
51237         }
51238         if(row < 0 && row >= this.ds.getCount()){
51239             return  null;
51240         }
51241         col = (col !== undefined ? col : 0);
51242         var cm = this.grid.colModel;
51243         while(cm.isHidden(col)){
51244             col++;
51245         }
51246
51247         var el = this.getCell(row, col);
51248         if(!el){
51249             return null;
51250         }
51251         var c = this.scroller.dom;
51252
51253         var ctop = parseInt(el.offsetTop, 10);
51254         var cleft = parseInt(el.offsetLeft, 10);
51255         var cbot = ctop + el.offsetHeight;
51256         var cright = cleft + el.offsetWidth;
51257         
51258         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
51259         var stop = parseInt(c.scrollTop, 10);
51260         var sleft = parseInt(c.scrollLeft, 10);
51261         var sbot = stop + ch;
51262         var sright = sleft + c.clientWidth;
51263         /*
51264         Roo.log('GridView.ensureVisible:' +
51265                 ' ctop:' + ctop +
51266                 ' c.clientHeight:' + c.clientHeight +
51267                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
51268                 ' stop:' + stop +
51269                 ' cbot:' + cbot +
51270                 ' sbot:' + sbot +
51271                 ' ch:' + ch  
51272                 );
51273         */
51274         if(ctop < stop){
51275              c.scrollTop = ctop;
51276             //Roo.log("set scrolltop to ctop DISABLE?");
51277         }else if(cbot > sbot){
51278             //Roo.log("set scrolltop to cbot-ch");
51279             c.scrollTop = cbot-ch;
51280         }
51281         
51282         if(hscroll !== false){
51283             if(cleft < sleft){
51284                 c.scrollLeft = cleft;
51285             }else if(cright > sright){
51286                 c.scrollLeft = cright-c.clientWidth;
51287             }
51288         }
51289          
51290         return el;
51291     },
51292
51293     updateColumns : function(){
51294         this.grid.stopEditing();
51295         var cm = this.grid.colModel, colIds = this.getColumnIds();
51296         //var totalWidth = cm.getTotalWidth();
51297         var pos = 0;
51298         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51299             //if(cm.isHidden(i)) continue;
51300             var w = cm.getColumnWidth(i);
51301             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
51302             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
51303         }
51304         this.updateSplitters();
51305     },
51306
51307     generateRules : function(cm){
51308         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
51309         Roo.util.CSS.removeStyleSheet(rulesId);
51310         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51311             var cid = cm.getColumnId(i);
51312             var align = '';
51313             if(cm.config[i].align){
51314                 align = 'text-align:'+cm.config[i].align+';';
51315             }
51316             var hidden = '';
51317             if(cm.isHidden(i)){
51318                 hidden = 'display:none;';
51319             }
51320             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
51321             ruleBuf.push(
51322                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
51323                     this.hdSelector, cid, " {\n", align, width, "}\n",
51324                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
51325                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
51326         }
51327         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
51328     },
51329
51330     updateSplitters : function(){
51331         var cm = this.cm, s = this.getSplitters();
51332         if(s){ // splitters not created yet
51333             var pos = 0, locked = true;
51334             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
51335                 if(cm.isHidden(i)) continue;
51336                 var w = cm.getColumnWidth(i); // make sure it's a number
51337                 if(!cm.isLocked(i) && locked){
51338                     pos = 0;
51339                     locked = false;
51340                 }
51341                 pos += w;
51342                 s[i].style.left = (pos-this.splitOffset) + "px";
51343             }
51344         }
51345     },
51346
51347     handleHiddenChange : function(colModel, colIndex, hidden){
51348         if(hidden){
51349             this.hideColumn(colIndex);
51350         }else{
51351             this.unhideColumn(colIndex);
51352         }
51353     },
51354
51355     hideColumn : function(colIndex){
51356         var cid = this.getColumnId(colIndex);
51357         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
51358         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
51359         if(Roo.isSafari){
51360             this.updateHeaders();
51361         }
51362         this.updateSplitters();
51363         this.layout();
51364     },
51365
51366     unhideColumn : function(colIndex){
51367         var cid = this.getColumnId(colIndex);
51368         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
51369         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
51370
51371         if(Roo.isSafari){
51372             this.updateHeaders();
51373         }
51374         this.updateSplitters();
51375         this.layout();
51376     },
51377
51378     insertRows : function(dm, firstRow, lastRow, isUpdate){
51379         if(firstRow == 0 && lastRow == dm.getCount()-1){
51380             this.refresh();
51381         }else{
51382             if(!isUpdate){
51383                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
51384             }
51385             var s = this.getScrollState();
51386             var markup = this.renderRows(firstRow, lastRow);
51387             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
51388             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
51389             this.restoreScroll(s);
51390             if(!isUpdate){
51391                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
51392                 this.syncRowHeights(firstRow, lastRow);
51393                 this.stripeRows(firstRow);
51394                 this.layout();
51395             }
51396         }
51397     },
51398
51399     bufferRows : function(markup, target, index){
51400         var before = null, trows = target.rows, tbody = target.tBodies[0];
51401         if(index < trows.length){
51402             before = trows[index];
51403         }
51404         var b = document.createElement("div");
51405         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
51406         var rows = b.firstChild.rows;
51407         for(var i = 0, len = rows.length; i < len; i++){
51408             if(before){
51409                 tbody.insertBefore(rows[0], before);
51410             }else{
51411                 tbody.appendChild(rows[0]);
51412             }
51413         }
51414         b.innerHTML = "";
51415         b = null;
51416     },
51417
51418     deleteRows : function(dm, firstRow, lastRow){
51419         if(dm.getRowCount()<1){
51420             this.fireEvent("beforerefresh", this);
51421             this.mainBody.update("");
51422             this.lockedBody.update("");
51423             this.fireEvent("refresh", this);
51424         }else{
51425             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
51426             var bt = this.getBodyTable();
51427             var tbody = bt.firstChild;
51428             var rows = bt.rows;
51429             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
51430                 tbody.removeChild(rows[firstRow]);
51431             }
51432             this.stripeRows(firstRow);
51433             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
51434         }
51435     },
51436
51437     updateRows : function(dataSource, firstRow, lastRow){
51438         var s = this.getScrollState();
51439         this.refresh();
51440         this.restoreScroll(s);
51441     },
51442
51443     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
51444         if(!noRefresh){
51445            this.refresh();
51446         }
51447         this.updateHeaderSortState();
51448     },
51449
51450     getScrollState : function(){
51451         
51452         var sb = this.scroller.dom;
51453         return {left: sb.scrollLeft, top: sb.scrollTop};
51454     },
51455
51456     stripeRows : function(startRow){
51457         if(!this.grid.stripeRows || this.ds.getCount() < 1){
51458             return;
51459         }
51460         startRow = startRow || 0;
51461         var rows = this.getBodyTable().rows;
51462         var lrows = this.getLockedTable().rows;
51463         var cls = ' x-grid-row-alt ';
51464         for(var i = startRow, len = rows.length; i < len; i++){
51465             var row = rows[i], lrow = lrows[i];
51466             var isAlt = ((i+1) % 2 == 0);
51467             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
51468             if(isAlt == hasAlt){
51469                 continue;
51470             }
51471             if(isAlt){
51472                 row.className += " x-grid-row-alt";
51473             }else{
51474                 row.className = row.className.replace("x-grid-row-alt", "");
51475             }
51476             if(lrow){
51477                 lrow.className = row.className;
51478             }
51479         }
51480     },
51481
51482     restoreScroll : function(state){
51483         //Roo.log('GridView.restoreScroll');
51484         var sb = this.scroller.dom;
51485         sb.scrollLeft = state.left;
51486         sb.scrollTop = state.top;
51487         this.syncScroll();
51488     },
51489
51490     syncScroll : function(){
51491         //Roo.log('GridView.syncScroll');
51492         var sb = this.scroller.dom;
51493         var sh = this.mainHd.dom;
51494         var bs = this.mainBody.dom;
51495         var lv = this.lockedBody.dom;
51496         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
51497         lv.scrollTop = bs.scrollTop = sb.scrollTop;
51498     },
51499
51500     handleScroll : function(e){
51501         this.syncScroll();
51502         var sb = this.scroller.dom;
51503         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
51504         e.stopEvent();
51505     },
51506
51507     handleWheel : function(e){
51508         var d = e.getWheelDelta();
51509         this.scroller.dom.scrollTop -= d*22;
51510         // set this here to prevent jumpy scrolling on large tables
51511         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
51512         e.stopEvent();
51513     },
51514
51515     renderRows : function(startRow, endRow){
51516         // pull in all the crap needed to render rows
51517         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
51518         var colCount = cm.getColumnCount();
51519
51520         if(ds.getCount() < 1){
51521             return ["", ""];
51522         }
51523
51524         // build a map for all the columns
51525         var cs = [];
51526         for(var i = 0; i < colCount; i++){
51527             var name = cm.getDataIndex(i);
51528             cs[i] = {
51529                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
51530                 renderer : cm.getRenderer(i),
51531                 id : cm.getColumnId(i),
51532                 locked : cm.isLocked(i)
51533             };
51534         }
51535
51536         startRow = startRow || 0;
51537         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
51538
51539         // records to render
51540         var rs = ds.getRange(startRow, endRow);
51541
51542         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
51543     },
51544
51545     // As much as I hate to duplicate code, this was branched because FireFox really hates
51546     // [].join("") on strings. The performance difference was substantial enough to
51547     // branch this function
51548     doRender : Roo.isGecko ?
51549             function(cs, rs, ds, startRow, colCount, stripe){
51550                 var ts = this.templates, ct = ts.cell, rt = ts.row;
51551                 // buffers
51552                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
51553                 
51554                 var hasListener = this.grid.hasListener('rowclass');
51555                 var rowcfg = {};
51556                 for(var j = 0, len = rs.length; j < len; j++){
51557                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
51558                     for(var i = 0; i < colCount; i++){
51559                         c = cs[i];
51560                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
51561                         p.id = c.id;
51562                         p.css = p.attr = "";
51563                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
51564                         if(p.value == undefined || p.value === "") p.value = "&#160;";
51565                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
51566                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
51567                         }
51568                         var markup = ct.apply(p);
51569                         if(!c.locked){
51570                             cb+= markup;
51571                         }else{
51572                             lcb+= markup;
51573                         }
51574                     }
51575                     var alt = [];
51576                     if(stripe && ((rowIndex+1) % 2 == 0)){
51577                         alt.push("x-grid-row-alt")
51578                     }
51579                     if(r.dirty){
51580                         alt.push(  " x-grid-dirty-row");
51581                     }
51582                     rp.cells = lcb;
51583                     if(this.getRowClass){
51584                         alt.push(this.getRowClass(r, rowIndex));
51585                     }
51586                     if (hasListener) {
51587                         rowcfg = {
51588                              
51589                             record: r,
51590                             rowIndex : rowIndex,
51591                             rowClass : ''
51592                         }
51593                         this.grid.fireEvent('rowclass', this, rowcfg);
51594                         alt.push(rowcfg.rowClass);
51595                     }
51596                     rp.alt = alt.join(" ");
51597                     lbuf+= rt.apply(rp);
51598                     rp.cells = cb;
51599                     buf+=  rt.apply(rp);
51600                 }
51601                 return [lbuf, buf];
51602             } :
51603             function(cs, rs, ds, startRow, colCount, stripe){
51604                 var ts = this.templates, ct = ts.cell, rt = ts.row;
51605                 // buffers
51606                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
51607                 var hasListener = this.grid.hasListener('rowclass');
51608  
51609                 var rowcfg = {};
51610                 for(var j = 0, len = rs.length; j < len; j++){
51611                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
51612                     for(var i = 0; i < colCount; i++){
51613                         c = cs[i];
51614                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
51615                         p.id = c.id;
51616                         p.css = p.attr = "";
51617                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
51618                         if(p.value == undefined || p.value === "") p.value = "&#160;";
51619                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
51620                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
51621                         }
51622                         
51623                         var markup = ct.apply(p);
51624                         if(!c.locked){
51625                             cb[cb.length] = markup;
51626                         }else{
51627                             lcb[lcb.length] = markup;
51628                         }
51629                     }
51630                     var alt = [];
51631                     if(stripe && ((rowIndex+1) % 2 == 0)){
51632                         alt.push( "x-grid-row-alt");
51633                     }
51634                     if(r.dirty){
51635                         alt.push(" x-grid-dirty-row");
51636                     }
51637                     rp.cells = lcb;
51638                     if(this.getRowClass){
51639                         alt.push( this.getRowClass(r, rowIndex));
51640                     }
51641                     if (hasListener) {
51642                         rowcfg = {
51643                              
51644                             record: r,
51645                             rowIndex : rowIndex,
51646                             rowClass : ''
51647                         }
51648                         this.grid.fireEvent('rowclass', this, rowcfg);
51649                         alt.push(rowcfg.rowClass);
51650                     }
51651                     rp.alt = alt.join(" ");
51652                     rp.cells = lcb.join("");
51653                     lbuf[lbuf.length] = rt.apply(rp);
51654                     rp.cells = cb.join("");
51655                     buf[buf.length] =  rt.apply(rp);
51656                 }
51657                 return [lbuf.join(""), buf.join("")];
51658             },
51659
51660     renderBody : function(){
51661         var markup = this.renderRows();
51662         var bt = this.templates.body;
51663         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
51664     },
51665
51666     /**
51667      * Refreshes the grid
51668      * @param {Boolean} headersToo
51669      */
51670     refresh : function(headersToo){
51671         this.fireEvent("beforerefresh", this);
51672         this.grid.stopEditing();
51673         var result = this.renderBody();
51674         this.lockedBody.update(result[0]);
51675         this.mainBody.update(result[1]);
51676         if(headersToo === true){
51677             this.updateHeaders();
51678             this.updateColumns();
51679             this.updateSplitters();
51680             this.updateHeaderSortState();
51681         }
51682         this.syncRowHeights();
51683         this.layout();
51684         this.fireEvent("refresh", this);
51685     },
51686
51687     handleColumnMove : function(cm, oldIndex, newIndex){
51688         this.indexMap = null;
51689         var s = this.getScrollState();
51690         this.refresh(true);
51691         this.restoreScroll(s);
51692         this.afterMove(newIndex);
51693     },
51694
51695     afterMove : function(colIndex){
51696         if(this.enableMoveAnim && Roo.enableFx){
51697             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
51698         }
51699         // if multisort - fix sortOrder, and reload..
51700         if (this.grid.dataSource.multiSort) {
51701             // the we can call sort again..
51702             var dm = this.grid.dataSource;
51703             var cm = this.grid.colModel;
51704             var so = [];
51705             for(var i = 0; i < cm.config.length; i++ ) {
51706                 
51707                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
51708                     continue; // dont' bother, it's not in sort list or being set.
51709                 }
51710                 
51711                 so.push(cm.config[i].dataIndex);
51712             };
51713             dm.sortOrder = so;
51714             dm.load(dm.lastOptions);
51715             
51716             
51717         }
51718         
51719     },
51720
51721     updateCell : function(dm, rowIndex, dataIndex){
51722         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
51723         if(typeof colIndex == "undefined"){ // not present in grid
51724             return;
51725         }
51726         var cm = this.grid.colModel;
51727         var cell = this.getCell(rowIndex, colIndex);
51728         var cellText = this.getCellText(rowIndex, colIndex);
51729
51730         var p = {
51731             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
51732             id : cm.getColumnId(colIndex),
51733             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
51734         };
51735         var renderer = cm.getRenderer(colIndex);
51736         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
51737         if(typeof val == "undefined" || val === "") val = "&#160;";
51738         cellText.innerHTML = val;
51739         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
51740         this.syncRowHeights(rowIndex, rowIndex);
51741     },
51742
51743     calcColumnWidth : function(colIndex, maxRowsToMeasure){
51744         var maxWidth = 0;
51745         if(this.grid.autoSizeHeaders){
51746             var h = this.getHeaderCellMeasure(colIndex);
51747             maxWidth = Math.max(maxWidth, h.scrollWidth);
51748         }
51749         var tb, index;
51750         if(this.cm.isLocked(colIndex)){
51751             tb = this.getLockedTable();
51752             index = colIndex;
51753         }else{
51754             tb = this.getBodyTable();
51755             index = colIndex - this.cm.getLockedCount();
51756         }
51757         if(tb && tb.rows){
51758             var rows = tb.rows;
51759             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
51760             for(var i = 0; i < stopIndex; i++){
51761                 var cell = rows[i].childNodes[index].firstChild;
51762                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
51763             }
51764         }
51765         return maxWidth + /*margin for error in IE*/ 5;
51766     },
51767     /**
51768      * Autofit a column to its content.
51769      * @param {Number} colIndex
51770      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
51771      */
51772      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
51773          if(this.cm.isHidden(colIndex)){
51774              return; // can't calc a hidden column
51775          }
51776         if(forceMinSize){
51777             var cid = this.cm.getColumnId(colIndex);
51778             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
51779            if(this.grid.autoSizeHeaders){
51780                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
51781            }
51782         }
51783         var newWidth = this.calcColumnWidth(colIndex);
51784         this.cm.setColumnWidth(colIndex,
51785             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
51786         if(!suppressEvent){
51787             this.grid.fireEvent("columnresize", colIndex, newWidth);
51788         }
51789     },
51790
51791     /**
51792      * Autofits all columns to their content and then expands to fit any extra space in the grid
51793      */
51794      autoSizeColumns : function(){
51795         var cm = this.grid.colModel;
51796         var colCount = cm.getColumnCount();
51797         for(var i = 0; i < colCount; i++){
51798             this.autoSizeColumn(i, true, true);
51799         }
51800         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
51801             this.fitColumns();
51802         }else{
51803             this.updateColumns();
51804             this.layout();
51805         }
51806     },
51807
51808     /**
51809      * Autofits all columns to the grid's width proportionate with their current size
51810      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
51811      */
51812     fitColumns : function(reserveScrollSpace){
51813         var cm = this.grid.colModel;
51814         var colCount = cm.getColumnCount();
51815         var cols = [];
51816         var width = 0;
51817         var i, w;
51818         for (i = 0; i < colCount; i++){
51819             if(!cm.isHidden(i) && !cm.isFixed(i)){
51820                 w = cm.getColumnWidth(i);
51821                 cols.push(i);
51822                 cols.push(w);
51823                 width += w;
51824             }
51825         }
51826         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
51827         if(reserveScrollSpace){
51828             avail -= 17;
51829         }
51830         var frac = (avail - cm.getTotalWidth())/width;
51831         while (cols.length){
51832             w = cols.pop();
51833             i = cols.pop();
51834             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
51835         }
51836         this.updateColumns();
51837         this.layout();
51838     },
51839
51840     onRowSelect : function(rowIndex){
51841         var row = this.getRowComposite(rowIndex);
51842         row.addClass("x-grid-row-selected");
51843     },
51844
51845     onRowDeselect : function(rowIndex){
51846         var row = this.getRowComposite(rowIndex);
51847         row.removeClass("x-grid-row-selected");
51848     },
51849
51850     onCellSelect : function(row, col){
51851         var cell = this.getCell(row, col);
51852         if(cell){
51853             Roo.fly(cell).addClass("x-grid-cell-selected");
51854         }
51855     },
51856
51857     onCellDeselect : function(row, col){
51858         var cell = this.getCell(row, col);
51859         if(cell){
51860             Roo.fly(cell).removeClass("x-grid-cell-selected");
51861         }
51862     },
51863
51864     updateHeaderSortState : function(){
51865         
51866         // sort state can be single { field: xxx, direction : yyy}
51867         // or   { xxx=>ASC , yyy : DESC ..... }
51868         
51869         var mstate = {};
51870         if (!this.ds.multiSort) { 
51871             var state = this.ds.getSortState();
51872             if(!state){
51873                 return;
51874             }
51875             mstate[state.field] = state.direction;
51876             // FIXME... - this is not used here.. but might be elsewhere..
51877             this.sortState = state;
51878             
51879         } else {
51880             mstate = this.ds.sortToggle;
51881         }
51882         //remove existing sort classes..
51883         
51884         var sc = this.sortClasses;
51885         var hds = this.el.select(this.headerSelector).removeClass(sc);
51886         
51887         for(var f in mstate) {
51888         
51889             var sortColumn = this.cm.findColumnIndex(f);
51890             
51891             if(sortColumn != -1){
51892                 var sortDir = mstate[f];        
51893                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
51894             }
51895         }
51896         
51897          
51898         
51899     },
51900
51901
51902     handleHeaderClick : function(g, index){
51903         if(this.headersDisabled){
51904             return;
51905         }
51906         var dm = g.dataSource, cm = g.colModel;
51907         if(!cm.isSortable(index)){
51908             return;
51909         }
51910         g.stopEditing();
51911         
51912         if (dm.multiSort) {
51913             // update the sortOrder
51914             var so = [];
51915             for(var i = 0; i < cm.config.length; i++ ) {
51916                 
51917                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
51918                     continue; // dont' bother, it's not in sort list or being set.
51919                 }
51920                 
51921                 so.push(cm.config[i].dataIndex);
51922             };
51923             dm.sortOrder = so;
51924         }
51925         
51926         
51927         dm.sort(cm.getDataIndex(index));
51928     },
51929
51930
51931     destroy : function(){
51932         if(this.colMenu){
51933             this.colMenu.removeAll();
51934             Roo.menu.MenuMgr.unregister(this.colMenu);
51935             this.colMenu.getEl().remove();
51936             delete this.colMenu;
51937         }
51938         if(this.hmenu){
51939             this.hmenu.removeAll();
51940             Roo.menu.MenuMgr.unregister(this.hmenu);
51941             this.hmenu.getEl().remove();
51942             delete this.hmenu;
51943         }
51944         if(this.grid.enableColumnMove){
51945             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
51946             if(dds){
51947                 for(var dd in dds){
51948                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
51949                         var elid = dds[dd].dragElId;
51950                         dds[dd].unreg();
51951                         Roo.get(elid).remove();
51952                     } else if(dds[dd].config.isTarget){
51953                         dds[dd].proxyTop.remove();
51954                         dds[dd].proxyBottom.remove();
51955                         dds[dd].unreg();
51956                     }
51957                     if(Roo.dd.DDM.locationCache[dd]){
51958                         delete Roo.dd.DDM.locationCache[dd];
51959                     }
51960                 }
51961                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
51962             }
51963         }
51964         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
51965         this.bind(null, null);
51966         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
51967     },
51968
51969     handleLockChange : function(){
51970         this.refresh(true);
51971     },
51972
51973     onDenyColumnLock : function(){
51974
51975     },
51976
51977     onDenyColumnHide : function(){
51978
51979     },
51980
51981     handleHdMenuClick : function(item){
51982         var index = this.hdCtxIndex;
51983         var cm = this.cm, ds = this.ds;
51984         switch(item.id){
51985             case "asc":
51986                 ds.sort(cm.getDataIndex(index), "ASC");
51987                 break;
51988             case "desc":
51989                 ds.sort(cm.getDataIndex(index), "DESC");
51990                 break;
51991             case "lock":
51992                 var lc = cm.getLockedCount();
51993                 if(cm.getColumnCount(true) <= lc+1){
51994                     this.onDenyColumnLock();
51995                     return;
51996                 }
51997                 if(lc != index){
51998                     cm.setLocked(index, true, true);
51999                     cm.moveColumn(index, lc);
52000                     this.grid.fireEvent("columnmove", index, lc);
52001                 }else{
52002                     cm.setLocked(index, true);
52003                 }
52004             break;
52005             case "unlock":
52006                 var lc = cm.getLockedCount();
52007                 if((lc-1) != index){
52008                     cm.setLocked(index, false, true);
52009                     cm.moveColumn(index, lc-1);
52010                     this.grid.fireEvent("columnmove", index, lc-1);
52011                 }else{
52012                     cm.setLocked(index, false);
52013                 }
52014             break;
52015             default:
52016                 index = cm.getIndexById(item.id.substr(4));
52017                 if(index != -1){
52018                     if(item.checked && cm.getColumnCount(true) <= 1){
52019                         this.onDenyColumnHide();
52020                         return false;
52021                     }
52022                     cm.setHidden(index, item.checked);
52023                 }
52024         }
52025         return true;
52026     },
52027
52028     beforeColMenuShow : function(){
52029         var cm = this.cm,  colCount = cm.getColumnCount();
52030         this.colMenu.removeAll();
52031         for(var i = 0; i < colCount; i++){
52032             this.colMenu.add(new Roo.menu.CheckItem({
52033                 id: "col-"+cm.getColumnId(i),
52034                 text: cm.getColumnHeader(i),
52035                 checked: !cm.isHidden(i),
52036                 hideOnClick:false
52037             }));
52038         }
52039     },
52040
52041     handleHdCtx : function(g, index, e){
52042         e.stopEvent();
52043         var hd = this.getHeaderCell(index);
52044         this.hdCtxIndex = index;
52045         var ms = this.hmenu.items, cm = this.cm;
52046         ms.get("asc").setDisabled(!cm.isSortable(index));
52047         ms.get("desc").setDisabled(!cm.isSortable(index));
52048         if(this.grid.enableColLock !== false){
52049             ms.get("lock").setDisabled(cm.isLocked(index));
52050             ms.get("unlock").setDisabled(!cm.isLocked(index));
52051         }
52052         this.hmenu.show(hd, "tl-bl");
52053     },
52054
52055     handleHdOver : function(e){
52056         var hd = this.findHeaderCell(e.getTarget());
52057         if(hd && !this.headersDisabled){
52058             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
52059                this.fly(hd).addClass("x-grid-hd-over");
52060             }
52061         }
52062     },
52063
52064     handleHdOut : function(e){
52065         var hd = this.findHeaderCell(e.getTarget());
52066         if(hd){
52067             this.fly(hd).removeClass("x-grid-hd-over");
52068         }
52069     },
52070
52071     handleSplitDblClick : function(e, t){
52072         var i = this.getCellIndex(t);
52073         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
52074             this.autoSizeColumn(i, true);
52075             this.layout();
52076         }
52077     },
52078
52079     render : function(){
52080
52081         var cm = this.cm;
52082         var colCount = cm.getColumnCount();
52083
52084         if(this.grid.monitorWindowResize === true){
52085             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52086         }
52087         var header = this.renderHeaders();
52088         var body = this.templates.body.apply({rows:""});
52089         var html = this.templates.master.apply({
52090             lockedBody: body,
52091             body: body,
52092             lockedHeader: header[0],
52093             header: header[1]
52094         });
52095
52096         //this.updateColumns();
52097
52098         this.grid.getGridEl().dom.innerHTML = html;
52099
52100         this.initElements();
52101         
52102         // a kludge to fix the random scolling effect in webkit
52103         this.el.on("scroll", function() {
52104             this.el.dom.scrollTop=0; // hopefully not recursive..
52105         },this);
52106
52107         this.scroller.on("scroll", this.handleScroll, this);
52108         this.lockedBody.on("mousewheel", this.handleWheel, this);
52109         this.mainBody.on("mousewheel", this.handleWheel, this);
52110
52111         this.mainHd.on("mouseover", this.handleHdOver, this);
52112         this.mainHd.on("mouseout", this.handleHdOut, this);
52113         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
52114                 {delegate: "."+this.splitClass});
52115
52116         this.lockedHd.on("mouseover", this.handleHdOver, this);
52117         this.lockedHd.on("mouseout", this.handleHdOut, this);
52118         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
52119                 {delegate: "."+this.splitClass});
52120
52121         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
52122             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
52123         }
52124
52125         this.updateSplitters();
52126
52127         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
52128             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
52129             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
52130         }
52131
52132         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
52133             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
52134             this.hmenu.add(
52135                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
52136                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
52137             );
52138             if(this.grid.enableColLock !== false){
52139                 this.hmenu.add('-',
52140                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
52141                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
52142                 );
52143             }
52144             if(this.grid.enableColumnHide !== false){
52145
52146                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
52147                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
52148                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
52149
52150                 this.hmenu.add('-',
52151                     {id:"columns", text: this.columnsText, menu: this.colMenu}
52152                 );
52153             }
52154             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
52155
52156             this.grid.on("headercontextmenu", this.handleHdCtx, this);
52157         }
52158
52159         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
52160             this.dd = new Roo.grid.GridDragZone(this.grid, {
52161                 ddGroup : this.grid.ddGroup || 'GridDD'
52162             });
52163             
52164         }
52165
52166         /*
52167         for(var i = 0; i < colCount; i++){
52168             if(cm.isHidden(i)){
52169                 this.hideColumn(i);
52170             }
52171             if(cm.config[i].align){
52172                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
52173                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
52174             }
52175         }*/
52176         
52177         this.updateHeaderSortState();
52178
52179         this.beforeInitialResize();
52180         this.layout(true);
52181
52182         // two part rendering gives faster view to the user
52183         this.renderPhase2.defer(1, this);
52184     },
52185
52186     renderPhase2 : function(){
52187         // render the rows now
52188         this.refresh();
52189         if(this.grid.autoSizeColumns){
52190             this.autoSizeColumns();
52191         }
52192     },
52193
52194     beforeInitialResize : function(){
52195
52196     },
52197
52198     onColumnSplitterMoved : function(i, w){
52199         this.userResized = true;
52200         var cm = this.grid.colModel;
52201         cm.setColumnWidth(i, w, true);
52202         var cid = cm.getColumnId(i);
52203         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
52204         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
52205         this.updateSplitters();
52206         this.layout();
52207         this.grid.fireEvent("columnresize", i, w);
52208     },
52209
52210     syncRowHeights : function(startIndex, endIndex){
52211         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
52212             startIndex = startIndex || 0;
52213             var mrows = this.getBodyTable().rows;
52214             var lrows = this.getLockedTable().rows;
52215             var len = mrows.length-1;
52216             endIndex = Math.min(endIndex || len, len);
52217             for(var i = startIndex; i <= endIndex; i++){
52218                 var m = mrows[i], l = lrows[i];
52219                 var h = Math.max(m.offsetHeight, l.offsetHeight);
52220                 m.style.height = l.style.height = h + "px";
52221             }
52222         }
52223     },
52224
52225     layout : function(initialRender, is2ndPass){
52226         var g = this.grid;
52227         var auto = g.autoHeight;
52228         var scrollOffset = 16;
52229         var c = g.getGridEl(), cm = this.cm,
52230                 expandCol = g.autoExpandColumn,
52231                 gv = this;
52232         //c.beginMeasure();
52233
52234         if(!c.dom.offsetWidth){ // display:none?
52235             if(initialRender){
52236                 this.lockedWrap.show();
52237                 this.mainWrap.show();
52238             }
52239             return;
52240         }
52241
52242         var hasLock = this.cm.isLocked(0);
52243
52244         var tbh = this.headerPanel.getHeight();
52245         var bbh = this.footerPanel.getHeight();
52246
52247         if(auto){
52248             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
52249             var newHeight = ch + c.getBorderWidth("tb");
52250             if(g.maxHeight){
52251                 newHeight = Math.min(g.maxHeight, newHeight);
52252             }
52253             c.setHeight(newHeight);
52254         }
52255
52256         if(g.autoWidth){
52257             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
52258         }
52259
52260         var s = this.scroller;
52261
52262         var csize = c.getSize(true);
52263
52264         this.el.setSize(csize.width, csize.height);
52265
52266         this.headerPanel.setWidth(csize.width);
52267         this.footerPanel.setWidth(csize.width);
52268
52269         var hdHeight = this.mainHd.getHeight();
52270         var vw = csize.width;
52271         var vh = csize.height - (tbh + bbh);
52272
52273         s.setSize(vw, vh);
52274
52275         var bt = this.getBodyTable();
52276         var ltWidth = hasLock ?
52277                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
52278
52279         var scrollHeight = bt.offsetHeight;
52280         var scrollWidth = ltWidth + bt.offsetWidth;
52281         var vscroll = false, hscroll = false;
52282
52283         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
52284
52285         var lw = this.lockedWrap, mw = this.mainWrap;
52286         var lb = this.lockedBody, mb = this.mainBody;
52287
52288         setTimeout(function(){
52289             var t = s.dom.offsetTop;
52290             var w = s.dom.clientWidth,
52291                 h = s.dom.clientHeight;
52292
52293             lw.setTop(t);
52294             lw.setSize(ltWidth, h);
52295
52296             mw.setLeftTop(ltWidth, t);
52297             mw.setSize(w-ltWidth, h);
52298
52299             lb.setHeight(h-hdHeight);
52300             mb.setHeight(h-hdHeight);
52301
52302             if(is2ndPass !== true && !gv.userResized && expandCol){
52303                 // high speed resize without full column calculation
52304                 
52305                 var ci = cm.getIndexById(expandCol);
52306                 if (ci < 0) {
52307                     ci = cm.findColumnIndex(expandCol);
52308                 }
52309                 ci = Math.max(0, ci); // make sure it's got at least the first col.
52310                 var expandId = cm.getColumnId(ci);
52311                 var  tw = cm.getTotalWidth(false);
52312                 var currentWidth = cm.getColumnWidth(ci);
52313                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
52314                 if(currentWidth != cw){
52315                     cm.setColumnWidth(ci, cw, true);
52316                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
52317                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
52318                     gv.updateSplitters();
52319                     gv.layout(false, true);
52320                 }
52321             }
52322
52323             if(initialRender){
52324                 lw.show();
52325                 mw.show();
52326             }
52327             //c.endMeasure();
52328         }, 10);
52329     },
52330
52331     onWindowResize : function(){
52332         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
52333             return;
52334         }
52335         this.layout();
52336     },
52337
52338     appendFooter : function(parentEl){
52339         return null;
52340     },
52341
52342     sortAscText : "Sort Ascending",
52343     sortDescText : "Sort Descending",
52344     lockText : "Lock Column",
52345     unlockText : "Unlock Column",
52346     columnsText : "Columns"
52347 });
52348
52349
52350 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
52351     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
52352     this.proxy.el.addClass('x-grid3-col-dd');
52353 };
52354
52355 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
52356     handleMouseDown : function(e){
52357
52358     },
52359
52360     callHandleMouseDown : function(e){
52361         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
52362     }
52363 });
52364 /*
52365  * Based on:
52366  * Ext JS Library 1.1.1
52367  * Copyright(c) 2006-2007, Ext JS, LLC.
52368  *
52369  * Originally Released Under LGPL - original licence link has changed is not relivant.
52370  *
52371  * Fork - LGPL
52372  * <script type="text/javascript">
52373  */
52374  
52375 // private
52376 // This is a support class used internally by the Grid components
52377 Roo.grid.SplitDragZone = function(grid, hd, hd2){
52378     this.grid = grid;
52379     this.view = grid.getView();
52380     this.proxy = this.view.resizeProxy;
52381     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
52382         "gridSplitters" + this.grid.getGridEl().id, {
52383         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
52384     });
52385     this.setHandleElId(Roo.id(hd));
52386     this.setOuterHandleElId(Roo.id(hd2));
52387     this.scroll = false;
52388 };
52389 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
52390     fly: Roo.Element.fly,
52391
52392     b4StartDrag : function(x, y){
52393         this.view.headersDisabled = true;
52394         this.proxy.setHeight(this.view.mainWrap.getHeight());
52395         var w = this.cm.getColumnWidth(this.cellIndex);
52396         var minw = Math.max(w-this.grid.minColumnWidth, 0);
52397         this.resetConstraints();
52398         this.setXConstraint(minw, 1000);
52399         this.setYConstraint(0, 0);
52400         this.minX = x - minw;
52401         this.maxX = x + 1000;
52402         this.startPos = x;
52403         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
52404     },
52405
52406
52407     handleMouseDown : function(e){
52408         ev = Roo.EventObject.setEvent(e);
52409         var t = this.fly(ev.getTarget());
52410         if(t.hasClass("x-grid-split")){
52411             this.cellIndex = this.view.getCellIndex(t.dom);
52412             this.split = t.dom;
52413             this.cm = this.grid.colModel;
52414             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
52415                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
52416             }
52417         }
52418     },
52419
52420     endDrag : function(e){
52421         this.view.headersDisabled = false;
52422         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
52423         var diff = endX - this.startPos;
52424         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
52425     },
52426
52427     autoOffset : function(){
52428         this.setDelta(0,0);
52429     }
52430 });/*
52431  * Based on:
52432  * Ext JS Library 1.1.1
52433  * Copyright(c) 2006-2007, Ext JS, LLC.
52434  *
52435  * Originally Released Under LGPL - original licence link has changed is not relivant.
52436  *
52437  * Fork - LGPL
52438  * <script type="text/javascript">
52439  */
52440  
52441 // private
52442 // This is a support class used internally by the Grid components
52443 Roo.grid.GridDragZone = function(grid, config){
52444     this.view = grid.getView();
52445     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
52446     if(this.view.lockedBody){
52447         this.setHandleElId(Roo.id(this.view.mainBody.dom));
52448         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
52449     }
52450     this.scroll = false;
52451     this.grid = grid;
52452     this.ddel = document.createElement('div');
52453     this.ddel.className = 'x-grid-dd-wrap';
52454 };
52455
52456 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
52457     ddGroup : "GridDD",
52458
52459     getDragData : function(e){
52460         var t = Roo.lib.Event.getTarget(e);
52461         var rowIndex = this.view.findRowIndex(t);
52462         var sm = this.grid.selModel;
52463             
52464         //Roo.log(rowIndex);
52465         
52466         if (sm.getSelectedCell) {
52467             // cell selection..
52468             if (!sm.getSelectedCell()) {
52469                 return false;
52470             }
52471             if (rowIndex != sm.getSelectedCell()[0]) {
52472                 return false;
52473             }
52474         
52475         }
52476         
52477         if(rowIndex !== false){
52478             
52479             // if editorgrid.. 
52480             
52481             
52482             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
52483                
52484             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
52485               //  
52486             //}
52487             if (e.hasModifier()){
52488                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
52489             }
52490             
52491             Roo.log("getDragData");
52492             
52493             return {
52494                 grid: this.grid,
52495                 ddel: this.ddel,
52496                 rowIndex: rowIndex,
52497                 selections:sm.getSelections ? sm.getSelections() : (
52498                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
52499                 )
52500             };
52501         }
52502         return false;
52503     },
52504
52505     onInitDrag : function(e){
52506         var data = this.dragData;
52507         this.ddel.innerHTML = this.grid.getDragDropText();
52508         this.proxy.update(this.ddel);
52509         // fire start drag?
52510     },
52511
52512     afterRepair : function(){
52513         this.dragging = false;
52514     },
52515
52516     getRepairXY : function(e, data){
52517         return false;
52518     },
52519
52520     onEndDrag : function(data, e){
52521         // fire end drag?
52522     },
52523
52524     onValidDrop : function(dd, e, id){
52525         // fire drag drop?
52526         this.hideProxy();
52527     },
52528
52529     beforeInvalidDrop : function(e, id){
52530
52531     }
52532 });/*
52533  * Based on:
52534  * Ext JS Library 1.1.1
52535  * Copyright(c) 2006-2007, Ext JS, LLC.
52536  *
52537  * Originally Released Under LGPL - original licence link has changed is not relivant.
52538  *
52539  * Fork - LGPL
52540  * <script type="text/javascript">
52541  */
52542  
52543
52544 /**
52545  * @class Roo.grid.ColumnModel
52546  * @extends Roo.util.Observable
52547  * This is the default implementation of a ColumnModel used by the Grid. It defines
52548  * the columns in the grid.
52549  * <br>Usage:<br>
52550  <pre><code>
52551  var colModel = new Roo.grid.ColumnModel([
52552         {header: "Ticker", width: 60, sortable: true, locked: true},
52553         {header: "Company Name", width: 150, sortable: true},
52554         {header: "Market Cap.", width: 100, sortable: true},
52555         {header: "$ Sales", width: 100, sortable: true, renderer: money},
52556         {header: "Employees", width: 100, sortable: true, resizable: false}
52557  ]);
52558  </code></pre>
52559  * <p>
52560  
52561  * The config options listed for this class are options which may appear in each
52562  * individual column definition.
52563  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
52564  * @constructor
52565  * @param {Object} config An Array of column config objects. See this class's
52566  * config objects for details.
52567 */
52568 Roo.grid.ColumnModel = function(config){
52569         /**
52570      * The config passed into the constructor
52571      */
52572     this.config = config;
52573     this.lookup = {};
52574
52575     // if no id, create one
52576     // if the column does not have a dataIndex mapping,
52577     // map it to the order it is in the config
52578     for(var i = 0, len = config.length; i < len; i++){
52579         var c = config[i];
52580         if(typeof c.dataIndex == "undefined"){
52581             c.dataIndex = i;
52582         }
52583         if(typeof c.renderer == "string"){
52584             c.renderer = Roo.util.Format[c.renderer];
52585         }
52586         if(typeof c.id == "undefined"){
52587             c.id = Roo.id();
52588         }
52589         if(c.editor && c.editor.xtype){
52590             c.editor  = Roo.factory(c.editor, Roo.grid);
52591         }
52592         if(c.editor && c.editor.isFormField){
52593             c.editor = new Roo.grid.GridEditor(c.editor);
52594         }
52595         this.lookup[c.id] = c;
52596     }
52597
52598     /**
52599      * The width of columns which have no width specified (defaults to 100)
52600      * @type Number
52601      */
52602     this.defaultWidth = 100;
52603
52604     /**
52605      * Default sortable of columns which have no sortable specified (defaults to false)
52606      * @type Boolean
52607      */
52608     this.defaultSortable = false;
52609
52610     this.addEvents({
52611         /**
52612              * @event widthchange
52613              * Fires when the width of a column changes.
52614              * @param {ColumnModel} this
52615              * @param {Number} columnIndex The column index
52616              * @param {Number} newWidth The new width
52617              */
52618             "widthchange": true,
52619         /**
52620              * @event headerchange
52621              * Fires when the text of a header changes.
52622              * @param {ColumnModel} this
52623              * @param {Number} columnIndex The column index
52624              * @param {Number} newText The new header text
52625              */
52626             "headerchange": true,
52627         /**
52628              * @event hiddenchange
52629              * Fires when a column is hidden or "unhidden".
52630              * @param {ColumnModel} this
52631              * @param {Number} columnIndex The column index
52632              * @param {Boolean} hidden true if hidden, false otherwise
52633              */
52634             "hiddenchange": true,
52635             /**
52636          * @event columnmoved
52637          * Fires when a column is moved.
52638          * @param {ColumnModel} this
52639          * @param {Number} oldIndex
52640          * @param {Number} newIndex
52641          */
52642         "columnmoved" : true,
52643         /**
52644          * @event columlockchange
52645          * Fires when a column's locked state is changed
52646          * @param {ColumnModel} this
52647          * @param {Number} colIndex
52648          * @param {Boolean} locked true if locked
52649          */
52650         "columnlockchange" : true
52651     });
52652     Roo.grid.ColumnModel.superclass.constructor.call(this);
52653 };
52654 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
52655     /**
52656      * @cfg {String} header The header text to display in the Grid view.
52657      */
52658     /**
52659      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
52660      * {@link Roo.data.Record} definition from which to draw the column's value. If not
52661      * specified, the column's index is used as an index into the Record's data Array.
52662      */
52663     /**
52664      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
52665      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
52666      */
52667     /**
52668      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
52669      * Defaults to the value of the {@link #defaultSortable} property.
52670      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
52671      */
52672     /**
52673      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
52674      */
52675     /**
52676      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
52677      */
52678     /**
52679      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
52680      */
52681     /**
52682      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
52683      */
52684     /**
52685      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
52686      * given the cell's data value. See {@link #setRenderer}. If not specified, the
52687      * default renderer uses the raw data value.
52688      */
52689        /**
52690      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
52691      */
52692     /**
52693      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
52694      */
52695
52696     /**
52697      * Returns the id of the column at the specified index.
52698      * @param {Number} index The column index
52699      * @return {String} the id
52700      */
52701     getColumnId : function(index){
52702         return this.config[index].id;
52703     },
52704
52705     /**
52706      * Returns the column for a specified id.
52707      * @param {String} id The column id
52708      * @return {Object} the column
52709      */
52710     getColumnById : function(id){
52711         return this.lookup[id];
52712     },
52713
52714     
52715     /**
52716      * Returns the column for a specified dataIndex.
52717      * @param {String} dataIndex The column dataIndex
52718      * @return {Object|Boolean} the column or false if not found
52719      */
52720     getColumnByDataIndex: function(dataIndex){
52721         var index = this.findColumnIndex(dataIndex);
52722         return index > -1 ? this.config[index] : false;
52723     },
52724     
52725     /**
52726      * Returns the index for a specified column id.
52727      * @param {String} id The column id
52728      * @return {Number} the index, or -1 if not found
52729      */
52730     getIndexById : function(id){
52731         for(var i = 0, len = this.config.length; i < len; i++){
52732             if(this.config[i].id == id){
52733                 return i;
52734             }
52735         }
52736         return -1;
52737     },
52738     
52739     /**
52740      * Returns the index for a specified column dataIndex.
52741      * @param {String} dataIndex The column dataIndex
52742      * @return {Number} the index, or -1 if not found
52743      */
52744     
52745     findColumnIndex : function(dataIndex){
52746         for(var i = 0, len = this.config.length; i < len; i++){
52747             if(this.config[i].dataIndex == dataIndex){
52748                 return i;
52749             }
52750         }
52751         return -1;
52752     },
52753     
52754     
52755     moveColumn : function(oldIndex, newIndex){
52756         var c = this.config[oldIndex];
52757         this.config.splice(oldIndex, 1);
52758         this.config.splice(newIndex, 0, c);
52759         this.dataMap = null;
52760         this.fireEvent("columnmoved", this, oldIndex, newIndex);
52761     },
52762
52763     isLocked : function(colIndex){
52764         return this.config[colIndex].locked === true;
52765     },
52766
52767     setLocked : function(colIndex, value, suppressEvent){
52768         if(this.isLocked(colIndex) == value){
52769             return;
52770         }
52771         this.config[colIndex].locked = value;
52772         if(!suppressEvent){
52773             this.fireEvent("columnlockchange", this, colIndex, value);
52774         }
52775     },
52776
52777     getTotalLockedWidth : function(){
52778         var totalWidth = 0;
52779         for(var i = 0; i < this.config.length; i++){
52780             if(this.isLocked(i) && !this.isHidden(i)){
52781                 this.totalWidth += this.getColumnWidth(i);
52782             }
52783         }
52784         return totalWidth;
52785     },
52786
52787     getLockedCount : function(){
52788         for(var i = 0, len = this.config.length; i < len; i++){
52789             if(!this.isLocked(i)){
52790                 return i;
52791             }
52792         }
52793     },
52794
52795     /**
52796      * Returns the number of columns.
52797      * @return {Number}
52798      */
52799     getColumnCount : function(visibleOnly){
52800         if(visibleOnly === true){
52801             var c = 0;
52802             for(var i = 0, len = this.config.length; i < len; i++){
52803                 if(!this.isHidden(i)){
52804                     c++;
52805                 }
52806             }
52807             return c;
52808         }
52809         return this.config.length;
52810     },
52811
52812     /**
52813      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
52814      * @param {Function} fn
52815      * @param {Object} scope (optional)
52816      * @return {Array} result
52817      */
52818     getColumnsBy : function(fn, scope){
52819         var r = [];
52820         for(var i = 0, len = this.config.length; i < len; i++){
52821             var c = this.config[i];
52822             if(fn.call(scope||this, c, i) === true){
52823                 r[r.length] = c;
52824             }
52825         }
52826         return r;
52827     },
52828
52829     /**
52830      * Returns true if the specified column is sortable.
52831      * @param {Number} col The column index
52832      * @return {Boolean}
52833      */
52834     isSortable : function(col){
52835         if(typeof this.config[col].sortable == "undefined"){
52836             return this.defaultSortable;
52837         }
52838         return this.config[col].sortable;
52839     },
52840
52841     /**
52842      * Returns the rendering (formatting) function defined for the column.
52843      * @param {Number} col The column index.
52844      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
52845      */
52846     getRenderer : function(col){
52847         if(!this.config[col].renderer){
52848             return Roo.grid.ColumnModel.defaultRenderer;
52849         }
52850         return this.config[col].renderer;
52851     },
52852
52853     /**
52854      * Sets the rendering (formatting) function for a column.
52855      * @param {Number} col The column index
52856      * @param {Function} fn The function to use to process the cell's raw data
52857      * to return HTML markup for the grid view. The render function is called with
52858      * the following parameters:<ul>
52859      * <li>Data value.</li>
52860      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
52861      * <li>css A CSS style string to apply to the table cell.</li>
52862      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
52863      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
52864      * <li>Row index</li>
52865      * <li>Column index</li>
52866      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
52867      */
52868     setRenderer : function(col, fn){
52869         this.config[col].renderer = fn;
52870     },
52871
52872     /**
52873      * Returns the width for the specified column.
52874      * @param {Number} col The column index
52875      * @return {Number}
52876      */
52877     getColumnWidth : function(col){
52878         return this.config[col].width * 1 || this.defaultWidth;
52879     },
52880
52881     /**
52882      * Sets the width for a column.
52883      * @param {Number} col The column index
52884      * @param {Number} width The new width
52885      */
52886     setColumnWidth : function(col, width, suppressEvent){
52887         this.config[col].width = width;
52888         this.totalWidth = null;
52889         if(!suppressEvent){
52890              this.fireEvent("widthchange", this, col, width);
52891         }
52892     },
52893
52894     /**
52895      * Returns the total width of all columns.
52896      * @param {Boolean} includeHidden True to include hidden column widths
52897      * @return {Number}
52898      */
52899     getTotalWidth : function(includeHidden){
52900         if(!this.totalWidth){
52901             this.totalWidth = 0;
52902             for(var i = 0, len = this.config.length; i < len; i++){
52903                 if(includeHidden || !this.isHidden(i)){
52904                     this.totalWidth += this.getColumnWidth(i);
52905                 }
52906             }
52907         }
52908         return this.totalWidth;
52909     },
52910
52911     /**
52912      * Returns the header for the specified column.
52913      * @param {Number} col The column index
52914      * @return {String}
52915      */
52916     getColumnHeader : function(col){
52917         return this.config[col].header;
52918     },
52919
52920     /**
52921      * Sets the header for a column.
52922      * @param {Number} col The column index
52923      * @param {String} header The new header
52924      */
52925     setColumnHeader : function(col, header){
52926         this.config[col].header = header;
52927         this.fireEvent("headerchange", this, col, header);
52928     },
52929
52930     /**
52931      * Returns the tooltip for the specified column.
52932      * @param {Number} col The column index
52933      * @return {String}
52934      */
52935     getColumnTooltip : function(col){
52936             return this.config[col].tooltip;
52937     },
52938     /**
52939      * Sets the tooltip for a column.
52940      * @param {Number} col The column index
52941      * @param {String} tooltip The new tooltip
52942      */
52943     setColumnTooltip : function(col, tooltip){
52944             this.config[col].tooltip = tooltip;
52945     },
52946
52947     /**
52948      * Returns the dataIndex for the specified column.
52949      * @param {Number} col The column index
52950      * @return {Number}
52951      */
52952     getDataIndex : function(col){
52953         return this.config[col].dataIndex;
52954     },
52955
52956     /**
52957      * Sets the dataIndex for a column.
52958      * @param {Number} col The column index
52959      * @param {Number} dataIndex The new dataIndex
52960      */
52961     setDataIndex : function(col, dataIndex){
52962         this.config[col].dataIndex = dataIndex;
52963     },
52964
52965     
52966     
52967     /**
52968      * Returns true if the cell is editable.
52969      * @param {Number} colIndex The column index
52970      * @param {Number} rowIndex The row index
52971      * @return {Boolean}
52972      */
52973     isCellEditable : function(colIndex, rowIndex){
52974         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
52975     },
52976
52977     /**
52978      * Returns the editor defined for the cell/column.
52979      * return false or null to disable editing.
52980      * @param {Number} colIndex The column index
52981      * @param {Number} rowIndex The row index
52982      * @return {Object}
52983      */
52984     getCellEditor : function(colIndex, rowIndex){
52985         return this.config[colIndex].editor;
52986     },
52987
52988     /**
52989      * Sets if a column is editable.
52990      * @param {Number} col The column index
52991      * @param {Boolean} editable True if the column is editable
52992      */
52993     setEditable : function(col, editable){
52994         this.config[col].editable = editable;
52995     },
52996
52997
52998     /**
52999      * Returns true if the column is hidden.
53000      * @param {Number} colIndex The column index
53001      * @return {Boolean}
53002      */
53003     isHidden : function(colIndex){
53004         return this.config[colIndex].hidden;
53005     },
53006
53007
53008     /**
53009      * Returns true if the column width cannot be changed
53010      */
53011     isFixed : function(colIndex){
53012         return this.config[colIndex].fixed;
53013     },
53014
53015     /**
53016      * Returns true if the column can be resized
53017      * @return {Boolean}
53018      */
53019     isResizable : function(colIndex){
53020         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
53021     },
53022     /**
53023      * Sets if a column is hidden.
53024      * @param {Number} colIndex The column index
53025      * @param {Boolean} hidden True if the column is hidden
53026      */
53027     setHidden : function(colIndex, hidden){
53028         this.config[colIndex].hidden = hidden;
53029         this.totalWidth = null;
53030         this.fireEvent("hiddenchange", this, colIndex, hidden);
53031     },
53032
53033     /**
53034      * Sets the editor for a column.
53035      * @param {Number} col The column index
53036      * @param {Object} editor The editor object
53037      */
53038     setEditor : function(col, editor){
53039         this.config[col].editor = editor;
53040     }
53041 });
53042
53043 Roo.grid.ColumnModel.defaultRenderer = function(value){
53044         if(typeof value == "string" && value.length < 1){
53045             return "&#160;";
53046         }
53047         return value;
53048 };
53049
53050 // Alias for backwards compatibility
53051 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
53052 /*
53053  * Based on:
53054  * Ext JS Library 1.1.1
53055  * Copyright(c) 2006-2007, Ext JS, LLC.
53056  *
53057  * Originally Released Under LGPL - original licence link has changed is not relivant.
53058  *
53059  * Fork - LGPL
53060  * <script type="text/javascript">
53061  */
53062
53063 /**
53064  * @class Roo.grid.AbstractSelectionModel
53065  * @extends Roo.util.Observable
53066  * Abstract base class for grid SelectionModels.  It provides the interface that should be
53067  * implemented by descendant classes.  This class should not be directly instantiated.
53068  * @constructor
53069  */
53070 Roo.grid.AbstractSelectionModel = function(){
53071     this.locked = false;
53072     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
53073 };
53074
53075 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
53076     /** @ignore Called by the grid automatically. Do not call directly. */
53077     init : function(grid){
53078         this.grid = grid;
53079         this.initEvents();
53080     },
53081
53082     /**
53083      * Locks the selections.
53084      */
53085     lock : function(){
53086         this.locked = true;
53087     },
53088
53089     /**
53090      * Unlocks the selections.
53091      */
53092     unlock : function(){
53093         this.locked = false;
53094     },
53095
53096     /**
53097      * Returns true if the selections are locked.
53098      * @return {Boolean}
53099      */
53100     isLocked : function(){
53101         return this.locked;
53102     }
53103 });/*
53104  * Based on:
53105  * Ext JS Library 1.1.1
53106  * Copyright(c) 2006-2007, Ext JS, LLC.
53107  *
53108  * Originally Released Under LGPL - original licence link has changed is not relivant.
53109  *
53110  * Fork - LGPL
53111  * <script type="text/javascript">
53112  */
53113 /**
53114  * @extends Roo.grid.AbstractSelectionModel
53115  * @class Roo.grid.RowSelectionModel
53116  * The default SelectionModel used by {@link Roo.grid.Grid}.
53117  * It supports multiple selections and keyboard selection/navigation. 
53118  * @constructor
53119  * @param {Object} config
53120  */
53121 Roo.grid.RowSelectionModel = function(config){
53122     Roo.apply(this, config);
53123     this.selections = new Roo.util.MixedCollection(false, function(o){
53124         return o.id;
53125     });
53126
53127     this.last = false;
53128     this.lastActive = false;
53129
53130     this.addEvents({
53131         /**
53132              * @event selectionchange
53133              * Fires when the selection changes
53134              * @param {SelectionModel} this
53135              */
53136             "selectionchange" : true,
53137         /**
53138              * @event afterselectionchange
53139              * Fires after the selection changes (eg. by key press or clicking)
53140              * @param {SelectionModel} this
53141              */
53142             "afterselectionchange" : true,
53143         /**
53144              * @event beforerowselect
53145              * Fires when a row is selected being selected, return false to cancel.
53146              * @param {SelectionModel} this
53147              * @param {Number} rowIndex The selected index
53148              * @param {Boolean} keepExisting False if other selections will be cleared
53149              */
53150             "beforerowselect" : true,
53151         /**
53152              * @event rowselect
53153              * Fires when a row is selected.
53154              * @param {SelectionModel} this
53155              * @param {Number} rowIndex The selected index
53156              * @param {Roo.data.Record} r The record
53157              */
53158             "rowselect" : true,
53159         /**
53160              * @event rowdeselect
53161              * Fires when a row is deselected.
53162              * @param {SelectionModel} this
53163              * @param {Number} rowIndex The selected index
53164              */
53165         "rowdeselect" : true
53166     });
53167     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
53168     this.locked = false;
53169 };
53170
53171 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
53172     /**
53173      * @cfg {Boolean} singleSelect
53174      * True to allow selection of only one row at a time (defaults to false)
53175      */
53176     singleSelect : false,
53177
53178     // private
53179     initEvents : function(){
53180
53181         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
53182             this.grid.on("mousedown", this.handleMouseDown, this);
53183         }else{ // allow click to work like normal
53184             this.grid.on("rowclick", this.handleDragableRowClick, this);
53185         }
53186
53187         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
53188             "up" : function(e){
53189                 if(!e.shiftKey){
53190                     this.selectPrevious(e.shiftKey);
53191                 }else if(this.last !== false && this.lastActive !== false){
53192                     var last = this.last;
53193                     this.selectRange(this.last,  this.lastActive-1);
53194                     this.grid.getView().focusRow(this.lastActive);
53195                     if(last !== false){
53196                         this.last = last;
53197                     }
53198                 }else{
53199                     this.selectFirstRow();
53200                 }
53201                 this.fireEvent("afterselectionchange", this);
53202             },
53203             "down" : function(e){
53204                 if(!e.shiftKey){
53205                     this.selectNext(e.shiftKey);
53206                 }else if(this.last !== false && this.lastActive !== false){
53207                     var last = this.last;
53208                     this.selectRange(this.last,  this.lastActive+1);
53209                     this.grid.getView().focusRow(this.lastActive);
53210                     if(last !== false){
53211                         this.last = last;
53212                     }
53213                 }else{
53214                     this.selectFirstRow();
53215                 }
53216                 this.fireEvent("afterselectionchange", this);
53217             },
53218             scope: this
53219         });
53220
53221         var view = this.grid.view;
53222         view.on("refresh", this.onRefresh, this);
53223         view.on("rowupdated", this.onRowUpdated, this);
53224         view.on("rowremoved", this.onRemove, this);
53225     },
53226
53227     // private
53228     onRefresh : function(){
53229         var ds = this.grid.dataSource, i, v = this.grid.view;
53230         var s = this.selections;
53231         s.each(function(r){
53232             if((i = ds.indexOfId(r.id)) != -1){
53233                 v.onRowSelect(i);
53234             }else{
53235                 s.remove(r);
53236             }
53237         });
53238     },
53239
53240     // private
53241     onRemove : function(v, index, r){
53242         this.selections.remove(r);
53243     },
53244
53245     // private
53246     onRowUpdated : function(v, index, r){
53247         if(this.isSelected(r)){
53248             v.onRowSelect(index);
53249         }
53250     },
53251
53252     /**
53253      * Select records.
53254      * @param {Array} records The records to select
53255      * @param {Boolean} keepExisting (optional) True to keep existing selections
53256      */
53257     selectRecords : function(records, keepExisting){
53258         if(!keepExisting){
53259             this.clearSelections();
53260         }
53261         var ds = this.grid.dataSource;
53262         for(var i = 0, len = records.length; i < len; i++){
53263             this.selectRow(ds.indexOf(records[i]), true);
53264         }
53265     },
53266
53267     /**
53268      * Gets the number of selected rows.
53269      * @return {Number}
53270      */
53271     getCount : function(){
53272         return this.selections.length;
53273     },
53274
53275     /**
53276      * Selects the first row in the grid.
53277      */
53278     selectFirstRow : function(){
53279         this.selectRow(0);
53280     },
53281
53282     /**
53283      * Select the last row.
53284      * @param {Boolean} keepExisting (optional) True to keep existing selections
53285      */
53286     selectLastRow : function(keepExisting){
53287         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
53288     },
53289
53290     /**
53291      * Selects the row immediately following the last selected row.
53292      * @param {Boolean} keepExisting (optional) True to keep existing selections
53293      */
53294     selectNext : function(keepExisting){
53295         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
53296             this.selectRow(this.last+1, keepExisting);
53297             this.grid.getView().focusRow(this.last);
53298         }
53299     },
53300
53301     /**
53302      * Selects the row that precedes the last selected row.
53303      * @param {Boolean} keepExisting (optional) True to keep existing selections
53304      */
53305     selectPrevious : function(keepExisting){
53306         if(this.last){
53307             this.selectRow(this.last-1, keepExisting);
53308             this.grid.getView().focusRow(this.last);
53309         }
53310     },
53311
53312     /**
53313      * Returns the selected records
53314      * @return {Array} Array of selected records
53315      */
53316     getSelections : function(){
53317         return [].concat(this.selections.items);
53318     },
53319
53320     /**
53321      * Returns the first selected record.
53322      * @return {Record}
53323      */
53324     getSelected : function(){
53325         return this.selections.itemAt(0);
53326     },
53327
53328
53329     /**
53330      * Clears all selections.
53331      */
53332     clearSelections : function(fast){
53333         if(this.locked) return;
53334         if(fast !== true){
53335             var ds = this.grid.dataSource;
53336             var s = this.selections;
53337             s.each(function(r){
53338                 this.deselectRow(ds.indexOfId(r.id));
53339             }, this);
53340             s.clear();
53341         }else{
53342             this.selections.clear();
53343         }
53344         this.last = false;
53345     },
53346
53347
53348     /**
53349      * Selects all rows.
53350      */
53351     selectAll : function(){
53352         if(this.locked) return;
53353         this.selections.clear();
53354         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
53355             this.selectRow(i, true);
53356         }
53357     },
53358
53359     /**
53360      * Returns True if there is a selection.
53361      * @return {Boolean}
53362      */
53363     hasSelection : function(){
53364         return this.selections.length > 0;
53365     },
53366
53367     /**
53368      * Returns True if the specified row is selected.
53369      * @param {Number/Record} record The record or index of the record to check
53370      * @return {Boolean}
53371      */
53372     isSelected : function(index){
53373         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
53374         return (r && this.selections.key(r.id) ? true : false);
53375     },
53376
53377     /**
53378      * Returns True if the specified record id is selected.
53379      * @param {String} id The id of record to check
53380      * @return {Boolean}
53381      */
53382     isIdSelected : function(id){
53383         return (this.selections.key(id) ? true : false);
53384     },
53385
53386     // private
53387     handleMouseDown : function(e, t){
53388         var view = this.grid.getView(), rowIndex;
53389         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
53390             return;
53391         };
53392         if(e.shiftKey && this.last !== false){
53393             var last = this.last;
53394             this.selectRange(last, rowIndex, e.ctrlKey);
53395             this.last = last; // reset the last
53396             view.focusRow(rowIndex);
53397         }else{
53398             var isSelected = this.isSelected(rowIndex);
53399             if(e.button !== 0 && isSelected){
53400                 view.focusRow(rowIndex);
53401             }else if(e.ctrlKey && isSelected){
53402                 this.deselectRow(rowIndex);
53403             }else if(!isSelected){
53404                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
53405                 view.focusRow(rowIndex);
53406             }
53407         }
53408         this.fireEvent("afterselectionchange", this);
53409     },
53410     // private
53411     handleDragableRowClick :  function(grid, rowIndex, e) 
53412     {
53413         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
53414             this.selectRow(rowIndex, false);
53415             grid.view.focusRow(rowIndex);
53416              this.fireEvent("afterselectionchange", this);
53417         }
53418     },
53419     
53420     /**
53421      * Selects multiple rows.
53422      * @param {Array} rows Array of the indexes of the row to select
53423      * @param {Boolean} keepExisting (optional) True to keep existing selections
53424      */
53425     selectRows : function(rows, keepExisting){
53426         if(!keepExisting){
53427             this.clearSelections();
53428         }
53429         for(var i = 0, len = rows.length; i < len; i++){
53430             this.selectRow(rows[i], true);
53431         }
53432     },
53433
53434     /**
53435      * Selects a range of rows. All rows in between startRow and endRow are also selected.
53436      * @param {Number} startRow The index of the first row in the range
53437      * @param {Number} endRow The index of the last row in the range
53438      * @param {Boolean} keepExisting (optional) True to retain existing selections
53439      */
53440     selectRange : function(startRow, endRow, keepExisting){
53441         if(this.locked) return;
53442         if(!keepExisting){
53443             this.clearSelections();
53444         }
53445         if(startRow <= endRow){
53446             for(var i = startRow; i <= endRow; i++){
53447                 this.selectRow(i, true);
53448             }
53449         }else{
53450             for(var i = startRow; i >= endRow; i--){
53451                 this.selectRow(i, true);
53452             }
53453         }
53454     },
53455
53456     /**
53457      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
53458      * @param {Number} startRow The index of the first row in the range
53459      * @param {Number} endRow The index of the last row in the range
53460      */
53461     deselectRange : function(startRow, endRow, preventViewNotify){
53462         if(this.locked) return;
53463         for(var i = startRow; i <= endRow; i++){
53464             this.deselectRow(i, preventViewNotify);
53465         }
53466     },
53467
53468     /**
53469      * Selects a row.
53470      * @param {Number} row The index of the row to select
53471      * @param {Boolean} keepExisting (optional) True to keep existing selections
53472      */
53473     selectRow : function(index, keepExisting, preventViewNotify){
53474         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
53475         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
53476             if(!keepExisting || this.singleSelect){
53477                 this.clearSelections();
53478             }
53479             var r = this.grid.dataSource.getAt(index);
53480             this.selections.add(r);
53481             this.last = this.lastActive = index;
53482             if(!preventViewNotify){
53483                 this.grid.getView().onRowSelect(index);
53484             }
53485             this.fireEvent("rowselect", this, index, r);
53486             this.fireEvent("selectionchange", this);
53487         }
53488     },
53489
53490     /**
53491      * Deselects a row.
53492      * @param {Number} row The index of the row to deselect
53493      */
53494     deselectRow : function(index, preventViewNotify){
53495         if(this.locked) return;
53496         if(this.last == index){
53497             this.last = false;
53498         }
53499         if(this.lastActive == index){
53500             this.lastActive = false;
53501         }
53502         var r = this.grid.dataSource.getAt(index);
53503         this.selections.remove(r);
53504         if(!preventViewNotify){
53505             this.grid.getView().onRowDeselect(index);
53506         }
53507         this.fireEvent("rowdeselect", this, index);
53508         this.fireEvent("selectionchange", this);
53509     },
53510
53511     // private
53512     restoreLast : function(){
53513         if(this._last){
53514             this.last = this._last;
53515         }
53516     },
53517
53518     // private
53519     acceptsNav : function(row, col, cm){
53520         return !cm.isHidden(col) && cm.isCellEditable(col, row);
53521     },
53522
53523     // private
53524     onEditorKey : function(field, e){
53525         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
53526         if(k == e.TAB){
53527             e.stopEvent();
53528             ed.completeEdit();
53529             if(e.shiftKey){
53530                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
53531             }else{
53532                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
53533             }
53534         }else if(k == e.ENTER && !e.ctrlKey){
53535             e.stopEvent();
53536             ed.completeEdit();
53537             if(e.shiftKey){
53538                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
53539             }else{
53540                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
53541             }
53542         }else if(k == e.ESC){
53543             ed.cancelEdit();
53544         }
53545         if(newCell){
53546             g.startEditing(newCell[0], newCell[1]);
53547         }
53548     }
53549 });/*
53550  * Based on:
53551  * Ext JS Library 1.1.1
53552  * Copyright(c) 2006-2007, Ext JS, LLC.
53553  *
53554  * Originally Released Under LGPL - original licence link has changed is not relivant.
53555  *
53556  * Fork - LGPL
53557  * <script type="text/javascript">
53558  */
53559 /**
53560  * @class Roo.grid.CellSelectionModel
53561  * @extends Roo.grid.AbstractSelectionModel
53562  * This class provides the basic implementation for cell selection in a grid.
53563  * @constructor
53564  * @param {Object} config The object containing the configuration of this model.
53565  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
53566  */
53567 Roo.grid.CellSelectionModel = function(config){
53568     Roo.apply(this, config);
53569
53570     this.selection = null;
53571
53572     this.addEvents({
53573         /**
53574              * @event beforerowselect
53575              * Fires before a cell is selected.
53576              * @param {SelectionModel} this
53577              * @param {Number} rowIndex The selected row index
53578              * @param {Number} colIndex The selected cell index
53579              */
53580             "beforecellselect" : true,
53581         /**
53582              * @event cellselect
53583              * Fires when a cell is selected.
53584              * @param {SelectionModel} this
53585              * @param {Number} rowIndex The selected row index
53586              * @param {Number} colIndex The selected cell index
53587              */
53588             "cellselect" : true,
53589         /**
53590              * @event selectionchange
53591              * Fires when the active selection changes.
53592              * @param {SelectionModel} this
53593              * @param {Object} selection null for no selection or an object (o) with two properties
53594                 <ul>
53595                 <li>o.record: the record object for the row the selection is in</li>
53596                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
53597                 </ul>
53598              */
53599             "selectionchange" : true,
53600         /**
53601              * @event tabend
53602              * Fires when the tab (or enter) was pressed on the last editable cell
53603              * You can use this to trigger add new row.
53604              * @param {SelectionModel} this
53605              */
53606             "tabend" : true,
53607          /**
53608              * @event beforeeditnext
53609              * Fires before the next editable sell is made active
53610              * You can use this to skip to another cell or fire the tabend
53611              *    if you set cell to false
53612              * @param {Object} eventdata object : { cell : [ row, col ] } 
53613              */
53614             "beforeeditnext" : true
53615     });
53616     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
53617 };
53618
53619 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
53620     
53621     enter_is_tab: false,
53622
53623     /** @ignore */
53624     initEvents : function(){
53625         this.grid.on("mousedown", this.handleMouseDown, this);
53626         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
53627         var view = this.grid.view;
53628         view.on("refresh", this.onViewChange, this);
53629         view.on("rowupdated", this.onRowUpdated, this);
53630         view.on("beforerowremoved", this.clearSelections, this);
53631         view.on("beforerowsinserted", this.clearSelections, this);
53632         if(this.grid.isEditor){
53633             this.grid.on("beforeedit", this.beforeEdit,  this);
53634         }
53635     },
53636
53637         //private
53638     beforeEdit : function(e){
53639         this.select(e.row, e.column, false, true, e.record);
53640     },
53641
53642         //private
53643     onRowUpdated : function(v, index, r){
53644         if(this.selection && this.selection.record == r){
53645             v.onCellSelect(index, this.selection.cell[1]);
53646         }
53647     },
53648
53649         //private
53650     onViewChange : function(){
53651         this.clearSelections(true);
53652     },
53653
53654         /**
53655          * Returns the currently selected cell,.
53656          * @return {Array} The selected cell (row, column) or null if none selected.
53657          */
53658     getSelectedCell : function(){
53659         return this.selection ? this.selection.cell : null;
53660     },
53661
53662     /**
53663      * Clears all selections.
53664      * @param {Boolean} true to prevent the gridview from being notified about the change.
53665      */
53666     clearSelections : function(preventNotify){
53667         var s = this.selection;
53668         if(s){
53669             if(preventNotify !== true){
53670                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
53671             }
53672             this.selection = null;
53673             this.fireEvent("selectionchange", this, null);
53674         }
53675     },
53676
53677     /**
53678      * Returns true if there is a selection.
53679      * @return {Boolean}
53680      */
53681     hasSelection : function(){
53682         return this.selection ? true : false;
53683     },
53684
53685     /** @ignore */
53686     handleMouseDown : function(e, t){
53687         var v = this.grid.getView();
53688         if(this.isLocked()){
53689             return;
53690         };
53691         var row = v.findRowIndex(t);
53692         var cell = v.findCellIndex(t);
53693         if(row !== false && cell !== false){
53694             this.select(row, cell);
53695         }
53696     },
53697
53698     /**
53699      * Selects a cell.
53700      * @param {Number} rowIndex
53701      * @param {Number} collIndex
53702      */
53703     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
53704         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
53705             this.clearSelections();
53706             r = r || this.grid.dataSource.getAt(rowIndex);
53707             this.selection = {
53708                 record : r,
53709                 cell : [rowIndex, colIndex]
53710             };
53711             if(!preventViewNotify){
53712                 var v = this.grid.getView();
53713                 v.onCellSelect(rowIndex, colIndex);
53714                 if(preventFocus !== true){
53715                     v.focusCell(rowIndex, colIndex);
53716                 }
53717             }
53718             this.fireEvent("cellselect", this, rowIndex, colIndex);
53719             this.fireEvent("selectionchange", this, this.selection);
53720         }
53721     },
53722
53723         //private
53724     isSelectable : function(rowIndex, colIndex, cm){
53725         return !cm.isHidden(colIndex);
53726     },
53727
53728     /** @ignore */
53729     handleKeyDown : function(e){
53730         //Roo.log('Cell Sel Model handleKeyDown');
53731         if(!e.isNavKeyPress()){
53732             return;
53733         }
53734         var g = this.grid, s = this.selection;
53735         if(!s){
53736             e.stopEvent();
53737             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
53738             if(cell){
53739                 this.select(cell[0], cell[1]);
53740             }
53741             return;
53742         }
53743         var sm = this;
53744         var walk = function(row, col, step){
53745             return g.walkCells(row, col, step, sm.isSelectable,  sm);
53746         };
53747         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
53748         var newCell;
53749
53750       
53751
53752         switch(k){
53753             case e.TAB:
53754                 // handled by onEditorKey
53755                 if (g.isEditor && g.editing) {
53756                     return;
53757                 }
53758                 if(e.shiftKey) {
53759                     newCell = walk(r, c-1, -1);
53760                 } else {
53761                     newCell = walk(r, c+1, 1);
53762                 }
53763                 break;
53764             
53765             case e.DOWN:
53766                newCell = walk(r+1, c, 1);
53767                 break;
53768             
53769             case e.UP:
53770                 newCell = walk(r-1, c, -1);
53771                 break;
53772             
53773             case e.RIGHT:
53774                 newCell = walk(r, c+1, 1);
53775                 break;
53776             
53777             case e.LEFT:
53778                 newCell = walk(r, c-1, -1);
53779                 break;
53780             
53781             case e.ENTER:
53782                 
53783                 if(g.isEditor && !g.editing){
53784                    g.startEditing(r, c);
53785                    e.stopEvent();
53786                    return;
53787                 }
53788                 
53789                 
53790              break;
53791         };
53792         if(newCell){
53793             this.select(newCell[0], newCell[1]);
53794             e.stopEvent();
53795             
53796         }
53797     },
53798
53799     acceptsNav : function(row, col, cm){
53800         return !cm.isHidden(col) && cm.isCellEditable(col, row);
53801     },
53802     /**
53803      * Selects a cell.
53804      * @param {Number} field (not used) - as it's normally used as a listener
53805      * @param {Number} e - event - fake it by using
53806      *
53807      * var e = Roo.EventObjectImpl.prototype;
53808      * e.keyCode = e.TAB
53809      *
53810      * 
53811      */
53812     onEditorKey : function(field, e){
53813         
53814         var k = e.getKey(),
53815             newCell,
53816             g = this.grid,
53817             ed = g.activeEditor,
53818             forward = false;
53819         ///Roo.log('onEditorKey' + k);
53820         
53821         
53822         if (this.enter_is_tab && k == e.ENTER) {
53823             k = e.TAB;
53824         }
53825         
53826         if(k == e.TAB){
53827             if(e.shiftKey){
53828                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
53829             }else{
53830                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
53831                 forward = true;
53832             }
53833             
53834             e.stopEvent();
53835             
53836         } else if(k == e.ENTER &&  !e.ctrlKey){
53837             ed.completeEdit();
53838             e.stopEvent();
53839             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
53840         
53841                 } else if(k == e.ESC){
53842             ed.cancelEdit();
53843         }
53844                 
53845         if (newCell) {
53846             var ecall = { cell : newCell, forward : forward };
53847             this.fireEvent('beforeeditnext', ecall );
53848             newCell = ecall.cell;
53849                         forward = ecall.forward;
53850         }
53851                 
53852         if(newCell){
53853             //Roo.log('next cell after edit');
53854             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
53855         } else if (forward) {
53856             // tabbed past last
53857             this.fireEvent.defer(100, this, ['tabend',this]);
53858         }
53859     }
53860 });/*
53861  * Based on:
53862  * Ext JS Library 1.1.1
53863  * Copyright(c) 2006-2007, Ext JS, LLC.
53864  *
53865  * Originally Released Under LGPL - original licence link has changed is not relivant.
53866  *
53867  * Fork - LGPL
53868  * <script type="text/javascript">
53869  */
53870  
53871 /**
53872  * @class Roo.grid.EditorGrid
53873  * @extends Roo.grid.Grid
53874  * Class for creating and editable grid.
53875  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
53876  * The container MUST have some type of size defined for the grid to fill. The container will be 
53877  * automatically set to position relative if it isn't already.
53878  * @param {Object} dataSource The data model to bind to
53879  * @param {Object} colModel The column model with info about this grid's columns
53880  */
53881 Roo.grid.EditorGrid = function(container, config){
53882     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
53883     this.getGridEl().addClass("xedit-grid");
53884
53885     if(!this.selModel){
53886         this.selModel = new Roo.grid.CellSelectionModel();
53887     }
53888
53889     this.activeEditor = null;
53890
53891         this.addEvents({
53892             /**
53893              * @event beforeedit
53894              * Fires before cell editing is triggered. The edit event object has the following properties <br />
53895              * <ul style="padding:5px;padding-left:16px;">
53896              * <li>grid - This grid</li>
53897              * <li>record - The record being edited</li>
53898              * <li>field - The field name being edited</li>
53899              * <li>value - The value for the field being edited.</li>
53900              * <li>row - The grid row index</li>
53901              * <li>column - The grid column index</li>
53902              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
53903              * </ul>
53904              * @param {Object} e An edit event (see above for description)
53905              */
53906             "beforeedit" : true,
53907             /**
53908              * @event afteredit
53909              * Fires after a cell is edited. <br />
53910              * <ul style="padding:5px;padding-left:16px;">
53911              * <li>grid - This grid</li>
53912              * <li>record - The record being edited</li>
53913              * <li>field - The field name being edited</li>
53914              * <li>value - The value being set</li>
53915              * <li>originalValue - The original value for the field, before the edit.</li>
53916              * <li>row - The grid row index</li>
53917              * <li>column - The grid column index</li>
53918              * </ul>
53919              * @param {Object} e An edit event (see above for description)
53920              */
53921             "afteredit" : true,
53922             /**
53923              * @event validateedit
53924              * Fires after a cell is edited, but before the value is set in the record. 
53925          * You can use this to modify the value being set in the field, Return false
53926              * to cancel the change. The edit event object has the following properties <br />
53927              * <ul style="padding:5px;padding-left:16px;">
53928          * <li>editor - This editor</li>
53929              * <li>grid - This grid</li>
53930              * <li>record - The record being edited</li>
53931              * <li>field - The field name being edited</li>
53932              * <li>value - The value being set</li>
53933              * <li>originalValue - The original value for the field, before the edit.</li>
53934              * <li>row - The grid row index</li>
53935              * <li>column - The grid column index</li>
53936              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
53937              * </ul>
53938              * @param {Object} e An edit event (see above for description)
53939              */
53940             "validateedit" : true
53941         });
53942     this.on("bodyscroll", this.stopEditing,  this);
53943     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
53944 };
53945
53946 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
53947     /**
53948      * @cfg {Number} clicksToEdit
53949      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
53950      */
53951     clicksToEdit: 2,
53952
53953     // private
53954     isEditor : true,
53955     // private
53956     trackMouseOver: false, // causes very odd FF errors
53957
53958     onCellDblClick : function(g, row, col){
53959         this.startEditing(row, col);
53960     },
53961
53962     onEditComplete : function(ed, value, startValue){
53963         this.editing = false;
53964         this.activeEditor = null;
53965         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
53966         var r = ed.record;
53967         var field = this.colModel.getDataIndex(ed.col);
53968         var e = {
53969             grid: this,
53970             record: r,
53971             field: field,
53972             originalValue: startValue,
53973             value: value,
53974             row: ed.row,
53975             column: ed.col,
53976             cancel:false,
53977             editor: ed
53978         };
53979         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
53980         cell.show();
53981           
53982         if(String(value) !== String(startValue)){
53983             
53984             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
53985                 r.set(field, e.value);
53986                 // if we are dealing with a combo box..
53987                 // then we also set the 'name' colum to be the displayField
53988                 if (ed.field.displayField && ed.field.name) {
53989                     r.set(ed.field.name, ed.field.el.dom.value);
53990                 }
53991                 
53992                 delete e.cancel; //?? why!!!
53993                 this.fireEvent("afteredit", e);
53994             }
53995         } else {
53996             this.fireEvent("afteredit", e); // always fire it!
53997         }
53998         this.view.focusCell(ed.row, ed.col);
53999     },
54000
54001     /**
54002      * Starts editing the specified for the specified row/column
54003      * @param {Number} rowIndex
54004      * @param {Number} colIndex
54005      */
54006     startEditing : function(row, col){
54007         this.stopEditing();
54008         if(this.colModel.isCellEditable(col, row)){
54009             this.view.ensureVisible(row, col, true);
54010           
54011             var r = this.dataSource.getAt(row);
54012             var field = this.colModel.getDataIndex(col);
54013             var cell = Roo.get(this.view.getCell(row,col));
54014             var e = {
54015                 grid: this,
54016                 record: r,
54017                 field: field,
54018                 value: r.data[field],
54019                 row: row,
54020                 column: col,
54021                 cancel:false 
54022             };
54023             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
54024                 this.editing = true;
54025                 var ed = this.colModel.getCellEditor(col, row);
54026                 
54027                 if (!ed) {
54028                     return;
54029                 }
54030                 if(!ed.rendered){
54031                     ed.render(ed.parentEl || document.body);
54032                 }
54033                 ed.field.reset();
54034                
54035                 cell.hide();
54036                 
54037                 (function(){ // complex but required for focus issues in safari, ie and opera
54038                     ed.row = row;
54039                     ed.col = col;
54040                     ed.record = r;
54041                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
54042                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
54043                     this.activeEditor = ed;
54044                     var v = r.data[field];
54045                     ed.startEdit(this.view.getCell(row, col), v);
54046                     // combo's with 'displayField and name set
54047                     if (ed.field.displayField && ed.field.name) {
54048                         ed.field.el.dom.value = r.data[ed.field.name];
54049                     }
54050                     
54051                     
54052                 }).defer(50, this);
54053             }
54054         }
54055     },
54056         
54057     /**
54058      * Stops any active editing
54059      */
54060     stopEditing : function(){
54061         if(this.activeEditor){
54062             this.activeEditor.completeEdit();
54063         }
54064         this.activeEditor = null;
54065     },
54066         
54067          /**
54068      * Called to get grid's drag proxy text, by default returns this.ddText.
54069      * @return {String}
54070      */
54071     getDragDropText : function(){
54072         var count = this.selModel.getSelectedCell() ? 1 : 0;
54073         return String.format(this.ddText, count, count == 1 ? '' : 's');
54074     }
54075         
54076 });/*
54077  * Based on:
54078  * Ext JS Library 1.1.1
54079  * Copyright(c) 2006-2007, Ext JS, LLC.
54080  *
54081  * Originally Released Under LGPL - original licence link has changed is not relivant.
54082  *
54083  * Fork - LGPL
54084  * <script type="text/javascript">
54085  */
54086
54087 // private - not really -- you end up using it !
54088 // This is a support class used internally by the Grid components
54089
54090 /**
54091  * @class Roo.grid.GridEditor
54092  * @extends Roo.Editor
54093  * Class for creating and editable grid elements.
54094  * @param {Object} config any settings (must include field)
54095  */
54096 Roo.grid.GridEditor = function(field, config){
54097     if (!config && field.field) {
54098         config = field;
54099         field = Roo.factory(config.field, Roo.form);
54100     }
54101     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
54102     field.monitorTab = false;
54103 };
54104
54105 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
54106     
54107     /**
54108      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
54109      */
54110     
54111     alignment: "tl-tl",
54112     autoSize: "width",
54113     hideEl : false,
54114     cls: "x-small-editor x-grid-editor",
54115     shim:false,
54116     shadow:"frame"
54117 });/*
54118  * Based on:
54119  * Ext JS Library 1.1.1
54120  * Copyright(c) 2006-2007, Ext JS, LLC.
54121  *
54122  * Originally Released Under LGPL - original licence link has changed is not relivant.
54123  *
54124  * Fork - LGPL
54125  * <script type="text/javascript">
54126  */
54127   
54128
54129   
54130 Roo.grid.PropertyRecord = Roo.data.Record.create([
54131     {name:'name',type:'string'},  'value'
54132 ]);
54133
54134
54135 Roo.grid.PropertyStore = function(grid, source){
54136     this.grid = grid;
54137     this.store = new Roo.data.Store({
54138         recordType : Roo.grid.PropertyRecord
54139     });
54140     this.store.on('update', this.onUpdate,  this);
54141     if(source){
54142         this.setSource(source);
54143     }
54144     Roo.grid.PropertyStore.superclass.constructor.call(this);
54145 };
54146
54147
54148
54149 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
54150     setSource : function(o){
54151         this.source = o;
54152         this.store.removeAll();
54153         var data = [];
54154         for(var k in o){
54155             if(this.isEditableValue(o[k])){
54156                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
54157             }
54158         }
54159         this.store.loadRecords({records: data}, {}, true);
54160     },
54161
54162     onUpdate : function(ds, record, type){
54163         if(type == Roo.data.Record.EDIT){
54164             var v = record.data['value'];
54165             var oldValue = record.modified['value'];
54166             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
54167                 this.source[record.id] = v;
54168                 record.commit();
54169                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
54170             }else{
54171                 record.reject();
54172             }
54173         }
54174     },
54175
54176     getProperty : function(row){
54177        return this.store.getAt(row);
54178     },
54179
54180     isEditableValue: function(val){
54181         if(val && val instanceof Date){
54182             return true;
54183         }else if(typeof val == 'object' || typeof val == 'function'){
54184             return false;
54185         }
54186         return true;
54187     },
54188
54189     setValue : function(prop, value){
54190         this.source[prop] = value;
54191         this.store.getById(prop).set('value', value);
54192     },
54193
54194     getSource : function(){
54195         return this.source;
54196     }
54197 });
54198
54199 Roo.grid.PropertyColumnModel = function(grid, store){
54200     this.grid = grid;
54201     var g = Roo.grid;
54202     g.PropertyColumnModel.superclass.constructor.call(this, [
54203         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
54204         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
54205     ]);
54206     this.store = store;
54207     this.bselect = Roo.DomHelper.append(document.body, {
54208         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
54209             {tag: 'option', value: 'true', html: 'true'},
54210             {tag: 'option', value: 'false', html: 'false'}
54211         ]
54212     });
54213     Roo.id(this.bselect);
54214     var f = Roo.form;
54215     this.editors = {
54216         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
54217         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
54218         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
54219         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
54220         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
54221     };
54222     this.renderCellDelegate = this.renderCell.createDelegate(this);
54223     this.renderPropDelegate = this.renderProp.createDelegate(this);
54224 };
54225
54226 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
54227     
54228     
54229     nameText : 'Name',
54230     valueText : 'Value',
54231     
54232     dateFormat : 'm/j/Y',
54233     
54234     
54235     renderDate : function(dateVal){
54236         return dateVal.dateFormat(this.dateFormat);
54237     },
54238
54239     renderBool : function(bVal){
54240         return bVal ? 'true' : 'false';
54241     },
54242
54243     isCellEditable : function(colIndex, rowIndex){
54244         return colIndex == 1;
54245     },
54246
54247     getRenderer : function(col){
54248         return col == 1 ?
54249             this.renderCellDelegate : this.renderPropDelegate;
54250     },
54251
54252     renderProp : function(v){
54253         return this.getPropertyName(v);
54254     },
54255
54256     renderCell : function(val){
54257         var rv = val;
54258         if(val instanceof Date){
54259             rv = this.renderDate(val);
54260         }else if(typeof val == 'boolean'){
54261             rv = this.renderBool(val);
54262         }
54263         return Roo.util.Format.htmlEncode(rv);
54264     },
54265
54266     getPropertyName : function(name){
54267         var pn = this.grid.propertyNames;
54268         return pn && pn[name] ? pn[name] : name;
54269     },
54270
54271     getCellEditor : function(colIndex, rowIndex){
54272         var p = this.store.getProperty(rowIndex);
54273         var n = p.data['name'], val = p.data['value'];
54274         
54275         if(typeof(this.grid.customEditors[n]) == 'string'){
54276             return this.editors[this.grid.customEditors[n]];
54277         }
54278         if(typeof(this.grid.customEditors[n]) != 'undefined'){
54279             return this.grid.customEditors[n];
54280         }
54281         if(val instanceof Date){
54282             return this.editors['date'];
54283         }else if(typeof val == 'number'){
54284             return this.editors['number'];
54285         }else if(typeof val == 'boolean'){
54286             return this.editors['boolean'];
54287         }else{
54288             return this.editors['string'];
54289         }
54290     }
54291 });
54292
54293 /**
54294  * @class Roo.grid.PropertyGrid
54295  * @extends Roo.grid.EditorGrid
54296  * This class represents the  interface of a component based property grid control.
54297  * <br><br>Usage:<pre><code>
54298  var grid = new Roo.grid.PropertyGrid("my-container-id", {
54299       
54300  });
54301  // set any options
54302  grid.render();
54303  * </code></pre>
54304   
54305  * @constructor
54306  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54307  * The container MUST have some type of size defined for the grid to fill. The container will be
54308  * automatically set to position relative if it isn't already.
54309  * @param {Object} config A config object that sets properties on this grid.
54310  */
54311 Roo.grid.PropertyGrid = function(container, config){
54312     config = config || {};
54313     var store = new Roo.grid.PropertyStore(this);
54314     this.store = store;
54315     var cm = new Roo.grid.PropertyColumnModel(this, store);
54316     store.store.sort('name', 'ASC');
54317     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
54318         ds: store.store,
54319         cm: cm,
54320         enableColLock:false,
54321         enableColumnMove:false,
54322         stripeRows:false,
54323         trackMouseOver: false,
54324         clicksToEdit:1
54325     }, config));
54326     this.getGridEl().addClass('x-props-grid');
54327     this.lastEditRow = null;
54328     this.on('columnresize', this.onColumnResize, this);
54329     this.addEvents({
54330          /**
54331              * @event beforepropertychange
54332              * Fires before a property changes (return false to stop?)
54333              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
54334              * @param {String} id Record Id
54335              * @param {String} newval New Value
54336          * @param {String} oldval Old Value
54337              */
54338         "beforepropertychange": true,
54339         /**
54340              * @event propertychange
54341              * Fires after a property changes
54342              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
54343              * @param {String} id Record Id
54344              * @param {String} newval New Value
54345          * @param {String} oldval Old Value
54346              */
54347         "propertychange": true
54348     });
54349     this.customEditors = this.customEditors || {};
54350 };
54351 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
54352     
54353      /**
54354      * @cfg {Object} customEditors map of colnames=> custom editors.
54355      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
54356      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
54357      * false disables editing of the field.
54358          */
54359     
54360       /**
54361      * @cfg {Object} propertyNames map of property Names to their displayed value
54362          */
54363     
54364     render : function(){
54365         Roo.grid.PropertyGrid.superclass.render.call(this);
54366         this.autoSize.defer(100, this);
54367     },
54368
54369     autoSize : function(){
54370         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
54371         if(this.view){
54372             this.view.fitColumns();
54373         }
54374     },
54375
54376     onColumnResize : function(){
54377         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
54378         this.autoSize();
54379     },
54380     /**
54381      * Sets the data for the Grid
54382      * accepts a Key => Value object of all the elements avaiable.
54383      * @param {Object} data  to appear in grid.
54384      */
54385     setSource : function(source){
54386         this.store.setSource(source);
54387         //this.autoSize();
54388     },
54389     /**
54390      * Gets all the data from the grid.
54391      * @return {Object} data  data stored in grid
54392      */
54393     getSource : function(){
54394         return this.store.getSource();
54395     }
54396 });/*
54397  * Based on:
54398  * Ext JS Library 1.1.1
54399  * Copyright(c) 2006-2007, Ext JS, LLC.
54400  *
54401  * Originally Released Under LGPL - original licence link has changed is not relivant.
54402  *
54403  * Fork - LGPL
54404  * <script type="text/javascript">
54405  */
54406  
54407 /**
54408  * @class Roo.LoadMask
54409  * A simple utility class for generically masking elements while loading data.  If the element being masked has
54410  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
54411  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
54412  * element's UpdateManager load indicator and will be destroyed after the initial load.
54413  * @constructor
54414  * Create a new LoadMask
54415  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
54416  * @param {Object} config The config object
54417  */
54418 Roo.LoadMask = function(el, config){
54419     this.el = Roo.get(el);
54420     Roo.apply(this, config);
54421     if(this.store){
54422         this.store.on('beforeload', this.onBeforeLoad, this);
54423         this.store.on('load', this.onLoad, this);
54424         this.store.on('loadexception', this.onLoadException, this);
54425         this.removeMask = false;
54426     }else{
54427         var um = this.el.getUpdateManager();
54428         um.showLoadIndicator = false; // disable the default indicator
54429         um.on('beforeupdate', this.onBeforeLoad, this);
54430         um.on('update', this.onLoad, this);
54431         um.on('failure', this.onLoad, this);
54432         this.removeMask = true;
54433     }
54434 };
54435
54436 Roo.LoadMask.prototype = {
54437     /**
54438      * @cfg {Boolean} removeMask
54439      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
54440      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
54441      */
54442     /**
54443      * @cfg {String} msg
54444      * The text to display in a centered loading message box (defaults to 'Loading...')
54445      */
54446     msg : 'Loading...',
54447     /**
54448      * @cfg {String} msgCls
54449      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
54450      */
54451     msgCls : 'x-mask-loading',
54452
54453     /**
54454      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
54455      * @type Boolean
54456      */
54457     disabled: false,
54458
54459     /**
54460      * Disables the mask to prevent it from being displayed
54461      */
54462     disable : function(){
54463        this.disabled = true;
54464     },
54465
54466     /**
54467      * Enables the mask so that it can be displayed
54468      */
54469     enable : function(){
54470         this.disabled = false;
54471     },
54472     
54473     onLoadException : function()
54474     {
54475         Roo.log(arguments);
54476         
54477         if (typeof(arguments[3]) != 'undefined') {
54478             Roo.MessageBox.alert("Error loading",arguments[3]);
54479         } 
54480         /*
54481         try {
54482             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
54483                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
54484             }   
54485         } catch(e) {
54486             
54487         }
54488         */
54489     
54490         
54491         
54492         this.el.unmask(this.removeMask);
54493     },
54494     // private
54495     onLoad : function()
54496     {
54497         this.el.unmask(this.removeMask);
54498     },
54499
54500     // private
54501     onBeforeLoad : function(){
54502         if(!this.disabled){
54503             this.el.mask(this.msg, this.msgCls);
54504         }
54505     },
54506
54507     // private
54508     destroy : function(){
54509         if(this.store){
54510             this.store.un('beforeload', this.onBeforeLoad, this);
54511             this.store.un('load', this.onLoad, this);
54512             this.store.un('loadexception', this.onLoadException, this);
54513         }else{
54514             var um = this.el.getUpdateManager();
54515             um.un('beforeupdate', this.onBeforeLoad, this);
54516             um.un('update', this.onLoad, this);
54517             um.un('failure', this.onLoad, this);
54518         }
54519     }
54520 };/*
54521  * Based on:
54522  * Ext JS Library 1.1.1
54523  * Copyright(c) 2006-2007, Ext JS, LLC.
54524  *
54525  * Originally Released Under LGPL - original licence link has changed is not relivant.
54526  *
54527  * Fork - LGPL
54528  * <script type="text/javascript">
54529  */
54530
54531
54532 /**
54533  * @class Roo.XTemplate
54534  * @extends Roo.Template
54535  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
54536 <pre><code>
54537 var t = new Roo.XTemplate(
54538         '&lt;select name="{name}"&gt;',
54539                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
54540         '&lt;/select&gt;'
54541 );
54542  
54543 // then append, applying the master template values
54544  </code></pre>
54545  *
54546  * Supported features:
54547  *
54548  *  Tags:
54549
54550 <pre><code>
54551       {a_variable} - output encoded.
54552       {a_variable.format:("Y-m-d")} - call a method on the variable
54553       {a_variable:raw} - unencoded output
54554       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
54555       {a_variable:this.method_on_template(...)} - call a method on the template object.
54556  
54557 </code></pre>
54558  *  The tpl tag:
54559 <pre><code>
54560         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
54561         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
54562         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
54563         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
54564   
54565         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
54566         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
54567 </code></pre>
54568  *      
54569  */
54570 Roo.XTemplate = function()
54571 {
54572     Roo.XTemplate.superclass.constructor.apply(this, arguments);
54573     if (this.html) {
54574         this.compile();
54575     }
54576 };
54577
54578
54579 Roo.extend(Roo.XTemplate, Roo.Template, {
54580
54581     /**
54582      * The various sub templates
54583      */
54584     tpls : false,
54585     /**
54586      *
54587      * basic tag replacing syntax
54588      * WORD:WORD()
54589      *
54590      * // you can fake an object call by doing this
54591      *  x.t:(test,tesT) 
54592      * 
54593      */
54594     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
54595
54596     /**
54597      * compile the template
54598      *
54599      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
54600      *
54601      */
54602     compile: function()
54603     {
54604         var s = this.html;
54605      
54606         s = ['<tpl>', s, '</tpl>'].join('');
54607     
54608         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
54609             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
54610             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
54611             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
54612             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
54613             m,
54614             id     = 0,
54615             tpls   = [];
54616     
54617         while(true == !!(m = s.match(re))){
54618             var forMatch   = m[0].match(nameRe),
54619                 ifMatch   = m[0].match(ifRe),
54620                 execMatch   = m[0].match(execRe),
54621                 namedMatch   = m[0].match(namedRe),
54622                 
54623                 exp  = null, 
54624                 fn   = null,
54625                 exec = null,
54626                 name = forMatch && forMatch[1] ? forMatch[1] : '';
54627                 
54628             if (ifMatch) {
54629                 // if - puts fn into test..
54630                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
54631                 if(exp){
54632                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
54633                 }
54634             }
54635             
54636             if (execMatch) {
54637                 // exec - calls a function... returns empty if true is  returned.
54638                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
54639                 if(exp){
54640                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
54641                 }
54642             }
54643             
54644             
54645             if (name) {
54646                 // for = 
54647                 switch(name){
54648                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
54649                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
54650                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
54651                 }
54652             }
54653             var uid = namedMatch ? namedMatch[1] : id;
54654             
54655             
54656             tpls.push({
54657                 id:     namedMatch ? namedMatch[1] : id,
54658                 target: name,
54659                 exec:   exec,
54660                 test:   fn,
54661                 body:   m[1] || ''
54662             });
54663             if (namedMatch) {
54664                 s = s.replace(m[0], '');
54665             } else { 
54666                 s = s.replace(m[0], '{xtpl'+ id + '}');
54667             }
54668             ++id;
54669         }
54670         this.tpls = [];
54671         for(var i = tpls.length-1; i >= 0; --i){
54672             this.compileTpl(tpls[i]);
54673             this.tpls[tpls[i].id] = tpls[i];
54674         }
54675         this.master = tpls[tpls.length-1];
54676         return this;
54677     },
54678     /**
54679      * same as applyTemplate, except it's done to one of the subTemplates
54680      * when using named templates, you can do:
54681      *
54682      * var str = pl.applySubTemplate('your-name', values);
54683      *
54684      * 
54685      * @param {Number} id of the template
54686      * @param {Object} values to apply to template
54687      * @param {Object} parent (normaly the instance of this object)
54688      */
54689     applySubTemplate : function(id, values, parent)
54690     {
54691         
54692         
54693         var t = this.tpls[id];
54694         
54695         
54696         try { 
54697             if(t.test && !t.test.call(this, values, parent)){
54698                 return '';
54699             }
54700         } catch(e) {
54701             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
54702             Roo.log(e.toString());
54703             Roo.log(t.test);
54704             return ''
54705         }
54706         try { 
54707             
54708             if(t.exec && t.exec.call(this, values, parent)){
54709                 return '';
54710             }
54711         } catch(e) {
54712             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
54713             Roo.log(e.toString());
54714             Roo.log(t.exec);
54715             return ''
54716         }
54717         try {
54718             var vs = t.target ? t.target.call(this, values, parent) : values;
54719             parent = t.target ? values : parent;
54720             if(t.target && vs instanceof Array){
54721                 var buf = [];
54722                 for(var i = 0, len = vs.length; i < len; i++){
54723                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
54724                 }
54725                 return buf.join('');
54726             }
54727             return t.compiled.call(this, vs, parent);
54728         } catch (e) {
54729             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
54730             Roo.log(e.toString());
54731             Roo.log(t.compiled);
54732             return '';
54733         }
54734     },
54735
54736     compileTpl : function(tpl)
54737     {
54738         var fm = Roo.util.Format;
54739         var useF = this.disableFormats !== true;
54740         var sep = Roo.isGecko ? "+" : ",";
54741         var undef = function(str) {
54742             Roo.log("Property not found :"  + str);
54743             return '';
54744         };
54745         
54746         var fn = function(m, name, format, args)
54747         {
54748             //Roo.log(arguments);
54749             args = args ? args.replace(/\\'/g,"'") : args;
54750             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
54751             if (typeof(format) == 'undefined') {
54752                 format= 'htmlEncode';
54753             }
54754             if (format == 'raw' ) {
54755                 format = false;
54756             }
54757             
54758             if(name.substr(0, 4) == 'xtpl'){
54759                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
54760             }
54761             
54762             // build an array of options to determine if value is undefined..
54763             
54764             // basically get 'xxxx.yyyy' then do
54765             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
54766             //    (function () { Roo.log("Property not found"); return ''; })() :
54767             //    ......
54768             
54769             var udef_ar = [];
54770             var lookfor = '';
54771             Roo.each(name.split('.'), function(st) {
54772                 lookfor += (lookfor.length ? '.': '') + st;
54773                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
54774             });
54775             
54776             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
54777             
54778             
54779             if(format && useF){
54780                 
54781                 args = args ? ',' + args : "";
54782                  
54783                 if(format.substr(0, 5) != "this."){
54784                     format = "fm." + format + '(';
54785                 }else{
54786                     format = 'this.call("'+ format.substr(5) + '", ';
54787                     args = ", values";
54788                 }
54789                 
54790                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
54791             }
54792              
54793             if (args.length) {
54794                 // called with xxyx.yuu:(test,test)
54795                 // change to ()
54796                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
54797             }
54798             // raw.. - :raw modifier..
54799             return "'"+ sep + udef_st  + name + ")"+sep+"'";
54800             
54801         };
54802         var body;
54803         // branched to use + in gecko and [].join() in others
54804         if(Roo.isGecko){
54805             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
54806                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
54807                     "';};};";
54808         }else{
54809             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
54810             body.push(tpl.body.replace(/(\r\n|\n)/g,
54811                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
54812             body.push("'].join('');};};");
54813             body = body.join('');
54814         }
54815         
54816         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
54817        
54818         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
54819         eval(body);
54820         
54821         return this;
54822     },
54823
54824     applyTemplate : function(values){
54825         return this.master.compiled.call(this, values, {});
54826         //var s = this.subs;
54827     },
54828
54829     apply : function(){
54830         return this.applyTemplate.apply(this, arguments);
54831     }
54832
54833  });
54834
54835 Roo.XTemplate.from = function(el){
54836     el = Roo.getDom(el);
54837     return new Roo.XTemplate(el.value || el.innerHTML);
54838 };/*
54839  * Original code for Roojs - LGPL
54840  * <script type="text/javascript">
54841  */
54842  
54843 /**
54844  * @class Roo.XComponent
54845  * A delayed Element creator...
54846  * Or a way to group chunks of interface together.
54847  * 
54848  * Mypart.xyx = new Roo.XComponent({
54849
54850     parent : 'Mypart.xyz', // empty == document.element.!!
54851     order : '001',
54852     name : 'xxxx'
54853     region : 'xxxx'
54854     disabled : function() {} 
54855      
54856     tree : function() { // return an tree of xtype declared components
54857         var MODULE = this;
54858         return 
54859         {
54860             xtype : 'NestedLayoutPanel',
54861             // technicall
54862         }
54863      ]
54864  *})
54865  *
54866  *
54867  * It can be used to build a big heiracy, with parent etc.
54868  * or you can just use this to render a single compoent to a dom element
54869  * MYPART.render(Roo.Element | String(id) | dom_element )
54870  * 
54871  * @extends Roo.util.Observable
54872  * @constructor
54873  * @param cfg {Object} configuration of component
54874  * 
54875  */
54876 Roo.XComponent = function(cfg) {
54877     Roo.apply(this, cfg);
54878     this.addEvents({ 
54879         /**
54880              * @event built
54881              * Fires when this the componnt is built
54882              * @param {Roo.XComponent} c the component
54883              */
54884         'built' : true
54885         
54886     });
54887     this.region = this.region || 'center'; // default..
54888     Roo.XComponent.register(this);
54889     this.modules = false;
54890     this.el = false; // where the layout goes..
54891     
54892     
54893 }
54894 Roo.extend(Roo.XComponent, Roo.util.Observable, {
54895     /**
54896      * @property el
54897      * The created element (with Roo.factory())
54898      * @type {Roo.Layout}
54899      */
54900     el  : false,
54901     
54902     /**
54903      * @property el
54904      * for BC  - use el in new code
54905      * @type {Roo.Layout}
54906      */
54907     panel : false,
54908     
54909     /**
54910      * @property layout
54911      * for BC  - use el in new code
54912      * @type {Roo.Layout}
54913      */
54914     layout : false,
54915     
54916      /**
54917      * @cfg {Function|boolean} disabled
54918      * If this module is disabled by some rule, return true from the funtion
54919      */
54920     disabled : false,
54921     
54922     /**
54923      * @cfg {String} parent 
54924      * Name of parent element which it get xtype added to..
54925      */
54926     parent: false,
54927     
54928     /**
54929      * @cfg {String} order
54930      * Used to set the order in which elements are created (usefull for multiple tabs)
54931      */
54932     
54933     order : false,
54934     /**
54935      * @cfg {String} name
54936      * String to display while loading.
54937      */
54938     name : false,
54939     /**
54940      * @cfg {String} region
54941      * Region to render component to (defaults to center)
54942      */
54943     region : 'center',
54944     
54945     /**
54946      * @cfg {Array} items
54947      * A single item array - the first element is the root of the tree..
54948      * It's done this way to stay compatible with the Xtype system...
54949      */
54950     items : false,
54951     
54952     /**
54953      * @property _tree
54954      * The method that retuns the tree of parts that make up this compoennt 
54955      * @type {function}
54956      */
54957     _tree  : false,
54958     
54959      /**
54960      * render
54961      * render element to dom or tree
54962      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
54963      */
54964     
54965     render : function(el)
54966     {
54967         
54968         el = el || false;
54969         var hp = this.parent ? 1 : 0;
54970         
54971         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
54972             // if parent is a '#.....' string, then let's use that..
54973             var ename = this.parent.substr(1)
54974             this.parent = false;
54975             el = Roo.get(ename);
54976             if (!el) {
54977                 Roo.log("Warning - element can not be found :#" + ename );
54978                 return;
54979             }
54980         }
54981         
54982         
54983         if (!this.parent) {
54984             
54985             el = el ? Roo.get(el) : false;      
54986             
54987             // it's a top level one..
54988             this.parent =  {
54989                 el : new Roo.BorderLayout(el || document.body, {
54990                 
54991                      center: {
54992                          titlebar: false,
54993                          autoScroll:false,
54994                          closeOnTab: true,
54995                          tabPosition: 'top',
54996                           //resizeTabs: true,
54997                          alwaysShowTabs: el && hp? false :  true,
54998                          hideTabs: el || !hp ? true :  false,
54999                          minTabWidth: 140
55000                      }
55001                  })
55002             }
55003         }
55004         
55005                 if (!this.parent.el) {
55006                         // probably an old style ctor, which has been disabled.
55007                         return;
55008                         
55009                 }
55010                 // The 'tree' method is  '_tree now' 
55011             
55012         var tree = this._tree ? this._tree() : this.tree();
55013         tree.region = tree.region || this.region;
55014         this.el = this.parent.el.addxtype(tree);
55015         this.fireEvent('built', this);
55016         
55017         this.panel = this.el;
55018         this.layout = this.panel.layout;
55019                 this.parentLayout = this.parent.layout  || false;  
55020          
55021     }
55022     
55023 });
55024
55025 Roo.apply(Roo.XComponent, {
55026     /**
55027      * @property  hideProgress
55028      * true to disable the building progress bar.. usefull on single page renders.
55029      * @type Boolean
55030      */
55031     hideProgress : false,
55032     /**
55033      * @property  buildCompleted
55034      * True when the builder has completed building the interface.
55035      * @type Boolean
55036      */
55037     buildCompleted : false,
55038      
55039     /**
55040      * @property  topModule
55041      * the upper most module - uses document.element as it's constructor.
55042      * @type Object
55043      */
55044      
55045     topModule  : false,
55046       
55047     /**
55048      * @property  modules
55049      * array of modules to be created by registration system.
55050      * @type {Array} of Roo.XComponent
55051      */
55052     
55053     modules : [],
55054     /**
55055      * @property  elmodules
55056      * array of modules to be created by which use #ID 
55057      * @type {Array} of Roo.XComponent
55058      */
55059      
55060     elmodules : [],
55061
55062     
55063     /**
55064      * Register components to be built later.
55065      *
55066      * This solves the following issues
55067      * - Building is not done on page load, but after an authentication process has occured.
55068      * - Interface elements are registered on page load
55069      * - Parent Interface elements may not be loaded before child, so this handles that..
55070      * 
55071      *
55072      * example:
55073      * 
55074      * MyApp.register({
55075           order : '000001',
55076           module : 'Pman.Tab.projectMgr',
55077           region : 'center',
55078           parent : 'Pman.layout',
55079           disabled : false,  // or use a function..
55080         })
55081      
55082      * * @param {Object} details about module
55083      */
55084     register : function(obj) {
55085                 
55086         Roo.XComponent.event.fireEvent('register', obj);
55087         switch(typeof(obj.disabled) ) {
55088                 
55089             case 'undefined':
55090                 break;
55091             
55092             case 'function':
55093                 if ( obj.disabled() ) {
55094                         return;
55095                 }
55096                 break;
55097             
55098             default:
55099                 if (obj.disabled) {
55100                         return;
55101                 }
55102                 break;
55103         }
55104                 
55105         this.modules.push(obj);
55106          
55107     },
55108     /**
55109      * convert a string to an object..
55110      * eg. 'AAA.BBB' -> finds AAA.BBB
55111
55112      */
55113     
55114     toObject : function(str)
55115     {
55116         if (!str || typeof(str) == 'object') {
55117             return str;
55118         }
55119         if (str.substring(0,1) == '#') {
55120             return str;
55121         }
55122
55123         var ar = str.split('.');
55124         var rt, o;
55125         rt = ar.shift();
55126             /** eval:var:o */
55127         try {
55128             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
55129         } catch (e) {
55130             throw "Module not found : " + str;
55131         }
55132         
55133         if (o === false) {
55134             throw "Module not found : " + str;
55135         }
55136         Roo.each(ar, function(e) {
55137             if (typeof(o[e]) == 'undefined') {
55138                 throw "Module not found : " + str;
55139             }
55140             o = o[e];
55141         });
55142         
55143         return o;
55144         
55145     },
55146     
55147     
55148     /**
55149      * move modules into their correct place in the tree..
55150      * 
55151      */
55152     preBuild : function ()
55153     {
55154         var _t = this;
55155         Roo.each(this.modules , function (obj)
55156         {
55157             Roo.XComponent.event.fireEvent('beforebuild', obj);
55158             
55159             var opar = obj.parent;
55160             try { 
55161                 obj.parent = this.toObject(opar);
55162             } catch(e) {
55163                 Roo.log("parent:toObject failed: " + e.toString());
55164                 return;
55165             }
55166             
55167             if (!obj.parent) {
55168                 Roo.debug && Roo.log("GOT top level module");
55169                 Roo.debug && Roo.log(obj);
55170                 obj.modules = new Roo.util.MixedCollection(false, 
55171                     function(o) { return o.order + '' }
55172                 );
55173                 this.topModule = obj;
55174                 return;
55175             }
55176                         // parent is a string (usually a dom element name..)
55177             if (typeof(obj.parent) == 'string') {
55178                 this.elmodules.push(obj);
55179                 return;
55180             }
55181             if (obj.parent.constructor != Roo.XComponent) {
55182                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
55183             }
55184             if (!obj.parent.modules) {
55185                 obj.parent.modules = new Roo.util.MixedCollection(false, 
55186                     function(o) { return o.order + '' }
55187                 );
55188             }
55189             if (obj.parent.disabled) {
55190                 obj.disabled = true;
55191             }
55192             obj.parent.modules.add(obj);
55193         }, this);
55194     },
55195     
55196      /**
55197      * make a list of modules to build.
55198      * @return {Array} list of modules. 
55199      */ 
55200     
55201     buildOrder : function()
55202     {
55203         var _this = this;
55204         var cmp = function(a,b) {   
55205             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
55206         };
55207         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
55208             throw "No top level modules to build";
55209         }
55210         
55211         // make a flat list in order of modules to build.
55212         var mods = this.topModule ? [ this.topModule ] : [];
55213                 
55214         
55215         // elmodules (is a list of DOM based modules )
55216         Roo.each(this.elmodules, function(e) {
55217             mods.push(e);
55218             if (!this.topModule &&
55219                 typeof(e.parent) == 'string' &&
55220                 e.parent.substring(0,1) == '#' &&
55221                 Roo.get(e.parent.substr(1))
55222                ) {
55223                 
55224                 _this.topModule = e;
55225             }
55226             
55227         });
55228
55229         
55230         // add modules to their parents..
55231         var addMod = function(m) {
55232             Roo.debug && Roo.log("build Order: add: " + m.name);
55233                 
55234             mods.push(m);
55235             if (m.modules && !m.disabled) {
55236                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
55237                 m.modules.keySort('ASC',  cmp );
55238                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
55239     
55240                 m.modules.each(addMod);
55241             } else {
55242                 Roo.debug && Roo.log("build Order: no child modules");
55243             }
55244             // not sure if this is used any more..
55245             if (m.finalize) {
55246                 m.finalize.name = m.name + " (clean up) ";
55247                 mods.push(m.finalize);
55248             }
55249             
55250         }
55251         if (this.topModule && this.topModule.modules) { 
55252             this.topModule.modules.keySort('ASC',  cmp );
55253             this.topModule.modules.each(addMod);
55254         } 
55255         return mods;
55256     },
55257     
55258      /**
55259      * Build the registered modules.
55260      * @param {Object} parent element.
55261      * @param {Function} optional method to call after module has been added.
55262      * 
55263      */ 
55264    
55265     build : function() 
55266     {
55267         
55268         this.preBuild();
55269         var mods = this.buildOrder();
55270       
55271         //this.allmods = mods;
55272         //Roo.debug && Roo.log(mods);
55273         //return;
55274         if (!mods.length) { // should not happen
55275             throw "NO modules!!!";
55276         }
55277         
55278         
55279         var msg = "Building Interface...";
55280         // flash it up as modal - so we store the mask!?
55281         if (!this.hideProgress) {
55282             Roo.MessageBox.show({ title: 'loading' });
55283             Roo.MessageBox.show({
55284                title: "Please wait...",
55285                msg: msg,
55286                width:450,
55287                progress:true,
55288                closable:false,
55289                modal: false
55290               
55291             });
55292         }
55293         var total = mods.length;
55294         
55295         var _this = this;
55296         var progressRun = function() {
55297             if (!mods.length) {
55298                 Roo.debug && Roo.log('hide?');
55299                 if (!this.hideProgress) {
55300                     Roo.MessageBox.hide();
55301                 }
55302                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
55303                 
55304                 // THE END...
55305                 return false;   
55306             }
55307             
55308             var m = mods.shift();
55309             
55310             
55311             Roo.debug && Roo.log(m);
55312             // not sure if this is supported any more.. - modules that are are just function
55313             if (typeof(m) == 'function') { 
55314                 m.call(this);
55315                 return progressRun.defer(10, _this);
55316             } 
55317             
55318             
55319             msg = "Building Interface " + (total  - mods.length) + 
55320                     " of " + total + 
55321                     (m.name ? (' - ' + m.name) : '');
55322                         Roo.debug && Roo.log(msg);
55323             if (!this.hideProgress) { 
55324                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
55325             }
55326             
55327          
55328             // is the module disabled?
55329             var disabled = (typeof(m.disabled) == 'function') ?
55330                 m.disabled.call(m.module.disabled) : m.disabled;    
55331             
55332             
55333             if (disabled) {
55334                 return progressRun(); // we do not update the display!
55335             }
55336             
55337             // now build 
55338             
55339                         
55340                         
55341             m.render();
55342             // it's 10 on top level, and 1 on others??? why...
55343             return progressRun.defer(10, _this);
55344              
55345         }
55346         progressRun.defer(1, _this);
55347      
55348         
55349         
55350     },
55351         
55352         
55353         /**
55354          * Event Object.
55355          *
55356          *
55357          */
55358         event: false, 
55359     /**
55360          * wrapper for event.on - aliased later..  
55361          * Typically use to register a event handler for register:
55362          *
55363          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
55364          *
55365          */
55366     on : false
55367    
55368     
55369     
55370 });
55371
55372 Roo.XComponent.event = new Roo.util.Observable({
55373                 events : { 
55374                         /**
55375                          * @event register
55376                          * Fires when an Component is registered,
55377                          * set the disable property on the Component to stop registration.
55378                          * @param {Roo.XComponent} c the component being registerd.
55379                          * 
55380                          */
55381                         'register' : true,
55382             /**
55383                          * @event beforebuild
55384                          * Fires before each Component is built
55385                          * can be used to apply permissions.
55386                          * @param {Roo.XComponent} c the component being registerd.
55387                          * 
55388                          */
55389                         'beforebuild' : true,
55390                         /**
55391                          * @event buildcomplete
55392                          * Fires on the top level element when all elements have been built
55393                          * @param {Roo.XComponent} the top level component.
55394                          */
55395                         'buildcomplete' : true
55396                         
55397                 }
55398 });
55399
55400 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
55401